C # में एक सूची <T> से एन यादृच्छिक तत्वों का चयन करें


158

मुझे जेनेरिक सूची से 5 यादृच्छिक तत्वों का चयन करने के लिए एक त्वरित एल्गोरिथ्म की आवश्यकता है। उदाहरण के लिए, मैं एक से 5 यादृच्छिक तत्वों को प्राप्त करना चाहूंगा List<string>


11
यादृच्छिक द्वारा, क्या आप समावेशी या अनन्य का मतलब है? IOW, क्या एक ही तत्व को एक से अधिक बार चुना जा सकता है? (वास्तव में यादृच्छिक) या एक बार एक तत्व उठाया है, यह अब उपलब्ध पूल से pickable होना चाहिए?
प्रेट्ज़ेल

जवाबों:


127

प्रत्येक तत्व के माध्यम से Iterate चयन की संभावना बनाते हैं = (संख्या आवश्यक) / (संख्या शेष)

इसलिए यदि आपके पास 40 आइटम हैं, तो पहले के लिए चयनित होने का 5/40 मौका होगा। यदि यह है, तो अगले के पास 4/39 मौका है, अन्यथा इसके पास 5/39 मौका है। जब तक आप अंत तक पहुँचते हैं तब तक आपके पास अपने 5 आइटम होंगे, और अक्सर इससे पहले कि आप उन सभी के पास होंगे।


33
मुझे लगता है कि यह सूक्ष्म रूप से गलत है। ऐसा लगता है कि सूची के पीछे के छोर को सामने के छोर से अधिक बार उठाया जाएगा क्योंकि पीछे के अंत में बहुत बड़ी संभावनाएं दिखाई देंगी। उदाहरण के लिए, यदि पहले 35 नंबर नहीं मिलते हैं, तो अंतिम 5 नंबर को चुनना होगा। पहली संख्या में केवल 5/40 मौका दिखाई देगा, लेकिन अंतिम संख्या 5/40 बार 1/1 बार देखेगी। इस एल्गोरिथ्म को लागू करने से पहले आपको पहले सूची को यादृच्छिक बनाना होगा।
अंकुर गोयल

23
ठीक है, मैंने इस एल्गोरिथम को 40 तत्वों की सूची में 10 मिलियन बार, प्रत्येक को 5/40 (.125) शॉट के साथ चुना, और फिर उस सिमुलेशन को कई बार चलाया। यह पता चला है कि यह समान रूप से वितरित नहीं है। तत्व 16 के बाद के 22 अप्रकाशित हो जाते हैं (16 = .123, 17 = .124), जबकि तत्व 34 का निरीक्षण किया जाता है (34 = .129)। तत्व ३ ९ और ४० भी अप्रकाशित हो जाते हैं लेकिन उतना नहीं (३ ९ = १२४ 40, ४० =। १२४६)
अंकुर गोयल

21
@Ankur: मेरा मानना ​​है कि यह सांख्यिकीय रूप से महत्वपूर्ण नहीं है। मेरा मानना ​​है कि एक प्रेरक प्रमाण है कि यह एक समान वितरण प्रदान करेगा।
पुनरावर्ती

9
मैंने एक ही परीक्षण 100 मिलियन बार दोहराया है, और मेरे परीक्षण में सबसे कम चुनी गई वस्तु को 0.106% से कम बार सबसे अधिक बार चुने गए आइटम से कम चुना गया था।
पुनरावर्ती

5
@ संक्षिप्त: प्रमाण लगभग तुच्छ है। हम जानते हैं कि किसी K के लिए K से K आइटम का चयन कैसे करें और किसी N के लिए N से 0 आइटम का चयन कैसे करें। मान लें कि हम N-1> = K में से K या K-1 आइटम को समान रूप से चुनने की विधि जानते हैं; तब हम K से आइटम का चयन कर सकते हैं N से बाहर पहली संभावना का चयन करके और फिर शेष N-1 में से K या K-1 आइटम का चयन करने के लिए ज्ञात विधि का उपयोग करके।
इल्मरी करोनें

216

Linq का उपयोग करना:

YourList.OrderBy(x => rnd.Next()).Take(5)

2
+1 लेकिन अगर दो तत्वों को rnd.Next () या समान से समान संख्या मिलती है तो पहले का चयन किया जाएगा और दूसरा संभवतः नहीं होगा (यदि अधिक तत्वों की आवश्यकता नहीं है)। यह उपयोग के आधार पर ठीक से यादृच्छिक रूप से पर्याप्त है, हालांकि।
लास एस्पेहोल्ट

7
मुझे लगता है कि O (n log (n)) द्वारा ऑर्डर दिया गया है, इसलिए मैं इस समाधान को चुनूंगा यदि कोड सरलता मुख्य चिंता है (यानी छोटी सूचियों के साथ)।
गुइडो

2
लेकिन क्या यह पूरी तरह से नहीं है और पूरी सूची को क्रमबद्ध करता है? जब तक, "त्वरित" से, ओपी का अर्थ "आसान" था, "
परफ़ॉर्मेंट

2
यह तभी काम करेगा जब ऑर्डरबी () केवल प्रत्येक तत्व के लिए एक बार कुंजी चयनकर्ता को बुलाएगा। अगर यह दो तत्वों के बीच तुलना करना चाहता है, तो इसे कॉल करता है, तो इसे हर बार एक अलग मूल्य वापस मिलेगा, जो सॉर्ट को अलग करेगा। [प्रलेखन] ( msdn.microsoft.com/en-us/library/vstudio/… ) यह नहीं कहता कि यह क्या करता है।
ओलिवर बॉक

2
देखो अगर YourListबहुत सारे आइटम हैं, लेकिन आप केवल कुछ का चयन करना चाहते हैं। इस मामले में यह ऐसा करने का एक कारगर तरीका नहीं है।
कैलम वॉटकिंस

39
public static List<T> GetRandomElements<T>(this IEnumerable<T> list, int elementsCount)
{
    return list.OrderBy(arg => Guid.NewGuid()).Take(elementsCount).ToList();
}

27

यह वास्तव में एक कठिन समस्या है जैसा कि यह लगता है, मुख्य रूप से क्योंकि कई गणितीय रूप से सही समाधान वास्तव में आपको सभी संभावनाओं (इस पर नीचे) को हिट करने की अनुमति देने में विफल होंगे।

सबसे पहले, यहां कुछ आसान-से-कार्यान्वयन, सही-अगर-आप-एक-वास्तव में यादृच्छिक-संख्या जनरेटर हैं:

(०) काइल का उत्तर, जो ओ (n) है।

(1) n जोड़े [(0, रैंड), (1, रैंड), (2, रैंड), ...] की एक सूची तैयार करें, उन्हें दूसरे समन्वय द्वारा क्रमित करें, और पहले k (आपके लिए, k) का उपयोग करें = 5) अपने यादृच्छिक सबसेट को पाने के लिए संकेत देता है। मुझे लगता है कि इसे लागू करना आसान है, हालांकि यह ओ (एन लॉग एन) समय है।

(२) एक खाली सूची s = [] जो k यादृच्छिक तत्वों के सूचकांकों के रूप में विकसित होगी। रैंडम, r = रैंड% n पर {0, 1, 2, ..., n-1} में एक नंबर r चुनें और इसे s में जोड़ें। अगला r = रैंड% (n-1) लें और एस में छड़ी करें; टकराव से बचने के लिए s में # उससे कम # तत्वों को जोड़ें। इसके बाद r = रैंड% (n-2) लें, और तब तक वही काम करें, जब तक आपके पास अलग-अलग तत्व न हों। यह सबसे खराब स्थिति में चलने वाला समय O (k ^ 2) है। तो k << n के लिए, यह तेज हो सकता है। यदि आप सॉर्ट किए जाते हैं और ट्रैक करते हैं कि इसमें कौन सा सन्निहित अंतराल है, तो आप इसे O (k log k) में कार्यान्वित कर सकते हैं, लेकिन यह अधिक कार्य है।

@ केली - आप सही कह रहे हैं, दूसरे विचार पर मैं आपके उत्तर से सहमत हूं। मैंने झट से इसे पढ़ लिया, और गलती से आपको लगा कि आप क्रमिक रूप से निश्चित प्रायिकता k / n के साथ प्रत्येक तत्व को चुनने का संकेत दे रहे हैं, जो गलत होगा - लेकिन आपका अनुकूली दृष्टिकोण मेरे लिए सही प्रतीत होता है। उसके लिए माफ़ करना।

ठीक है, और अब किकर के लिए: asymptotically (फिक्स्ड k, n ग्रोइंग के लिए), n ^ k / k हैं! n तत्वों के विकल्प n तत्वों से बाहर निकले [यह (n choose k)] का एक अनुमान है। यदि n बड़ा है, और k बहुत छोटा नहीं है, तो ये संख्या बहुत बड़ी है। किसी भी मानक 32 बिट यादृच्छिक संख्या जनरेटर के लिए आप सबसे अच्छी चक्र लंबाई की उम्मीद कर सकते हैं 2 ^ 32 = 256 ^ 4। इसलिए यदि हमारे पास 1000 तत्वों की एक सूची है, और हम यादृच्छिक पर 5 चुनना चाहते हैं, तो कोई रास्ता नहीं है एक मानक यादृच्छिक संख्या जनरेटर सभी संभावनाओं को हिट करेगा। हालांकि, जब तक आप एक विकल्प के साथ ठीक हैं, जो छोटे सेट के लिए ठीक काम करता है, और हमेशा "यादृच्छिक" दिखता है, तो इन एल्गोरिदम को ठीक होना चाहिए।

परिशिष्ट : यह लिखने के बाद, मुझे एहसास हुआ कि यह विचार (2) को सही ढंग से लागू करने के लिए मुश्किल है, इसलिए मैं इस उत्तर को स्पष्ट करना चाहता था। O (k log k) समय प्राप्त करने के लिए, आपको एक सरणी जैसी संरचना की आवश्यकता होती है जो O (log m) खोज और आवेषण का समर्थन करती है - एक संतुलित बाइनरी ट्री ऐसा कर सकता है। ऐसी संरचना का उपयोग करके एस नामक एक सरणी बनाने के लिए, यहाँ कुछ छद्म विज्ञान है:

# Returns a container s with k distinct random numbers from {0, 1, ..., n-1}
def ChooseRandomSubset(n, k):
  for i in range(k):
    r = UniformRandom(0, n-i)                 # May be 0, must be < n-i
    q = s.FirstIndexSuchThat( s[q] - q > r )  # This is the search.
    s.InsertInOrder(q ? r + q : r + len(s))   # Inserts right before q.
  return s

मैं कुछ नमूना मामलों के माध्यम से यह देखने का सुझाव देता हूं कि यह कुशलता से अंग्रेजी विवरण को कैसे लागू करता है।


2
(1) के लिए आप छँटाई की तुलना में तेजी से एक सूची फेरबदल कर सकते हैं, (2) के लिए आप%
jk

किसी ऐंगल के चक्र की लंबाई के बारे में आपके द्वारा उठाए गए आक्षेप को देखते हुए, क्या कोई ऐसा तरीका है जिससे हम एक एल्गोरिथ्म का निर्माण कर सकते हैं जो सभी सेटों को अपनी पसंद के साथ चुनेंगे?
योना

(1) के लिए, O (n लॉग (n)) को बेहतर बनाने के लिए आप k सबसे छोटे तत्वों को खोजने के लिए चयन प्रकार का उपयोग कर सकते हैं। जो O (n * k) में चलेगा।
जारेड

@ जोनाह: मुझे ऐसा लगता है। मान लेते हैं कि हम एक बड़ा बनाने के लिए कई स्वतंत्र यादृच्छिक संख्या जनरेटर जोड़ सकते हैं ( crypto.stackexchange.com/a/27431 )। फिर आपको प्रश्न में सूची के आकार से निपटने के लिए एक बड़ी पर्याप्त सीमा की आवश्यकता है।
जेरेड

16

मुझे लगता है कि चयनित उत्तर सही और सुंदर है। मैंने इसे अलग तरीके से लागू किया, क्योंकि मैं भी यादृच्छिक क्रम में परिणाम चाहता था।

    static IEnumerable<SomeType> PickSomeInRandomOrder<SomeType>(
        IEnumerable<SomeType> someTypes,
        int maxCount)
    {
        Random random = new Random(DateTime.Now.Millisecond);

        Dictionary<double, SomeType> randomSortTable = new Dictionary<double,SomeType>();

        foreach(SomeType someType in someTypes)
            randomSortTable[random.NextDouble()] = someType;

        return randomSortTable.OrderBy(KVP => KVP.Key).Take(maxCount).Select(KVP => KVP.Value);
    }

बहुत बढ़िया! वास्तव में मेरी मदद की!
आर्मस्ट्रांगेस्ट

1
क्या आपके पास नए रैंडम () का उपयोग न करने का कोई कारण है जो कि पर्यावरण पर आधारित है। विकीाउंट बनाम डेटटाइम। नोव। माइलिसकॉन्ड?
लासे एस्पेहोल्ट

नहीं, बस पता नहीं था कि डिफ़ॉल्ट मौजूद है।
फ्रैंक श्वेतेरमैन

RandomSortTable का एक inprovement: randomSortTable = someTypes.ToDictionary (x => random.NextDouble (), y => y); फॉरेस्ट लूप को बचाता है।
केलटेक्स

2
ठीक है एक साल देर से लेकिन ... क्या यह पैन @ ersin के छोटे जवाब से बाहर नहीं निकलता है, और यदि आप बार-बार रैंडम नंबर प्राप्त करते हैं तो यह विफल नहीं होगा (जहाँ Ersin का दोहराया जोड़ी के पहले आइटम के प्रति पूर्वाग्रह होगा)
एंडीह

12

मैं बस इस समस्या में भाग गया, और कुछ और Google खोज ने मुझे यादृच्छिक रूप से सूची में फेरबदल करने की समस्या में लाया: http://en.wikipedia.org/wiki/Fisher-Yates_shuffle

अपनी सूची को पूरी तरह से बेतरतीब ढंग से फेरबदल करने के लिए (आप इसकी जगह):

N तत्वों की एक सरणी फेरबदल करने के लिए (सूचकांक 0..n-1):

  for i from n  1 downto 1 do
       j  random integer with 0  j  i
       exchange a[j] and a[i]

यदि आपको केवल पहले 5 तत्वों की आवश्यकता है, तो मैं n-1 से 1 तक सभी तरह से चलाने के बजाय, आपको इसे केवल n-5 पर चलाने की आवश्यकता है (यानी: n-5)

आपको k आइटम की आवश्यकता है,

यह बन जाता है:

  for (i = n  1; i >= n-k; i--)
  {
       j = random integer with 0  j  i
       exchange a[j] and a[i]
  }

चयनित प्रत्येक आइटम को सरणी के अंत की ओर स्वैप किया जाता है, इसलिए चुने गए k तत्व सरणी के अंतिम k तत्व हैं।

इसमें O (k) का समय लगता है, जहां k को यादृच्छिक रूप से चयनित तत्वों की संख्या होती है जिनकी आपको आवश्यकता होती है।

इसके अलावा, यदि आप अपनी प्रारंभिक सूची को संशोधित नहीं करना चाहते हैं, तो आप अपने सभी स्वैप को एक अस्थायी सूची में लिख सकते हैं, उस सूची को उल्टा कर सकते हैं, और उन्हें फिर से लागू कर सकते हैं, इस प्रकार स्वैप के व्युत्क्रम सेट का प्रदर्शन कर सकते हैं और आपको बिना बदले अपनी प्रारंभिक सूची वापस कर सकते हैं। O (k) चलने का समय।

अंत में, असली स्टिकलर के लिए, यदि (n == k), आपको 1 पर रोकना चाहिए, एनके नहीं, क्योंकि यादृच्छिक रूप से चुने गए पूर्णांक हमेशा 0 होंगे।


मैंने अपने ब्लॉग पोस्ट में इसे C # का उपयोग करके कार्यान्वित किया: vijayt.com/post/random-select-using-fisher-yates-algorithm । आशा है कि यह किसी को C # रास्ता ढूंढने में मदद करता है।
विजेयस्ट

9

आप इसका इस्तेमाल कर सकते हैं लेकिन ऑर्डर क्लाइंट की तरफ से होगा

 .AsEnumerable().OrderBy(n => Guid.NewGuid()).Take(5);

माना। यह सबसे अच्छा प्रदर्शन या सबसे यादृच्छिक नहीं हो सकता है, लेकिन अधिकांश लोगों के लिए यह काफी अच्छा होगा।
रिचीबन

नीचे की ओर संकेत किया गया क्योंकि मार्गदर्शिकाएँ अद्वितीय होने की गारंटी हैं, यादृच्छिक नहीं
थियोडोर जूलियास

8

एल्गोरिथ्म में ड्रेगन से , C # में एक व्याख्या:

int k = 10; // items to select
var items = new List<int>(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 });
var selected = new List<int>();
double needed = k;
double available = items.Count;
var rand = new Random();
while (selected.Count < k) {
   if( rand.NextDouble() < needed / available ) {
      selected.Add(items[(int)available-1])
      needed--;
   }
   available--;
}

यह एल्गोरिथम आइटम सूची के अद्वितीय संकेतों का चयन करेगा।


केवल सूची में पर्याप्त आइटम प्राप्त करें, लेकिन यादृच्छिक रूप से नहीं।
पुल

2
यह कार्यान्वयन टूट गया है क्योंकि और दोनों पूर्णांकों varमें परिणामों का उपयोग करते हुए , जो हमेशा neededavailableneeded/available
निको

1
यह स्वीकृत उत्तर का कार्यान्वयन प्रतीत होता है।
DCShannon

6

एक समूह से एन यादृच्छिक आइटम का चयन करने के लिए ऑर्डर के साथ कुछ भी नहीं होना चाहिए ! यादृच्छिकता अप्रत्याशितता के बारे में है न कि किसी समूह में फेरबदल की स्थिति के बारे में। सभी जवाब जो थोड़े ऑर्डर के साथ सौदा करते हैं, वह उन लोगों की तुलना में कम कुशल है जो ऐसा नहीं करते हैं। चूंकि दक्षता यहां कुंजी है, इसलिए मैं कुछ ऐसा पोस्ट करूंगा जो वस्तुओं के क्रम को बहुत अधिक नहीं बदलता है।

1) यदि आपको सच्चे यादृच्छिक मूल्यों की आवश्यकता है, जिसका अर्थ है कि किन तत्वों पर कोई प्रतिबंध नहीं है (यानी, एक बार चुने गए आइटम को फिर से चुना जा सकता है):

public static List<T> GetTrueRandom<T>(this IList<T> source, int count, 
                                       bool throwArgumentOutOfRangeException = true)
{
    if (throwArgumentOutOfRangeException && count > source.Count)
        throw new ArgumentOutOfRangeException();

    var randoms = new List<T>(count);
    randoms.AddRandomly(source, count);
    return randoms;
}

यदि आप अपवाद ध्वज को बंद करते हैं, तो आप किसी भी समय यादृच्छिक आइटम चुन सकते हैं।

यदि आपके पास {1, 2, 3, 4} है, तो यह 3 आइटम के लिए {1, 4, 4}, {1, 4, 3} आदि दे सकता है या {1, 4, 3, 2, 4} के लिए भी दे सकता है। 5 आइटम!

यह बहुत तेज़ होना चाहिए, क्योंकि इसमें कुछ भी नहीं है।

2) यदि आपको पुनरावृत्ति वाले समूह से अलग-अलग सदस्यों की आवश्यकता है , तो मैं एक शब्दकोश पर भरोसा करूंगा (जैसा कि कई पहले ही बता चुके हैं)।

public static List<T> GetDistinctRandom<T>(this IList<T> source, int count)
{
    if (count > source.Count)
        throw new ArgumentOutOfRangeException();

    if (count == source.Count)
        return new List<T>(source);

    var sourceDict = source.ToIndexedDictionary();

    if (count > source.Count / 2)
    {
        while (sourceDict.Count > count)
            sourceDict.Remove(source.GetRandomIndex());

        return sourceDict.Select(kvp => kvp.Value).ToList();
    }

    var randomDict = new Dictionary<int, T>(count);
    while (randomDict.Count < count)
    {
        int key = source.GetRandomIndex();
        if (!randomDict.ContainsKey(key))
            randomDict.Add(key, sourceDict[key]);
    }

    return randomDict.Select(kvp => kvp.Value).ToList();
}

कोड अन्य शब्दकोश दृष्टिकोणों की तुलना में थोड़ा लंबा है, क्योंकि मैं न केवल जोड़ रहा हूं, बल्कि सूची से भी हटा रहा हूं, इसलिए इसकी थोड़े दो छोरें। आप यहाँ देख सकते हैं कि मैंने कुछ भी नहीं लिखा है जब countसमान हो जाता है source.Count। ऐसा इसलिए है क्योंकि मेरा मानना ​​है कि पूरे सेट के रूप में बेतरतीब वापसी होनी चाहिए । मेरा मतलब है अगर आप चाहते हैं 5 यादृच्छिक वस्तुओं से 1, 2, 3, 4, 5है, यह बात अगर इसकी नहीं करना चाहिए 1, 3, 4, 2, 5या 1, 2, 3, 4, 5, लेकिन अगर आप की जरूरत है 4 एक ही सेट से आइटम है, तो यह अप्रत्याशित रूप में उपज चाहिए 1, 2, 3, 4, 1, 3, 5, 2, 2, 3, 5, 4आदि दूसरे, जब यादृच्छिक आइटम की गणना होने के लिए लौटा मूल समूह के आधे से अधिक है, तो इसे हटाने के लिए आसान हैsource.Count - countआइटम जोड़ने से समूह से countआइटम। प्रदर्शन कारणों के लिए मैंने हटाने की विधि में यादृच्छिक सूचकांक प्राप्त करने के sourceबजाय उपयोग किया है sourceDict

इसलिए यदि आपके पास {1, 2, 3, 4} है, तो यह 3 आइटम के लिए {1, 2, 3}, {3, 4, 1} आदि में समाप्त हो सकता है।

3) यदि आपको मूल समूह में डुप्लिकेट को ध्यान में रखते हुए अपने समूह से वास्तव में अलग-अलग यादृच्छिक मूल्यों की आवश्यकता है, तो आप ऊपर के समान दृष्टिकोण का उपयोग कर सकते हैं, लेकिन HashSetएक शब्दकोश की तुलना में हल्का होगा।

public static List<T> GetTrueDistinctRandom<T>(this IList<T> source, int count, 
                                               bool throwArgumentOutOfRangeException = true)
{
    if (count > source.Count)
        throw new ArgumentOutOfRangeException();

    var set = new HashSet<T>(source);

    if (throwArgumentOutOfRangeException && count > set.Count)
        throw new ArgumentOutOfRangeException();

    List<T> list = hash.ToList();

    if (count >= set.Count)
        return list;

    if (count > set.Count / 2)
    {
        while (set.Count > count)
            set.Remove(list.GetRandom());

        return set.ToList();
    }

    var randoms = new HashSet<T>();
    randoms.AddRandomly(list, count);
    return randoms.ToList();
}

randomsचर एक बना है HashSetसे बचने के लिए डुप्लिकेट नायाब मामलों में जहां के नायाब में जोड़ा जा रहा है Random.Nextएक ही मूल्य प्राप्त हो सकते हैं, खासकर जब इनपुट सूची छोटा है।

तो {1, 2, 2, 4} => 3 यादृच्छिक आइटम => {1, 2, 4} और कभी नहीं {1, 2, 2}

{, 2, 2, 4} => 4 यादृच्छिक आइटम => अपवाद !! या {1, 2, 4} ध्वज सेट पर निर्भर करता है।

मेरे द्वारा उपयोग की जाने वाली कुछ विस्तार विधियाँ:

static Random rnd = new Random();
public static int GetRandomIndex<T>(this ICollection<T> source)
{
    return rnd.Next(source.Count);
}

public static T GetRandom<T>(this IList<T> source)
{
    return source[source.GetRandomIndex()];
}

static void AddRandomly<T>(this ICollection<T> toCol, IList<T> fromList, int count)
{
    while (toCol.Count < count)
        toCol.Add(fromList.GetRandom());
}

public static Dictionary<int, T> ToIndexedDictionary<T>(this IEnumerable<T> lst)
{
    return lst.ToIndexedDictionary(t => t);
}

public static Dictionary<int, T> ToIndexedDictionary<S, T>(this IEnumerable<S> lst, 
                                                           Func<S, T> valueSelector)
{
    int index = -1;
    return lst.ToDictionary(t => ++index, valueSelector);
}

यदि इसकी सभी वस्तुओं के प्रदर्शन के बारे में 10000 से अधिक बार होने वाली सूची में इसके प्रदर्शन के बारे में है, तो आप शायद इसकी तुलना में तेजी से यादृच्छिक वर्ग चाहते हैं System.Random, लेकिन मुझे नहीं लगता कि बाद में सबसे अधिक विचार करने वाला एक बड़ा सौदा शायद कभी नहीं है अड़चन, इसके काफी तेजी से पर्याप्त ..

संपादित करें: यदि आपको लौटे हुए सामान के ऑर्डर को फिर से व्यवस्थित करने की आवश्यकता है, तो ऐसा कुछ भी नहीं है जो ढकीम के फिशर-येट्स के दृष्टिकोण को हरा सकता है - लघु, मीठा और सरल ।।


6

@JohnShedletsky द्वारा टिप्पणी के बारे में स्वीकृत उत्तर के बारे में सोच रहा था (पैराप्रेज़ ):

आप इसे O (मूल-प्रवाह) के बजाय O (सबसेट.लिफ्ट) में सक्षम कर सकते हैं

मूल रूप से, आपको subsetयादृच्छिक सूचकांक उत्पन्न करने में सक्षम होना चाहिए और फिर उन्हें मूल सूची से हटा देना चाहिए।

प्रक्रिया

public static class EnumerableExtensions {

    public static Random randomizer = new Random(); // you'd ideally be able to replace this with whatever makes you comfortable

    public static IEnumerable<T> GetRandom<T>(this IEnumerable<T> list, int numItems) {
        return (list as T[] ?? list.ToArray()).GetRandom(numItems);

        // because ReSharper whined about duplicate enumeration...
        /*
        items.Add(list.ElementAt(randomizer.Next(list.Count()))) ) numItems--;
        */
    }

    // just because the parentheses were getting confusing
    public static IEnumerable<T> GetRandom<T>(this T[] list, int numItems) {
        var items = new HashSet<T>(); // don't want to add the same item twice; otherwise use a list
        while (numItems > 0 )
            // if we successfully added it, move on
            if( items.Add(list[randomizer.Next(list.Length)]) ) numItems--;

        return items;
    }

    // and because it's really fun; note -- you may get repetition
    public static IEnumerable<T> PluckRandomly<T>(this IEnumerable<T> list) {
        while( true )
            yield return list.ElementAt(randomizer.Next(list.Count()));
    }

}

यदि आप और भी अधिक कुशल बनना चाहते थे, तो आप शायद सूचकांकोंHashSet का उपयोग करेंगे , न कि वास्तविक सूची तत्वों (यदि आपको जटिल प्रकार या महंगी तुलना मिली है);

यूनिट टेस्ट

और यह सुनिश्चित करने के लिए कि हमारे पास कोई टक्कर नहीं है, आदि।

[TestClass]
public class RandomizingTests : UnitTestBase {
    [TestMethod]
    public void GetRandomFromList() {
        this.testGetRandomFromList((list, num) => list.GetRandom(num));
    }

    [TestMethod]
    public void PluckRandomly() {
        this.testGetRandomFromList((list, num) => list.PluckRandomly().Take(num), requireDistinct:false);
    }

    private void testGetRandomFromList(Func<IEnumerable<int>, int, IEnumerable<int>> methodToGetRandomItems, int numToTake = 10, int repetitions = 100000, bool requireDistinct = true) {
        var items = Enumerable.Range(0, 100);
        IEnumerable<int> randomItems = null;

        while( repetitions-- > 0 ) {
            randomItems = methodToGetRandomItems(items, numToTake);
            Assert.AreEqual(numToTake, randomItems.Count(),
                            "Did not get expected number of items {0}; failed at {1} repetition--", numToTake, repetitions);
            if(requireDistinct) Assert.AreEqual(numToTake, randomItems.Distinct().Count(),
                            "Collisions (non-unique values) found, failed at {0} repetition--", repetitions);
            Assert.IsTrue(randomItems.All(o => items.Contains(o)),
                        "Some unknown values found; failed at {0} repetition--", repetitions);
        }
    }
}

2
समस्याओं के साथ अच्छा विचार है। (1) यदि आपकी बड़ी सूची बहुत बड़ी है (उदाहरण के लिए डेटाबेस से पढ़ें), तो आपको पूरी सूची का एहसास होता है, जो कि स्मृति से अधिक हो सकता है। (2) यदि K, N के करीब है, तो आप अपने लूप में एक लावारिस इंडेक्स की खोज में बहुत कुछ करेंगे, जिससे कोड को अप्रत्याशित मात्रा में समय की आवश्यकता होगी। ये समस्याएं हल करने योग्य हैं।
पॉल चेरोच

1
थ्रैशिंग की समस्या का मेरा समाधान यह है: यदि K <N / 2, तो इसे अपने तरीके से करें। यदि K> = N / 2, उन सूचकांकों को चुनें, जिन्हें नहीं रखा जाना चाहिए, बजाय कि उन्हें रखा जाना चाहिए। अभी भी कुछ जोर है, लेकिन बहुत कम है।
पॉल चेरोच

यह भी देखा गया कि यह उन वस्तुओं के क्रम में फेरबदल करता है, जो कुछ स्थितियों में स्वीकार्य हो सकती हैं, लेकिन दूसरों में नहीं।
पॉल चेरोच

औसतन, K = N / 2 (पॉल के सुझाए गए सुधार के लिए सबसे खराब स्थिति) के लिए, (थ्रशिंग इम्प्रूव्ड) एल्गोरिथ्म ~ 0.693 * N पुनरावृत्तियों को लेता प्रतीत होता है। अब गति की तुलना करें। क्या यह स्वीकृत उत्तर से बेहतर है? किस नमूने के लिए आकार?
mbomb007

6

मैंने उपरोक्त उत्तरों में से कई को एक लेज़ी-मूल्यांकनित विस्तार विधि बनाने के लिए संयुक्त किया। मेरे परीक्षण से पता चला है कि काइल का दृष्टिकोण (ऑर्डर (एन)) ड्रेज़स की तुलना में कई बार धीमा है, ताकि सेट (ऑर्डर (के)) चुनने के लिए यादृच्छिक सूचकांकों का प्रस्ताव किया जा सके। पूर्व यादृच्छिक संख्या जनरेटर के लिए कई और कॉल करता है, प्लस आइटम पर अधिक बार पुनरावृत्त करता है।

मेरे कार्यान्वयन के लक्ष्य थे:

1) पूर्ण सूची का एहसास नहीं है अगर एक IEnumerable है कि एक IList नहीं दिया जाता है। यदि मुझे एक ज़िलियन आइटम का अनुक्रम दिया जाता है, तो मैं स्मृति से बाहर नहीं भागना चाहता। ऑन-लाइन समाधान के लिए काइल के दृष्टिकोण का उपयोग करें।

2) अगर मैं बता सकता हूं कि यह एक IList है, एक मोड़ के साथ drzaus के दृष्टिकोण का उपयोग करें। यदि K, N के आधे से अधिक है, तो मैं बार-बार कई यादृच्छिक सूचकांकों का चयन करता हूं और उन्हें छोड़ना पड़ता है। इस प्रकार मैं सूचकांकों की सूची नहीं रखने के लिए रचना करता हूं।

3) मैं गारंटी देता हूं कि आइटम उसी क्रम में वापस आ जाएंगे जो उनका सामना किया गया था। काइल के एल्गोरिथ्म को किसी भी परिवर्तन की आवश्यकता नहीं थी। drzaus के एल्गोरिथ्म के लिए आवश्यक है कि मैं उस आइटम का उत्सर्जन न करूं जिस क्रम में यादृच्छिक सूचक चुने जाते हैं। मैं सभी सूचकांकों को एक SortedSet में इकट्ठा करता हूं, फिर सॉर्ट किए गए इंडेक्स ऑर्डर में आइटम का उत्सर्जन करता हूं।

4) यदि K, N की तुलना में बड़ा है और मैं सेट की समझ को उलटता हूं, तो मैं सभी आइटमों की गणना करता हूं और परीक्षण करता हूं कि क्या इंडेक्स सेट में नहीं है। इसका मतलब है कि मैं ऑर्डर (K) रन टाइम खो देता हूं, लेकिन चूंकि K इन मामलों में N के करीब है, इसलिए मैं ज्यादा नहीं हारता।

यहाँ कोड है:

    /// <summary>
    /// Takes k elements from the next n elements at random, preserving their order.
    /// 
    /// If there are fewer than n elements in items, this may return fewer than k elements.
    /// </summary>
    /// <typeparam name="TElem">Type of element in the items collection.</typeparam>
    /// <param name="items">Items to be randomly selected.</param>
    /// <param name="k">Number of items to pick.</param>
    /// <param name="n">Total number of items to choose from.
    /// If the items collection contains more than this number, the extra members will be skipped.
    /// If the items collection contains fewer than this number, it is possible that fewer than k items will be returned.</param>
    /// <returns>Enumerable over the retained items.
    /// 
    /// See http://stackoverflow.com/questions/48087/select-a-random-n-elements-from-listt-in-c-sharp for the commentary.
    /// </returns>
    public static IEnumerable<TElem> TakeRandom<TElem>(this IEnumerable<TElem> items, int k, int n)
    {
        var r = new FastRandom();
        var itemsList = items as IList<TElem>;

        if (k >= n || (itemsList != null && k >= itemsList.Count))
            foreach (var item in items) yield return item;
        else
        {  
            // If we have a list, we can infer more information and choose a better algorithm.
            // When using an IList, this is about 7 times faster (on one benchmark)!
            if (itemsList != null && k < n/2)
            {
                // Since we have a List, we can use an algorithm suitable for Lists.
                // If there are fewer than n elements, reduce n.
                n = Math.Min(n, itemsList.Count);

                // This algorithm picks K index-values randomly and directly chooses those items to be selected.
                // If k is more than half of n, then we will spend a fair amount of time thrashing, picking
                // indices that we have already picked and having to try again.   
                var invertSet = k >= n/2;  
                var positions = invertSet ? (ISet<int>) new HashSet<int>() : (ISet<int>) new SortedSet<int>();

                var numbersNeeded = invertSet ? n - k : k;
                while (numbersNeeded > 0)
                    if (positions.Add(r.Next(0, n))) numbersNeeded--;

                if (invertSet)
                {
                    // positions contains all the indices of elements to Skip.
                    for (var itemIndex = 0; itemIndex < n; itemIndex++)
                    {
                        if (!positions.Contains(itemIndex))
                            yield return itemsList[itemIndex];
                    }
                }
                else
                {
                    // positions contains all the indices of elements to Take.
                    foreach (var itemIndex in positions)
                        yield return itemsList[itemIndex];              
                }
            }
            else
            {
                // Since we do not have a list, we will use an online algorithm.
                // This permits is to skip the rest as soon as we have enough items.
                var found = 0;
                var scanned = 0;
                foreach (var item in items)
                {
                    var rand = r.Next(0,n-scanned);
                    if (rand < k - found)
                    {
                        yield return item;
                        found++;
                    }
                    scanned++;
                    if (found >= k || scanned >= n)
                        break;
                }
            }
        }  
    } 

मैं एक विशेष यादृच्छिक संख्या जनरेटर का उपयोग करता हूं, लेकिन यदि आप चाहें तो आप सी # के यादृच्छिक का उपयोग कर सकते हैं। ( FastRandom कॉलिन ग्रीन द्वारा लिखा गया था और SharpNEAT का हिस्सा है। इसकी अवधि 2 ^ 128-1 है जो कई RNG से बेहतर है।)

यहाँ इकाई परीक्षण हैं:

[TestClass]
public class TakeRandomTests
{
    /// <summary>
    /// Ensure that when randomly choosing items from an array, all items are chosen with roughly equal probability.
    /// </summary>
    [TestMethod]
    public void TakeRandom_Array_Uniformity()
    {
        const int numTrials = 2000000;
        const int expectedCount = numTrials/20;
        var timesChosen = new int[100];
        var century = new int[100];
        for (var i = 0; i < century.Length; i++)
            century[i] = i;

        for (var trial = 0; trial < numTrials; trial++)
        {
            foreach (var i in century.TakeRandom(5, 100))
                timesChosen[i]++;
        }
        var avg = timesChosen.Average();
        var max = timesChosen.Max();
        var min = timesChosen.Min();
        var allowedDifference = expectedCount/100;
        AssertBetween(avg, expectedCount - 2, expectedCount + 2, "Average");
        //AssertBetween(min, expectedCount - allowedDifference, expectedCount, "Min");
        //AssertBetween(max, expectedCount, expectedCount + allowedDifference, "Max");

        var countInRange = timesChosen.Count(i => i >= expectedCount - allowedDifference && i <= expectedCount + allowedDifference);
        Assert.IsTrue(countInRange >= 90, String.Format("Not enough were in range: {0}", countInRange));
    }

    /// <summary>
    /// Ensure that when randomly choosing items from an IEnumerable that is not an IList, 
    /// all items are chosen with roughly equal probability.
    /// </summary>
    [TestMethod]
    public void TakeRandom_IEnumerable_Uniformity()
    {
        const int numTrials = 2000000;
        const int expectedCount = numTrials / 20;
        var timesChosen = new int[100];

        for (var trial = 0; trial < numTrials; trial++)
        {
            foreach (var i in Range(0,100).TakeRandom(5, 100))
                timesChosen[i]++;
        }
        var avg = timesChosen.Average();
        var max = timesChosen.Max();
        var min = timesChosen.Min();
        var allowedDifference = expectedCount / 100;
        var countInRange =
            timesChosen.Count(i => i >= expectedCount - allowedDifference && i <= expectedCount + allowedDifference);
        Assert.IsTrue(countInRange >= 90, String.Format("Not enough were in range: {0}", countInRange));
    }

    private IEnumerable<int> Range(int low, int count)
    {
        for (var i = low; i < low + count; i++)
            yield return i;
    }

    private static void AssertBetween(int x, int low, int high, String message)
    {
        Assert.IsTrue(x > low, String.Format("Value {0} is less than lower limit of {1}. {2}", x, low, message));
        Assert.IsTrue(x < high, String.Format("Value {0} is more than upper limit of {1}. {2}", x, high, message));
    }

    private static void AssertBetween(double x, double low, double high, String message)
    {
        Assert.IsTrue(x > low, String.Format("Value {0} is less than lower limit of {1}. {2}", x, low, message));
        Assert.IsTrue(x < high, String.Format("Value {0} is more than upper limit of {1}. {2}", x, high, message));
    }
}

क्या परीक्षण में कोई त्रुटि नहीं है? आपके पास if (itemsList != null && k < n/2)जो है, if invertSetवह अंदर है, falseजिसका अर्थ है कि तर्क का उपयोग कभी नहीं किया जाता है।
नेटमैज

4

@ Ers के उत्तर से विस्तार, यदि कोई ऑर्डरबाय के विभिन्न कार्यान्वयनों के बारे में चिंतित है, तो यह सुरक्षित होना चाहिए:

// Instead of this
YourList.OrderBy(x => rnd.Next()).Take(5)

// Temporarily transform 
YourList
    .Select(v => new {v, i = rnd.Next()}) // Associate a random index to each entry
    .OrderBy(x => x.i).Take(5) // Sort by (at this point fixed) random index 
    .Select(x => x.v); // Go back to enumerable of entry

3

यह सबसे अच्छा है जो मैं पहली कट पर आ सकता हूं:

public List<String> getRandomItemsFromList(int returnCount, List<String> list)
{
    List<String> returnList = new List<String>();
    Dictionary<int, int> randoms = new Dictionary<int, int>();

    while (randoms.Count != returnCount)
    {
        //generate new random between one and total list count
        int randomInt = new Random().Next(list.Count);

        // store this in dictionary to ensure uniqueness
        try
        {
            randoms.Add(randomInt, randomInt);
        }
        catch (ArgumentException aex)
        {
            Console.Write(aex.Message);
        } //we can assume this element exists in the dictonary already 

        //check for randoms length and then iterate through the original list 
        //adding items we select via random to the return list
        if (randoms.Count == returnCount)
        {
            foreach (int key in randoms.Keys)
                returnList.Add(list[randoms[key]]);

            break; //break out of _while_ loop
        }
    }

    return returnList;
}

1 - कुल सूची संख्या की श्रेणी के भीतर रैंडम की सूची का उपयोग करना और फिर सूची में उन वस्तुओं को खींचना सबसे अच्छा तरीका लगता है, लेकिन अद्वितीयता सुनिश्चित करने के लिए शब्दकोश का उपयोग करना कुछ ऐसा है जिस पर मैं अभी भी विचार कर रहा हूं।

यह भी ध्यान दें कि मैंने एक स्ट्रिंग सूची का उपयोग किया, आवश्यकतानुसार बदलें।


1
पहले शॉट पर काम किया!
सांग

3

मेरे द्वारा उपयोग किया जाने वाला सरल समाधान (संभवत: बड़ी सूचियों के लिए अच्छा नहीं है): सूची को अस्थायी सूची में कॉपी करें, फिर लूप में बेतरतीब ढंग से अस्थायी सूची से आइटम का चयन करें और इसे अस्थायी सूची बनाते समय चयनित आइटम सूची में डालें (इसलिए यह नहीं हो सकता है reselected)।

उदाहरण:

List<Object> temp = OriginalList.ToList();
List<Object> selectedItems = new List<Object>();
Random rnd = new Random();
Object o;
int i = 0;
while (i < NumberOfSelectedItems)
{
            o = temp[rnd.Next(temp.Count)];
            selectedItems.Add(o);
            temp.Remove(o);
            i++;
 }

एक सूची के बीच से हटाना इतनी बार महंगा हो जाएगा। आप एक एल्गोरिथ्म के लिए लिंक की गई सूची का उपयोग करने पर विचार कर सकते हैं, जिसमें कई निष्कासन की आवश्यकता होती है। या समान रूप से, हटाए गए आइटम को एक अशक्त मान से प्रतिस्थापित करें, लेकिन फिर आप पहले से हटाए गए आइटम उठाते हैं और फिर से चुनना होगा।
पॉल चेर्नोच

3

यहाँ आपके पास फिशर-येट्स शफल के आधार पर एक कार्यान्वयन है, जिसका एल्गोरिथ्म जटिलता ओ (n) है जहां n, सूची आकार के बजाय, उप-आकार या नमूना आकार है, जैसा कि जॉन शेडलेटस्की ने बताया है।

public static IEnumerable<T> GetRandomSample<T>(this IList<T> list, int sampleSize)
{
    if (list == null) throw new ArgumentNullException("list");
    if (sampleSize > list.Count) throw new ArgumentException("sampleSize may not be greater than list count", "sampleSize");
    var indices = new Dictionary<int, int>(); int index;
    var rnd = new Random();

    for (int i = 0; i < sampleSize; i++)
    {
        int j = rnd.Next(i, list.Count);
        if (!indices.TryGetValue(j, out index)) index = j;

        yield return list[index];

        if (!indices.TryGetValue(i, out index)) index = i;
        indices[j] = index;
    }
}

2

काइल के जवाब के आधार पर, यहाँ मेरा c # कार्यान्वयन है।

/// <summary>
/// Picks random selection of available game ID's
/// </summary>
private static List<int> GetRandomGameIDs(int count)
{       
    var gameIDs = (int[])HttpContext.Current.Application["NonDeletedArcadeGameIDs"];
    var totalGameIDs = gameIDs.Count();
    if (count > totalGameIDs) count = totalGameIDs;

    var rnd = new Random();
    var leftToPick = count;
    var itemsLeft = totalGameIDs;
    var arrPickIndex = 0;
    var returnIDs = new List<int>();
    while (leftToPick > 0)
    {
        if (rnd.Next(0, itemsLeft) < leftToPick)
        {
            returnIDs .Add(gameIDs[arrPickIndex]);
            leftToPick--;
        }
        arrPickIndex++;
        itemsLeft--;
    }

    return returnIDs ;
}

2

यह विधि काइल के बराबर हो सकती है।

मान लें कि आपकी सूची आकार n की है और आप k तत्व चाहते हैं।

Random rand = new Random();
for(int i = 0; k>0; ++i) 
{
    int r = rand.Next(0, n-i);
    if(r<k) 
    {
        //include element i
        k--;
    }
} 

एक जादू की तरह काम करता है :)

-एलेक्स गिल्बर्ट


1
जो मेरे बराबर दिखता है। इसी तरह की तुलना stackoverflow.com/a/48141/2449863
DCShannon

1

ऐसा क्यों नहीं है:

 Dim ar As New ArrayList
    Dim numToGet As Integer = 5
    'hard code just to test
    ar.Add("12")
    ar.Add("11")
    ar.Add("10")
    ar.Add("15")
    ar.Add("16")
    ar.Add("17")

    Dim randomListOfProductIds As New ArrayList

    Dim toAdd As String = ""
    For i = 0 To numToGet - 1
        toAdd = ar(CInt((ar.Count - 1) * Rnd()))

        randomListOfProductIds.Add(toAdd)
        'remove from id list
        ar.Remove(toAdd)

    Next
'sorry i'm lazy and have to write vb at work :( and didn't feel like converting to c#

1

यह बहुत कठिन है जितना कोई सोचता है। देखें महान लेख "फेरबदल"जेफ से ।

मैंने उस विषय पर एक बहुत छोटा लेख लिखा था, जिसमें C # कोड:
किसी दिए गए एरे के एन तत्वों के रैंडम सबसेट लौटें


1

लक्ष्य: दोहराव के बिना संग्रह स्रोत से आइटम की संख्या का चयन करें। मैंने किसी भी सामान्य संग्रह के लिए एक एक्सटेंशन बनाया। यहाँ है कि मैं यह कैसे किया:

public static class CollectionExtension
{
    public static IList<TSource> RandomizeCollection<TSource>(this IList<TSource> source, int maxItems)
    {
        int randomCount = source.Count > maxItems ? maxItems : source.Count;
        int?[] randomizedIndices = new int?[randomCount];
        Random random = new Random();

        for (int i = 0; i < randomizedIndices.Length; i++)
        {
            int randomResult = -1;
            while (randomizedIndices.Contains((randomResult = random.Next(0, source.Count))))
            {
                //0 -> since all list starts from index 0; source.Count -> maximum number of items that can be randomize
                //continue looping while the generated random number is already in the list of randomizedIndices
            }

            randomizedIndices[i] = randomResult;
        }

        IList<TSource> result = new List<TSource>();
        foreach (int index in randomizedIndices)
            result.Add(source.ElementAt(index));

        return result;
    }
}

0

मैंने हाल ही में टायलर के पॉइंट 1 के समान एक विचार का उपयोग करके अपनी परियोजना पर ऐसा किया ।
मैं सवालों का एक गुच्छा लोड कर रहा था और यादृच्छिक पर पांच का चयन कर रहा था। एक IComparer का उपयोग करके छंटनी हासिल की गई थी ।
सभी प्रश्न प्रश्नावली सूची में लोड किए गए थे, जिसे तब सूची के क्रमबद्ध फ़ंक्शन और उन पहले k तत्वों का उपयोग करके सॉर्ट किया गया था, जहां चयनित थे।

    private class QuestionSorter : IComparable<QuestionSorter>
    {
        public double SortingKey
        {
            get;
            set;
        }

        public Question QuestionObject
        {
            get;
            set;
        }

        public QuestionSorter(Question q)
        {
            this.SortingKey = RandomNumberGenerator.RandomDouble;
            this.QuestionObject = q;
        }

        public int CompareTo(QuestionSorter other)
        {
            if (this.SortingKey < other.SortingKey)
            {
                return -1;
            }
            else if (this.SortingKey > other.SortingKey)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
    }

उपयोग:

    List<QuestionSorter> unsortedQuestions = new List<QuestionSorter>();

    // add the questions here

    unsortedQuestions.Sort(unsortedQuestions as IComparer<QuestionSorter>);

    // select the first k elements

0

यहाँ मेरा दृष्टिकोण है (पूरा पाठ यहाँ http://krkadev.blogspot.com/2010/08/random-numbers-without-repetition.html ) है।

इसे O (N) के बजाय O (K) में चलना चाहिए, जहाँ K वांछित तत्वों की संख्या है और N से चुनने के लिए सूची का आकार है:

public <T> List<T> take(List<T> source, int k) {
 int n = source.size();
 if (k > n) {
   throw new IllegalStateException(
     "Can not take " + k +
     " elements from a list with " + n +
     " elements");
 }
 List<T> result = new ArrayList<T>(k);
 Map<Integer,Integer> used = new HashMap<Integer,Integer>();
 int metric = 0;
 for (int i = 0; i < k; i++) {
   int off = random.nextInt(n - i);
   while (true) {
     metric++;
     Integer redirect = used.put(off, n - i - 1);
     if (redirect == null) {
       break;
     }
     off = redirect;
   }
   result.add(source.get(off));
 }
 assert metric <= 2*k;
 return result;
}

0

यह स्वीकार किए जाते हैं समाधान के रूप में के रूप में सुंदर या कुशल नहीं है, लेकिन यह लिखने के लिए जल्दी है। सबसे पहले, सरणी को यादृच्छिक रूप से अनुमति दें, फिर पहले K तत्वों का चयन करें। अजगर में,

import numpy

N = 20
K = 5

idx = np.arange(N)
numpy.random.shuffle(idx)

print idx[:K]

0

मैं एक एक्सटेंशन विधि का उपयोग करेगा।

    public static IEnumerable<T> TakeRandom<T>(this IEnumerable<T> elements, int countToTake)
    {
        var random = new Random();

        var internalList = elements.ToList();

        var selected = new List<T>();
        for (var i = 0; i < countToTake; ++i)
        {
            var next = random.Next(0, internalList.Count - selected.Count);
            selected.Add(internalList[next]);
            internalList[next] = internalList[internalList.Count - selected.Count];
        }
        return selected;
    }

0
public static IEnumerable<T> GetRandom<T>(this IList<T> list, int count, Random random)
    {
        // Probably you should throw exception if count > list.Count
        count = Math.Min(list.Count, count);

        var selectedIndices = new SortedSet<int>();

        // Random upper bound
        int randomMax = list.Count - 1;

        while (selectedIndices.Count < count)
        {
            int randomIndex = random.Next(0, randomMax);

            // skip over already selected indeces
            foreach (var selectedIndex in selectedIndices)
                if (selectedIndex <= randomIndex)
                    ++randomIndex;
                else
                    break;

            yield return list[randomIndex];

            selectedIndices.Add(randomIndex);
            --randomMax;
        }
    }

मेमोरी: ~ गिनती
जटिलता: O (गिनती 2 )


0

जब N बहुत बड़ा होता है, तो सामान्य विधि जो अनियमित रूप से N संख्याओं का फेरबदल करती है और कहती है, कहते हैं, पहली k संख्या, अंतरिक्ष जटिलता के कारण निषेधात्मक हो सकती है। निम्नलिखित एल्गोरिथ्म में समय और अंतरिक्ष जटिलताओं दोनों के लिए केवल ओ (के) की आवश्यकता होती है।

http://arxiv.org/abs/1512.00501

def random_selection_indices(num_samples, N):
    modified_entries = {}
    seq = []
    for n in xrange(num_samples):
        i = N - n - 1
        j = random.randrange(i)

        # swap a[j] and a[i] 
        a_j = modified_entries[j] if j in modified_entries else j 
        a_i = modified_entries[i] if i in modified_entries else i

        if a_i != j:
            modified_entries[j] = a_i   
        elif j in modified_entries:   # no need to store the modified value if it is the same as index
            modified_entries.pop(j)

        if a_j != i:
            modified_entries[i] = a_j 
        elif i in modified_entries:   # no need to store the modified value if it is the same as index
            modified_entries.pop(i)
        seq.append(a_j)
    return seq

0

LINQ का उपयोग बड़ी सूचियों के साथ (जब प्रत्येक तत्व को छूना महंगा हो) और यदि आप डुप्लिकेट की संभावना के साथ रह सकते हैं:

new int[5].Select(o => (int)(rnd.NextDouble() * maxIndex)).Select(i => YourIEnum.ElementAt(i))

मेरे उपयोग के लिए मेरे पास 100.000 तत्वों की एक सूची थी, और क्योंकि उन्हें पूरी सूची में एक रैंड की तुलना में आधे समय (या बेहतर) के बारे में एक डीबी I से खींचा गया था।

बड़ी सूची होने से डुप्लिकेट के लिए बाधाओं में बहुत कमी आएगी।


इस घोल में बार-बार तत्व हो सकते हैं !! छेद सूची में यादृच्छिक नहीं हो सकता है।
एक्सलवॉस

हम्म। सच। हालांकि मैं इसका उपयोग करता हूं, हालांकि इससे कोई फर्क नहीं पड़ता। इसका उत्तर प्रतिबिंबित करने के लिए संपादित किया।
वोल्फ 5

-1

इससे आपकी समस्या दूर हो जाएगी

var entries=new List<T>();
var selectedItems = new List<T>();


                for (var i = 0; i !=10; i++)
                {
                    var rdm = new Random().Next(entries.Count);
                        while (selectedItems.Contains(entries[rdm]))
                            rdm = new Random().Next(entries.Count);

                    selectedItems.Add(entries[rdm]);
                }

हालांकि यह प्रश्न का उत्तर दे सकता है, आपको इस कोड को इस प्रश्न का उत्तर देने के तरीके के स्पष्टीकरण को शामिल करने के लिए अपने उत्तर को संपादित करना चाहिए । यह संदर्भ प्रदान करने में मदद करता है और भविष्य के पाठकों के लिए आपके उत्तर को अधिक उपयोगी बनाता है।
होप्टुप्पुनेट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.