.NET के साथ एक सरणी यादृच्छिक करने के लिए सबसे अच्छा तरीका है


141

.NET के साथ स्ट्रिंग्स की एक सरणी को यादृच्छिक करने का सबसे अच्छा तरीका क्या है? मेरी सरणी में लगभग 500 स्ट्रिंग्स हैं और मैं एक Arrayही स्ट्रिंग्स के साथ एक यादृच्छिक क्रम में एक नया बनाना चाहता हूं ।

कृपया अपने उत्तर में C # उदाहरण शामिल करें।


1
इसके लिए एक अजीब लेकिन सरल उपाय है - stackoverflow.com/a/4262134/1298685
इयान कैंपबेल

1
का उपयोग करते हुए MedallionRandom NuGet पैकेज, यह सिर्फ myArray.Shuffled().ToArray()(या myArray.Shuffle()आप वर्तमान सरणी उत्परिवर्तित करना चाहते हैं)
ChaseMedallion

जवाबों:


171

यदि आप .NET 3.5 पर हैं, तो आप निम्न IEnumerable शीतलता का उपयोग कर सकते हैं (VB.NET, C # नहीं, लेकिन विचार स्पष्ट होना चाहिए ...):

Random rnd=new Random();
string[] MyRandomArray = MyArray.OrderBy(x => rnd.Next()).ToArray();    

संपादित करें: ठीक है और यहाँ VB.NET कोड संगत है:

Dim rnd As New System.Random
Dim MyRandomArray = MyArray.OrderBy(Function() rnd.Next()).ToArray()

दूसरा संपादन, उस टिप्पणी के जवाब में कि System.Random "थ्रेडसेफ़ नहीं है" और "केवल खिलौना-ऐप के लिए उपयुक्त" एक समय-आधारित अनुक्रम वापस करने के कारण: जैसा कि मेरे उदाहरण में उपयोग किया गया है, रैंडम () पूरी तरह से थ्रेड-सुरक्षित है, जब तक कि आप उस रूटीन की अनुमति दे रहे हैं जिसमें आप सरणी को फिर से दर्ज करने के लिए यादृच्छिक करते हैं, जिस स्थिति में आपको lock (MyRandomArray)अपने डेटा को दूषित न करने के लिए किसी भी तरह से कुछ की आवश्यकता होगी, जो रक्षा करेगाrnd

इसके अलावा, यह अच्छी तरह से समझा जाना चाहिए कि Entropy के स्रोत के रूप में System.Random बहुत मजबूत नहीं है। जैसा कि MSDN दस्तावेज़ीकरण में उल्लेख किया गया है , आपको System.Security.Cryptography.RandomNumberGeneratorकुछ सुरक्षा से संबंधित कार्य करने से प्राप्त होने वाली चीज़ का उपयोग करना चाहिए । उदाहरण के लिए:

using System.Security.Cryptography;

...

RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider();
string[] MyRandomArray = MyArray.OrderBy(x => GetNextInt32(rnd)).ToArray();

...

static int GetNextInt32(RNGCryptoServiceProvider rnd)
    {
        byte[] randomInt = new byte[4];
        rnd.GetBytes(randomInt);
        return Convert.ToInt32(randomInt[0]);
    }

दो नोट: 1) सिस्टम। आयामी थ्रेड-सुरक्षित नहीं है (आपको चेतावनी दी गई है) और 2) सिस्टम। आयामी समय आधारित है, इसलिए यदि आप एक भारी समवर्ती प्रणाली में इस कोड का उपयोग करते हैं, तो दो अनुरोध प्राप्त करना संभव है एक ही मूल्य (यानी
वेबैप्स में

2
बस ऊपर स्पष्ट करने के लिए, System.Random वर्तमान समय का उपयोग करके खुद को बीज देगा, इसलिए एक साथ बनाए गए दो उदाहरण एक ही "यादृच्छिक" अनुक्रम उत्पन्न करेंगे। System.Random केवल खिलौना क्षुधा में इस्तेमाल किया जाना चाहिए
therealhoff

8
इसके अलावा यह एल्गोरिथ्म O (n log n) है और Qsort एल्गोरिथम द्वारा पक्षपाती है। O (n) निष्पक्ष समाधान के लिए मेरा उत्तर देखें।
मैट हॉवेल्स

9
जब तक OrderByआंतरिक रूप से सॉर्ट कीज़ को कैश नहीं किया जाता है, तब तक ऑर्डर की गई तुलनाओं की ट्रांज़िटिव संपत्ति का उल्लंघन करने की समस्या भी होती है। यदि कभी कोई डिबग मोड सत्यापन होता है OrderByजो सही परिणाम उत्पन्न करता है, तो सिद्धांत रूप में यह एक अपवाद फेंक सकता है।
सैम हरवेल


205

निम्नलिखित कार्यान्वयन फिशर-येट्स एल्गोरिथ्म AKA द नूथ शफल का उपयोग करता है । यह ओ (एन) समय में चलता है और जगह में फेरबदल करता है, इसलिए यह 'रैंडम बाय सॉर्ट' तकनीक की तुलना में बेहतर प्रदर्शन करता है, हालांकि यह कोड की अधिक लाइनें हैं। कुछ तुलनात्मक प्रदर्शन माप के लिए यहां देखें । मैंने System.Random का उपयोग किया है, जो गैर-क्रिप्टोग्राफिक उद्देश्यों के लिए ठीक है। *

static class RandomExtensions
{
    public static void Shuffle<T> (this Random rng, T[] array)
    {
        int n = array.Length;
        while (n > 1) 
        {
            int k = rng.Next(n--);
            T temp = array[n];
            array[n] = array[k];
            array[k] = temp;
        }
    }
}

उपयोग:

var array = new int[] {1, 2, 3, 4};
var rng = new Random();
rng.Shuffle(array);
rng.Shuffle(array); // different order from first call to Shuffle

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


1
क्या मापदंडों को बदलना और उपयोग की तरह बनाना बेहतर नहीं होगा array.Shuffle(new Random());..?
केन किन

आप Tuples का उपयोग करके स्वैप को सरल बना सकते हैं 4.0 -> (सरणी [n], सरणी [k]) = (array [k], array [n]);
डायनेमिकल

@ केन परिजन: नहीं, यह बुरा होगा। कारण यह है कि new Random()वर्तमान प्रणाली के समय के आधार पर एक बीज मूल्य के साथ आरंभीकृत किया जाता है, जो केवल हर ~ 16ms को अपडेट करता है।
मैट हॉवेल्स

इस सूची को हटाने के समाधान के कुछ त्वरित परीक्षणों में, 999 तत्वों में एक छोटा सा अंतर है। अंतर 99999 यादृच्छिक ints में कठोर हो जाता है, इस समाधान के साथ 3ms और दूसरे पर 1810ms है।

18

आप एक फेरबदल एल्गोरिथ्म के लिए देख रहे हैं, है ना?

ठीक है, ऐसा करने के दो तरीके हैं: चतुर-लेकिन-लोग-हमेशा-से-गलतफहमी-यह-यह-और-यह गलत-गलत-तो-शायद-नहीं-इसके-चालाक-बाद-सभी रास्ता, और गूंगा-के रूप में चट्टानों-लेकिन-कौन परवाह करता है, क्योंकि यह काम करता है।

गूंगा रास्ता

  • अपने पहले सरणी का एक डुप्लिकेट बनाएं, लेकिन प्रत्येक स्ट्रिंग को एक यादृच्छिक संख्या के साथ टैग करना चाहिए।
  • यादृच्छिक संख्या के संबंध में डुप्लिकेट सरणी को क्रमबद्ध करें।

यह एल्गोरिथ्म अच्छी तरह से काम करता है, लेकिन सुनिश्चित करें कि आपका यादृच्छिक संख्या जनरेटर एक ही नंबर के साथ दो तारों को टैग करने की संभावना नहीं है। तथाकथित बर्थडे पैराडॉक्स के कारण , यह आपकी अपेक्षा से अधिक बार होता है। इसकी समय जटिलता O ( n log n ) है।

चतुर तरीका

मैं इसे एक पुनरावर्ती एल्गोरिथ्म के रूप में वर्णित करूंगा:

आकार n की एक फेरबदल करने के लिए (सीमा में सूचकांक [0) n -1] ):

यदि n = 0
  • कुछ मत करो
यदि एन > 0
  • (पुनरावर्ती कदम) पहले एन फेरबदल सरणी -1 तत्वों को
  • एक यादृच्छिक सूचकांक, x , सीमा [0 .. n में चुनें -1]
  • अनुक्रमणिका x पर तत्व के साथ अनुक्रमणिका n -1 पर तत्व को स्वैप करें

पुनरावृत्ति बराबर सरणी के माध्यम से एक इटरेटर चलने के लिए, यादृच्छिक तत्वों के साथ अदला-बदली के रूप में आप साथ जाने है, लेकिन सूचना है कि आप कर सकते हैं एक तत्व के साथ अदला-बदली नहीं के बाद कि करने के लिए इटरेटर अंक एक। यह एक बहुत ही सामान्य गलती है, और एक पक्षपाती फेरबदल की ओर जाता है।

समय जटिलता O ( n ) है।


8

यह एल्गोरिथ्म सरल है लेकिन कुशल नहीं है, ओ (एन 2 )। सभी "आदेश द्वारा" एल्गोरिदम आमतौर पर ओ (एन लॉग एन) हैं। यह संभवतः सैकड़ों हजारों तत्वों के नीचे एक अंतर नहीं करता है, लेकिन यह बड़ी सूची के लिए होगा।

var stringlist = ... // add your values to stringlist

var r = new Random();

var res = new List<string>(stringlist.Count);

while (stringlist.Count >0)
{
   var i = r.Next(stringlist.Count);
   res.Add(stringlist[i]);
   stringlist.RemoveAt(i);
}

इसका कारण O (N 2 ) सूक्ष्म है: List.RemoveAt () एक O (N) ऑपरेशन है जब तक कि आप अंत से क्रम में नहीं हटाते हैं।


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

1
मैं इस सुंदर और आसानी से समझ में आता है और 500 तार पर यह एक फर्क नहीं पड़ता ...
Sklivvz

4

आप मैट हॉवेल से बाहर निकालने की विधि भी बना सकते हैं। उदाहरण।

   namespace System
    {
        public static class MSSystemExtenstions
        {
            private static Random rng = new Random();
            public static void Shuffle<T>(this T[] array)
            {
                rng = new Random();
                int n = array.Length;
                while (n > 1)
                {
                    int k = rng.Next(n);
                    n--;
                    T temp = array[n];
                    array[n] = array[k];
                    array[k] = temp;
                }
            }
        }
    }

तो आप बस इसे उपयोग कर सकते हैं जैसे:

        string[] names = new string[] {
                "Aaron Moline1", 
                "Aaron Moline2", 
                "Aaron Moline3", 
                "Aaron Moline4", 
                "Aaron Moline5", 
                "Aaron Moline6", 
                "Aaron Moline7", 
                "Aaron Moline8", 
                "Aaron Moline9", 
            };
        names.Shuffle<string>();

आप विधि के लिए हर कॉल को फिर से क्यों बना रहे हैं ... आप इसे कक्षा स्तर पर घोषित करते हैं, लेकिन इसे स्थानीय के रूप में उपयोग करते हैं ...
यार्न

1

सरणी को यादृच्छिक करना गहन है क्योंकि आपको स्ट्रिंग्स के एक झुंड के चारों ओर शिफ्ट करना होगा। क्यों नहीं बस बेतरतीब ढंग से सरणी से पढ़ा? सबसे खराब स्थिति में आप एक getNextString () के साथ एक रैपर क्लास भी बना सकते हैं। यदि आपको वास्तव में एक यादृच्छिक सरणी बनाने की आवश्यकता है तो आप कुछ ऐसा कर सकते हैं

for i = 0 -> i= array.length * 5
   swap two strings in random places

* 5 मनमाना है।


सरणी से एक यादृच्छिक पढ़ने के लिए कई बार कुछ आइटम हिट करने और दूसरों को याद करने की संभावना है!
रे हेस

फेरबदल एल्गोरिथ्म टूट गया है। आपके फेरबदल के निष्पक्ष होने से पहले आपको अपने मनमाने 5 को वास्तव में उच्च बनाना होगा।
पितरौ सिप

(पूर्णांक) अनुक्रमित का एक सरणी बनाएँ। तर्जनी का फेरबदल करें। बस उस यादृच्छिक क्रम में अनुक्रमित का उपयोग करें। कोई डुप्लिकेट, स्मृति में स्ट्रिंग संदर्भों के आसपास कोई फेरबदल नहीं (जो प्रत्येक ट्रिगर इंटर्निंग और क्या नहीं हो सकता है)।
क्रिस्टोफर

1

बस मेरे सिर के ऊपर से सोच रहा है, आप ऐसा कर सकते हैं:

public string[] Randomize(string[] input)
{
  List<string> inputList = input.ToList();
  string[] output = new string[input.Length];
  Random randomizer = new Random();
  int i = 0;

  while (inputList.Count > 0)
  {
    int index = r.Next(inputList.Count);
    output[i++] = inputList[index];
    inputList.RemoveAt(index);
  }

  return (output);
}

0

एक ही लंबाई के यादृच्छिक फ़्लोट्स या इन्ट्स का एक सरणी उत्पन्न करें। उस सरणी को क्रमबद्ध करें, और अपने लक्ष्य सरणी पर संबंधित स्वैप करें।

यह वास्तव में एक स्वतंत्र पैदावार है।


0
Random r = new Random();
List<string> list = new List(originalArray);
List<string> randomStrings = new List();

while(list.Count > 0)
{
int i = r.Random(list.Count);
randomStrings.Add(list[i]);
list.RemoveAt(i);
}

0

जैको, आपका समाधान एक कस्टम IComparer ising सुरक्षित नहीं है। क्रमबद्ध दिनचर्या को ठीक से काम करने के लिए कई आवश्यकताओं के अनुरूप तुलना करने की आवश्यकता होती है। उनमें से पहली संगति है। यदि तुलनाकर्ता को एक ही जोड़ी की वस्तुओं पर बुलाया जाता है, तो उसे हमेशा एक ही परिणाम देना चाहिए। (तुलना भी सकर्मक होनी चाहिए)।

इन आवश्यकताओं को पूरा करने में विफलता एक अनंत लूप की संभावना सहित छँटाई दिनचर्या में किसी भी समस्या का कारण बन सकती है।

उन समाधानों के बारे में जो प्रत्येक प्रविष्टि के साथ एक यादृच्छिक संख्यात्मक मान को जोड़ते हैं और फिर उस मूल्य के अनुसार क्रमबद्ध होते हैं, ये आउटपुट में निहित पूर्वाग्रह की ओर ले जाते हैं क्योंकि किसी भी समय दो प्रविष्टियों को समान संख्यात्मक मान सौंपा जाता है, आउटपुट की यादृच्छिकता से समझौता किया जाएगा। (एक "स्थिर" सॉर्ट रूटीन में, जो भी इनपुट में पहले है आउटपुट में पहले होगा। Array.Sort स्थिर होने के लिए नहीं होता है, लेकिन क्विकसॉर्ट एल्गोरिथम द्वारा किए गए विभाजन के आधार पर अभी भी एक पूर्वाग्रह है)।

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

गीत-सूची फेरबदल के लिए, वरीयता प्राप्त PRNG (जैसे System.Random) का उपयोग करने में कोई समस्या नहीं है। एक पोकर साइट के लिए, यह एक विकल्प भी नहीं है और आपको स्टैकफ़्लो पर किसी के लिए बहुत कठिन समस्या के बारे में सोचने की ज़रूरत है। (एक क्रिप्टोग्राफिक आरएनजी का उपयोग केवल शुरुआत है, आपको यह सुनिश्चित करने की आवश्यकता है कि आपका एल्गोरिथ्म एक पूर्वाग्रह का परिचय नहीं देता है, कि आपके पास एन्ट्रॉपी के पर्याप्त स्रोत हैं, और यह कि आप किसी भी आंतरिक स्थिति को उजागर नहीं करते हैं जो बाद की यादृच्छिकता से समझौता करेगा)।


0

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

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


1
आपके लिंक अभी भी टूटे हुए हैं: /
वाई हा ली

0

ठीक है, यह स्पष्ट रूप से मेरी ओर से एक टक्कर है (माफी माँगता है ...), लेकिन मैं अक्सर एक काफी सामान्य और क्रिप्टोग्राफिक रूप से मजबूत विधि का उपयोग करता हूं।

public static class EnumerableExtensions
{
    static readonly RNGCryptoServiceProvider RngCryptoServiceProvider = new RNGCryptoServiceProvider();
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> enumerable)
    {
        var randomIntegerBuffer = new byte[4];
        Func<int> rand = () =>
                             {
                                 RngCryptoServiceProvider.GetBytes(randomIntegerBuffer);
                                 return BitConverter.ToInt32(randomIntegerBuffer, 0);
                             };
        return from item in enumerable
               let rec = new {item, rnd = rand()}
               orderby rec.rnd
               select rec.item;
    }
}

Shuffle () किसी भी IEnumerable पर एक एक्सटेंशन है, इसलिए कहते हैं, एक सूची में यादृच्छिक क्रम में 0 से 1000 तक की संख्या के साथ किया जा सकता है

Enumerable.Range(0,1000).Shuffle().ToList()

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


0

आपको जटिल एल्गोरिदम की आवश्यकता नहीं है।

बस एक सरल रेखा:

Random random = new Random();
array.ToList().Sort((x, y) => random.Next(-1, 1)).ToArray();

ध्यान दें कि हम बदलने की आवश्यकता Arrayएक करने के लिए List, पहले अगर आप का उपयोग नहीं करतेList पहली जगह में।

इसके अलावा, यह ध्यान रखें कि यह बहुत बड़ी सरणियों के लिए कुशल नहीं है! अन्यथा यह साफ और सरल है।


त्रुटि: ऑपरेटर '।' '' शून्य '' के प्रकार पर लागू नहीं किया जा सकता
उपयोगी

0

यह एक पूर्ण कार्य कंसोल समाधान है जो यहां दिए गए उदाहरण पर आधारित है :

class Program
{
    static string[] words1 = new string[] { "brown", "jumped", "the", "fox", "quick" };

    static void Main()
    {
        var result = Shuffle(words1);
        foreach (var i in result)
        {
            Console.Write(i + " ");
        }
        Console.ReadKey();
    }

   static string[] Shuffle(string[] wordArray) {
        Random random = new Random();
        for (int i = wordArray.Length - 1; i > 0; i--)
        {
            int swapIndex = random.Next(i + 1);
            string temp = wordArray[i];
            wordArray[i] = wordArray[swapIndex];
            wordArray[swapIndex] = temp;
        }
        return wordArray;
    }         
}

0
        int[] numbers = {0,1,2,3,4,5,6,7,8,9};
        List<int> numList = new List<int>();
        numList.AddRange(numbers);

        Console.WriteLine("Original Order");
        for (int i = 0; i < numList.Count; i++)
        {
            Console.Write(String.Format("{0} ",numList[i]));
        }

        Random random = new Random();
        Console.WriteLine("\n\nRandom Order");
        for (int i = 0; i < numList.Capacity; i++)
        {
            int randomIndex = random.Next(numList.Count);
            Console.Write(String.Format("{0} ", numList[randomIndex]));
            numList.RemoveAt(randomIndex);
        }
        Console.ReadLine();

-1

यहां OLINQ का उपयोग करने का एक सरल तरीका है:

// Input array
List<String> lst = new List<string>();
for (int i = 0; i < 500; i += 1) lst.Add(i.ToString());

// Output array
List<String> lstRandom = new List<string>();

// Randomize
Random rnd = new Random();
lstRandom.AddRange(from s in lst orderby rnd.Next(100) select s);

-2
private ArrayList ShuffleArrayList(ArrayList source)
{
    ArrayList sortedList = new ArrayList();
    Random generator = new Random();

    while (source.Count > 0)
    {
        int position = generator.Next(source.Count);
        sortedList.Add(source[position]);
        source.RemoveAt(position);
    }  
    return sortedList;
}

मेरे लिए ऐसा लगता है कि आप एक दूसरे ऐरे की घोषणा करके एक ऐरे को फेरबदल करने के बजाय दक्षता और तत्परता दोनों बढ़ा सकते हैं, आप बेहतर कर सकते हैं कि एक सूची में फेरबदल करें, और एक ऐरे में वापस लौटें:sortedList = source.ToList().OrderBy(x => generator.Next()).ToArray();
T_0
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.