रैंडम और ऑर्डरबी का उपयोग करना एक अच्छा फेरबदल एल्गोरिथ्म है?


164

मैंने कोडिंग हॉरर पर विभिन्न फेरबदल एल्गोरिदम के बारे में एक लेख पढ़ा है । मैंने देखा है कि कहीं न कहीं लोगों ने एक सूची में फेरबदल करने के लिए ऐसा किया है:

var r = new Random();
var shuffled = ordered.OrderBy(x => r.Next());

क्या यह एक अच्छा फेरबदल एल्गोरिथ्म है? यह वास्तव में काम कैसे करता है? क्या यह ऐसा करने का एक स्वीकार्य तरीका है?

जवाबों:


205

यह फेरबदल का एक तरीका नहीं है जो मुझे पसंद है, ज्यादातर इस आधार पर कि यह ओ (एन लॉग एन) है बिना किसी अच्छे कारण के जब ओ (एन) फेरबदल को लागू करना आसान है। प्रश्न में कोड "काम करता है" मूल रूप से प्रत्येक तत्व को एक यादृच्छिक (उम्मीद से अनूठा!) नंबर देता है, फिर उस संख्या के अनुसार तत्वों का आदेश देता है।

मैं फिशर-येट्स फेरबदल के डर्स्टनफील्ड के संस्करण को पसंद करता हूं जो तत्वों को स्वैप करता है।

एक साधारण Shuffleविस्तार पद्धति को लागू करना मूल रूप से फिशर-येट्स के मौजूदा कार्यान्वयन का उपयोग करके कॉल ToListया ToArrayइनपुट पर करना होगा । ( Randomजीवन को आम तौर पर अच्छा बनाने के लिए एक पैरामीटर के रूप में पास करें ।) चारों ओर बहुत सारे कार्यान्वयन हैं ... मुझे शायद एक उत्तर में कहीं मिल गया है।

इस तरह के एक विस्तार विधि के बारे में अच्छी बात यह है कि यह पाठक को बहुत स्पष्ट होगा कि आप वास्तव में क्या करने की कोशिश कर रहे हैं।

संपादित करें: यहाँ एक सरल कार्यान्वयन है (कोई त्रुटि जाँच नहीं!):

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length-1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        T tmp = elements[i];
        elements[i] = elements[swapIndex];
        elements[swapIndex] = tmp;
    }
    // Lazily yield (avoiding aliasing issues etc)
    foreach (T element in elements)
    {
        yield return element;
    }
}

संपादित करें: नीचे दिए गए प्रदर्शन पर टिप्पणियों ने मुझे याद दिलाया कि हम वास्तव में तत्वों को वापस कर सकते हैं क्योंकि हम उन्हें फेरबदल करते हैं:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    for (int i = elements.Length - 1; i >= 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        // ... except we don't really need to swap it fully, as we can
        // return it immediately, and afterwards it's irrelevant.
        int swapIndex = rng.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}

यह अब केवल उतना ही काम करेगा जितना इसे करने की आवश्यकता है।

ध्यान दें कि दोनों ही मामलों में, आपको इस बात की सावधानी बरतनी चाहिए कि Randomआप किस तरह का उपयोग करते हैं:

  • Randomलगभग एक ही समय में दो उदाहरण बनाने से यादृच्छिक संख्याओं का समान अनुक्रम प्राप्त होगा (जब उसी तरीके से उपयोग किया जाता है)
  • Random धागा सुरक्षित नहीं है।

मेरे पास एक लेख हैRandom जिस पर इन मुद्दों पर अधिक विस्तार से जाना जाता है और समाधान प्रदान करता है।


5
ठीक है, छोटे, लेकिन महत्वपूर्ण के लिए कार्यान्वयन, इस तरह की चीजें मैं कहूंगा कि स्टैकऑवरफ्लो पर यहां मिलना हमेशा अच्छा होता है। तो हाँ कृपया, अगर आप =) चाहते हैं
Svish

9
जॉन - फिशर-येट्स की आपकी व्याख्या प्रश्न (भोले संस्करण) में दिए गए कार्यान्वयन के बराबर है। डर्स्टनफेल्ड / नूथ ने ओ (एन) को असाइनमेंट द्वारा नहीं, बल्कि एक घटते सेट और स्वैपिंग से चयन करके प्राप्त किया। इस तरह से चयनित रैंडम संख्या दोहराई जा सकती है और एल्गोरिथ्म केवल O (n) लेता है।
tvanfosson

8
आप शायद इस पर मुझसे सुनने के लिए बीमार हो रहे हैं, लेकिन मैं अपने यूनिट परीक्षणों में थोड़ी सी समस्या में भाग गया, जिसे आप जागरूक होना चाहते हैं। ElementAt के साथ एक क्विर्क है जो अविश्वसनीय परिणाम देते हुए, हर बार विस्तार को लागू करता है। अपने परीक्षणों में मैं इससे बचने के लिए जाँच करने से पहले परिणाम को बता रहा हूँ।
tvanfosson

3
@tvanfosson: बिलकुल भी बीमार नहीं है :) लेकिन हाँ, कॉल करने वालों को इस बात का पता होना चाहिए कि इसका आलसी मूल्यांकन किया गया है।
जॉन स्कीट

4
एक थोड़ी देर हो चुकी है, लेकिन कृपया ध्यान दें source.ToArray();की अपेक्षा करती है using System.Linq;एक ही फाइल में। यदि आप नहीं करते हैं, तो आपको यह त्रुटि मिलती है:'System.Collections.Generic.IEnumerable<T>' does not contain a definition for 'ToArray' and no extension method 'ToArray' accepting a first argument of type 'System.Collections.Generic.IEnumerable<T>' could be found (are you missing a using directive or an assembly reference?)
पॉवरलॉर्ड

70

यह जॉन स्कीट के जवाब पर आधारित है ।

उस उत्तर में, सरणी फेरबदल की जाती है, फिर उपयोग करके वापस आ जाती है yield। शुद्ध परिणाम यह है कि सरणी को फॉरेक्स की अवधि के लिए स्मृति में रखा गया है, साथ ही पुनरावृत्ति के लिए आवश्यक वस्तुएं, और फिर भी लागत शुरुआत में ही है - उपज मूल रूप से एक खाली लूप है।

इस एल्गोरिथ्म का उपयोग खेलों में बहुत किया जाता है, जहां पहले तीन आइटम उठाए जाते हैं, और अन्य की आवश्यकता केवल बाद में होगी। yieldजैसे ही उन्हें अदला बदली होगी , मेरा सुझाव संख्याओं पर है। यह ओ (1) पर पुनरावृत्ति लागत (मूल रूप से प्रति संचालन 5 संचालन) रखते हुए, स्टार्ट-अप लागत को कम करेगा। कुल लागत समान रहेगी, लेकिन फेरबदल खुद ही तेज होगा। ऐसे मामलों में जहां इसे कहा जाता है, collection.Shuffle().ToArray()यह सैद्धांतिक रूप से कोई फर्क नहीं पड़ेगा, लेकिन उपरोक्त उपयोग के मामलों में यह स्टार्ट-अप को गति देगा। इसके अलावा, यह उन मामलों के लिए एल्गोरिथ्म को उपयोगी बनाएगा जहां आपको केवल कुछ विशिष्ट वस्तुओं की आवश्यकता होती है। उदाहरण के लिए, यदि आपको 52 के डेक से तीन कार्ड निकालने की आवश्यकता है, तो आप कॉल कर सकते हैं deck.Shuffle().Take(3)और केवल तीन स्वैप होंगे (हालांकि पूरे सरणी को पहले कॉपी करना होगा)।

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length - 1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
        // we don't actually perform the swap, we can forget about the
        // swapped element because we already returned it.
    }

    // there is one item remaining that was not returned - we return it now
    yield return elements[0]; 
}

आउच! यह संभवतः स्रोत के सभी आइटम नहीं लौटाएगा। आप एन पुनरावृत्तियों के लिए एक यादृच्छिक संख्या के अद्वितीय होने पर भरोसा नहीं कर सकते।
पी डैडी

2
चतुर! (और मुझे इस 15 चरित्र सामग्री से नफरत है ...)
Svish

@P डैडी: हुह? विस्तृत करने के लिए परवाह?
Svish

1
या आप> 0 को> = 0 से बदल सकते हैं और नहीं (हालांकि, एक अतिरिक्त
आरएनजी

4
स्रोत की लागत के रूप में स्टार्टअप लागत ओ (एन) है। एयरर ();
डेव हिलियर

8

स्कीट के इस उद्धरण से शुरू:

यह फेरबदल का एक तरीका नहीं है जो मुझे पसंद है, ज्यादातर इस आधार पर कि यह ओ (एन लॉग एन) है बिना किसी अच्छे कारण के जब ओ (एन) फेरबदल को लागू करना आसान है। प्रश्न में कोड "काम करता है" मूल रूप से प्रत्येक तत्व को एक यादृच्छिक ( उम्मीद से अनूठा! ) नंबर देता है, फिर उस संख्या के अनुसार तत्वों का आदेश देता है।

मैं उम्मीद है कि अद्वितीय के लिए कारण समझा थोड़ा पर जाऊँगा !

अब, Enumerable.OrderBy से :

यह विधि एक स्थिर प्रकार करती है; अर्थात्, यदि दो तत्वों की कुंजियाँ समान हैं, तो तत्वों का क्रम संरक्षित है

यह बहुत महत्वपूर्ण है! यदि दो तत्व एक ही यादृच्छिक संख्या "प्राप्त" करते हैं तो क्या होगा? ऐसा होता है कि वे उसी क्रम में बने रहते हैं जब वे सरणी में होते हैं। अब, ऐसा होने की क्या संभावना है? यह वास्तव में गणना करना मुश्किल है, लेकिन जन्मदिन की समस्या है कि वास्तव में यह समस्या है।

अब, क्या यह वास्तविक है? क्या यह सच है?

हमेशा की तरह, जब संदेह हो, तो प्रोग्राम की कुछ पंक्तियाँ लिखें: http://pastebin.com/5CDnUxPG

कोड का यह छोटा सा खंड 3 तत्वों की एक सरणी फेरबदल करता है जो कई बार फिशर-येट्स एल्गोरिथ्म का उपयोग करके पिछड़ा हुआ होता है, फिशर-येट्स एल्गोरिथ्म आगे किया जाता है ( विकी पृष्ठ में दो छद्म कोड एल्गोरिदम हैं ... यह समान उत्पादन करते हैं परिणाम, लेकिन एक पहले से अंतिम तत्व तक किया जाता है, जबकि दूसरा अंतिम से पहले तत्व तक किया जाता है), http://blog.codinghorror.com/the-danger-of-naivete/ के भोले गलत एल्गोरिथ्म और का उपयोग कर .OrderBy(x => r.Next())और .OrderBy(x => r.Next(someValue))

अब, Random.Next है

एक 32-बिट हस्ताक्षरित पूर्णांक जो 0 से अधिक या उससे अधिक है और मैक्सवैल्यू से कम है।

इसलिए यह इसके बराबर है

OrderBy(x => r.Next(int.MaxValue))

यह जाँचने के लिए कि क्या यह समस्या मौजूद है, हम ऐरे को बढ़ा सकते हैं (कुछ बहुत धीमी गति से) या बस यादृच्छिक संख्या जनरेटर के अधिकतम मूल्य को कम कर सकते हैं ( int.MaxValueएक "विशेष" संख्या नहीं है ... यह बस एक बहुत बड़ी संख्या है)। अंत में, यदि एल्गोरिथ्म की गतिहीनता से पक्षपाती नहीं है OrderBy, तो मूल्यों की किसी भी सीमा को समान परिणाम देना चाहिए।

कार्यक्रम फिर कुछ मूल्यों का परीक्षण करता है, सीमा 1 ... 4096 में। परिणाम को देखते हुए, यह काफी स्पष्ट है कि कम मूल्यों (<128) के लिए, एल्गोरिथ्म बहुत पक्षपाती (4-8%) है। 3 मूल्यों के साथ आपको कम से कम की आवश्यकता है r.Next(1024)। यदि आप सरणी को बड़ा बनाते हैं (4 या 5), तो भी r.Next(1024)पर्याप्त नहीं है। मैं फेरबदल और गणित में एक विशेषज्ञ नहीं हूं, लेकिन मुझे लगता है कि सरणी के प्रत्येक अतिरिक्त बिट के लिए, आपको अधिकतम मूल्य के 2 अतिरिक्त बिट्स की आवश्यकता है (क्योंकि जन्मदिन का विरोधाभास sqrt (संख्याओं) से जुड़ा हुआ है), इसलिए यदि अधिकतम मान 2 ^ 31 है, तो मैं कहूंगा कि आप 2 ^ 12/2 ^ 13 बिट्स (4096-8192 तत्व) तक सरणियों को छाँटने में सक्षम होंगे


अच्छी तरह से कहा गया है, और मूल प्रश्न के साथ पूरी तरह से एक समस्या प्रदर्शित करता है। इसे जॉन के जवाब के साथ मिला दिया जाना चाहिए।
TheSoftwareJedi

6

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

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

यह 99.9% अनुप्रयोगों के लिए पूरी तरह से स्वीकार्य है (जब तक कि आपको ऊपर के किनारे के मामले को संभालने की आवश्यकता नहीं है)। इसके अलावा, इसके रनटाइम के लिए स्कीट की आपत्ति मान्य है, इसलिए यदि आप एक लंबी सूची फेरबदल कर रहे हैं तो आप इसका उपयोग नहीं करना चाहेंगे।


4

यह पहले भी कई बार सामने आया है। StackOverflow पर फिशर-येट्स की खोज करें।

इस एल्गोरिथ्म के लिए मैंने एक C # कोड नमूना लिखा है। यदि आप चाहें, तो आप इसे कुछ अन्य प्रकारों पर पैरामीटर कर सकते हैं।

static public class FisherYates
{
        //      Based on Java code from wikipedia:
        //      http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
        static public void Shuffle(int[] deck)
        {
                Random r = new Random();
                for (int n = deck.Length - 1; n > 0; --n)
                {
                        int k = r.Next(n+1);
                        int temp = deck[n];
                        deck[n] = deck[k];
                        deck[k] = temp;
                }
        }
}

2
आपको Randomइस तरह एक स्थिर चर के रूप में उपयोग नहीं करना चाहिए - Randomथ्रेड-सुरक्षित नहीं है। Csharpindepth.com/Articles/Chapter12/Random.aspx
जॉन स्कीट

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

1
इसका मतलब है कि यह ओपी के दृष्टिकोण से "कम गलत" है। इसका मतलब यह नहीं है कि यह कोड है जिसे यह समझने के बिना उपयोग किया जाना चाहिए कि इसे बहु-थ्रेडेड संदर्भ में सुरक्षित रूप से उपयोग नहीं किया जा सकता है ... जो कि आपके द्वारा उल्लेख नहीं किया गया है। एक उचित उम्मीद है कि स्थिर सदस्यों को कई थ्रेड्स से सुरक्षित रूप से उपयोग किया जा सकता है।
जॉन स्कीट

@ जीन स्कीट: ज़रूर, मैं इसे बदल सकता हूं। किया हुआ। मैं सोचता हूं कि साढ़े तीन साल पहले एक सवाल का जवाब दिया गया था और कहा गया था, "यह गलत है क्योंकि यह बहुप्रचलित उपयोग के मामले को हैंडल नहीं करता है" जब ओपी ने एल्गोरिदम से अधिक के बारे में कभी भी कुछ नहीं पूछा। वर्षों में मेरे उत्तरों की समीक्षा करें। अक्सर मैंने ओपी जवाब दिया है जो बताई गई आवश्यकताओं से परे है। मुझे इसके लिए आलोचना की गई है। मुझे उम्मीद है कि ओपी को ऐसे उत्तर नहीं मिलेंगे जो सभी संभव उपयोगों के अनुकूल हों।
हुग्डब्रोर्न

मैंने केवल इस उत्तर का दौरा किया क्योंकि किसी और ने मुझे चैट पर बताया। ओ पी नहीं था विशेष रूप से सूत्रण का उल्लेख है, मुझे लगता है कि यह निश्चित रूप से उल्लेख के लायक है जब एक स्थिर विधि है नहीं है थ्रेड-सुरक्षित, के रूप में यह असामान्य है और कोड संशोधन के बिना कई स्थितियों के लिए अनुपयुक्त बना देता है। आपका नया कोड थ्रेड-सुरक्षित है - लेकिन फिर भी आदर्श नहीं है जैसे कि आप इसे कई थ्रेड्स से "मोटे तौर पर" एक ही समय में एक ही आकार के दो संग्रह में फेरबदल करने के लिए कहते हैं, तो फेरबदल समकक्ष होगा। मूल रूप से, Randomएक दर्द का उपयोग करने के लिए है, जैसा कि मेरे लेख में उल्लेख किया गया है।
जॉन स्कीट

3

एक अच्छा फेरबदल एल्गोरिथ्म की तरह लगता है, अगर आप प्रदर्शन पर बहुत चिंतित नहीं हैं। एकमात्र समस्या यह है कि इसका व्यवहार नियंत्रणीय नहीं है, इसलिए आपको इसका परीक्षण करने में कठिन समय लग सकता है।

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


3

मुझे जॉन स्कीट का उत्तर पूरी तरह से संतोषजनक लगा, लेकिन मेरे क्लाइंट का रब्बो-स्कैनर किसी भी Randomसुरक्षा दोष के रूप में रिपोर्ट करेगा । इसलिए मैंने इसके लिए अदला-बदली की System.Security.Cryptography.RNGCryptoServiceProvider। एक बोनस के रूप में, यह उस थ्रेड-सुरक्षा समस्या को ठीक करता है जिसका उल्लेख किया गया था। दूसरी ओर, RNGCryptoServiceProviderउपयोग करने की तुलना में 300x धीमे के रूप में मापा गया है Random

उपयोग:

using (var rng = new RNGCryptoServiceProvider())
{
    var data = new byte[4];
    yourCollection = yourCollection.Shuffle(rng, data);
}

तरीका:

/// <summary>
/// Shuffles the elements of a sequence randomly.
/// </summary>
/// <param name="source">A sequence of values to shuffle.</param>
/// <param name="rng">An instance of a random number generator.</param>
/// <param name="data">A placeholder to generate random bytes into.</param>
/// <returns>A sequence whose elements are shuffled randomly.</returns>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, RNGCryptoServiceProvider rng, byte[] data)
{
    var elements = source.ToArray();
    for (int i = elements.Length - 1; i >= 0; i--)
    {
        rng.GetBytes(data);
        var swapIndex = BitConverter.ToUInt32(data, 0) % (i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}

3

एक एल्गोरिथ्म के लिए खोज रहे हैं? आप मेरी ShuffleListकक्षा का उपयोग कर सकते हैं :

class ShuffleList<T> : List<T>
{
    public void Shuffle()
    {
        Random random = new Random();
        for (int count = Count; count > 0; count--)
        {
            int i = random.Next(count);
            Add(this[i]);
            RemoveAt(i);
        }
    }
}

फिर, इसे इस तरह उपयोग करें:

ShuffleList<int> list = new ShuffleList<int>();
// Add elements to your list.
list.Shuffle();

यह कैसे काम करता है?

के 5 पहले पूर्णांकों की एक प्रारंभिक क्रमबद्ध सूची लेते हैं: { 0, 1, 2, 3, 4 }

विधि तत्वों के न्युमर को गिनकर शुरू होती है और इसे कॉल करती है count। फिर, साथ countहर कदम पर कम हो रही है, यह एक यादृच्छिक संख्या के बीच ले जाता है 0और countऔर सूची के अंत में यह बढ़ता रहता है।

निम्नलिखित चरण-दर-चरण उदाहरण में, जिन वस्तुओं को स्थानांतरित किया जा सकता है वे इटैलिक हैं , चयनित आइटम बोल्ड है :

0 1 2 3 4
0 1 2 3 4
0 1 2 4 3
0 1 2 4 3
1 2 4 3 0
1 2 4 3 0
1 2 3 4 4
2 2 3 4 4
2 2 4 4 2 2 3 4 4 1 1 1
2 3 4 5 4 1
३ ० ४ १ २


वह O (n) नहीं है। RemoveAt अकेले O (n) है।
paparazzo

हम्म, लगता है जैसे तुम सही हो, मेरे बुरे! मैं उस हिस्से को हटा दूंगा।
स्टेवड्रोज़

1

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


1

थोड़ा असंबंधित, लेकिन यहां एक दिलचस्प तरीका है (कि भले ही यह वास्तव में अत्यधिक है, वास्तव में कार्यान्वित किया गया है) सही मायने में रैंडम जनरेशन रोल के लिए!

पासा-ओ-मैटिक

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


1

मैं कहूंगा कि यहां कई उत्तर जैसे "यह एल्गोरिथ्म एक सूची में प्रत्येक मूल्य के लिए एक नया यादृच्छिक मूल्य उत्पन्न करके फेरबदल करता है, फिर उन यादृच्छिक मूल्यों द्वारा सूची का आदेश देना" बहुत गलत हो सकता है!

मुझे लगता है कि यह स्रोत संग्रह के प्रत्येक तत्व के लिए एक यादृच्छिक मूल्य प्रदान नहीं करता है। इसके बजाय एक तरह का एल्गोरिथ्म हो सकता है जो क्विकसॉर्ट की तरह चल रहा है, जो एक तुलना-फ़ंक्शन को लगभग n लॉग एन बार कॉल करेगा। कुछ प्रकार के अल्ग्रोइथम वास्तव में इस तुलना-कार्य को स्थिर होने की उम्मीद करते हैं और हमेशा एक ही परिणाम देते हैं!

क्या ऐसा नहीं हो सकता है कि IEnumerableSorter प्रत्येक एल्गोरिथम के उदाहरण के लिए फंक्शन की तुलना करता है जैसे कि क्विकसॉर्ट और हर बार x => r.Next()इन मापदंडों के लिए फ़ंक्शन को कैशिंग किए बिना कहता है !

उस स्थिति में आप वास्तव में सॉर्ट एल्गोरिथ्म को गड़बड़ कर सकते हैं और इसे उन एल्गोरिदम के निर्माण की अपेक्षा से बहुत बदतर बना सकते हैं। बेशक, यह अंततः स्थिर हो जाएगा और कुछ वापस करेगा।

मैं इसे एक नए "नेक्स्ट" फंक्शन के अंदर डिबगिंग आउटपुट डालकर बाद में देख सकता हूं कि क्या होता है। रिफ्लेक्टर में मुझे तुरंत पता नहीं चला कि यह कैसे काम करता है।


1
यह मामला नहीं है: आंतरिक ओवरराइड शून्य ComputeKeys (Telement [] तत्व, int count); घोषणा प्रकार: System.Linq.EnumerableSorter <Telement, TKey> असेंबली: System.Core, संस्करण = 3.5.0.0 यह फ़ंक्शन उन सभी कुंजियों के साथ सबसे पहले एक सरणी बनाता है जो मेमोरी का उपभोग करती है, इससे पहले कि वे उसे सॉर्ट करें
क्रिश्चियन

यह जानना अच्छा है - फिर भी सिर्फ एक कार्यान्वयन विवरण हालांकि, जो भविष्य के संस्करणों में परिवर्तन कर सकता है!
Blorgbeard

-5

स्पष्ट सभी थ्रेड्स और हर नए परीक्षण को कैश के साथ कोड पर चलाने के लिए स्टार्टअप समय,

पहले असफल कोड। यह LINQPad पर चलता है। यदि आप इस कोड का परीक्षण करना चाहते हैं।

Stopwatch st = new Stopwatch();
st.Start();
var r = new Random();
List<string[]> list = new List<string[]>();
list.Add(new String[] {"1","X"});
list.Add(new String[] {"2","A"});
list.Add(new String[] {"3","B"});
list.Add(new String[] {"4","C"});
list.Add(new String[] {"5","D"});
list.Add(new String[] {"6","E"});

//list.OrderBy (l => r.Next()).Dump();
list.OrderBy (l => Guid.NewGuid()).Dump();
st.Stop();
Console.WriteLine(st.Elapsed.TotalMilliseconds);

list.OrderBy (x => r.Next ()) 38.6528 एमएस का उपयोग करता है

list.OrderBy (x => Guid.NewGuid ()) 36.7634 ms (यह IDN से अनुशंसित है।) का उपयोग करता है।

दूसरी बार के बाद दोनों एक ही समय में उपयोग करते हैं।

संपादित करें: इंटेल कोर i7 4@2.1GHz, राम 8 जीबी DDR3 @ 1600, HDD SATA 5200 rpm पर [डेटा: www.dropbox.com/s/pbtmh5s9lw2bkp/data] पर टेस्ट कोड।

using System;
using System.Runtime;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Threading;

namespace Algorithm
{
    class Program
    {
        public static void Main(string[] args)
        {
            try {
                int i = 0;
                int limit = 10;
                var result = GetTestRandomSort(limit);
                foreach (var element in result) {
                    Console.WriteLine();
                    Console.WriteLine("time {0}: {1} ms", ++i, element);
                }
            } catch (Exception e) {
                Console.WriteLine(e.Message);
            } finally {
                Console.Write("Press any key to continue . . . ");
                Console.ReadKey(true);
            }
        }

        public static IEnumerable<double> GetTestRandomSort(int limit)
        {
            for (int i = 0; i < 5; i++) {
                string path = null, temp = null;
                Stopwatch st = null;
                StreamReader sr = null;
                int? count = null;
                List<string> list = null;
                Random r = null;

                GC.Collect();
                GC.WaitForPendingFinalizers();
                Thread.Sleep(5000);

                st = Stopwatch.StartNew();
                #region Import Input Data
                path = Environment.CurrentDirectory + "\\data";
                list = new List<string>();
                sr = new StreamReader(path);
                count = 0;
                while (count < limit && (temp = sr.ReadLine()) != null) {
//                  Console.WriteLine(temp);
                    list.Add(temp);
                    count++;
                }
                sr.Close();
                #endregion

//              Console.WriteLine("--------------Random--------------");
//              #region Sort by Random with OrderBy(random.Next())
//              r = new Random();
//              list = list.OrderBy(l => r.Next()).ToList();
//              #endregion

//              #region Sort by Random with OrderBy(Guid)
//              list = list.OrderBy(l => Guid.NewGuid()).ToList();
//              #endregion

//              #region Sort by Random with Parallel and OrderBy(random.Next())
//              r = new Random();
//              list = list.AsParallel().OrderBy(l => r.Next()).ToList();
//              #endregion

//              #region Sort by Random with Parallel OrderBy(Guid)
//              list = list.AsParallel().OrderBy(l => Guid.NewGuid()).ToList();
//              #endregion

//              #region Sort by Random with User-Defined Shuffle Method
//              r = new Random();
//              list = list.Shuffle(r).ToList();
//              #endregion

//              #region Sort by Random with Parallel User-Defined Shuffle Method
//              r = new Random();
//              list = list.AsParallel().Shuffle(r).ToList();
//              #endregion

                // Result
//              
                st.Stop();
                yield return st.Elapsed.TotalMilliseconds;
                foreach (var element in list) {
                Console.WriteLine(element);
            }
            }

        }
    }
}

परिणाम विवरण: https://www.dropbox.com/s/9dw9wl259dfs04g/ResultDescription.PNG
परिणाम स्टेट: https://www.dropbox.com/s/ewq5ybtsvesme4d/ResultStat.PNG

निष्कर्ष:
मान लें: LINQ OrderBy (r.Next ()) और OrderBy (Guide.NewGuid ()) फर्स्ट सॉल्यूशन में यूज़र-डिफ़ाइंड शफल विधि से बदतर नहीं हैं।

उत्तर: वे विरोधाभास हैं।


1
दूसरा विकल्प सही नहीं है , और इसलिए यह प्रदर्शन अप्रासंगिक है । यह अभी भी इस सवाल का जवाब नहीं देता है कि क्या यादृच्छिक संख्या द्वारा ऑर्डर करना स्वीकार्य, कुशल है या यह कैसे काम करता है। पहले समाधान में शुद्धता की समस्या भी है, लेकिन वे एक सौदे के रूप में बड़े नहीं हैं ।
सेवई

क्षमा करें, मैं जानना चाहूंगा कि लिन्क ऑर्डरबी के क्विकॉर्ट के बेहतर प्रकार क्या है? मुझे प्रदर्शन का परीक्षण करने की आवश्यकता है। हालाँकि, मुझे लगता है कि int type में सिर्फ string of Guide से बेहतर स्पीड है लेकिन ऐसा नहीं है। मैं समझ गया कि MSDN ने सिफारिश क्यों की। पहला समाधान संपादित प्रदर्शन रैंडम उदाहरण के साथ ऑर्डरबी के समान है।
GMzo

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

मेरे पास अधिक डेटा पर परीक्षण करने का समय होना चाहिए, यदि यह समाप्त हो गया है, तो मैं फिर से पोस्ट करने का वादा करता हूं। मान लें: मुझे लगता है कि Linq OrderBy पहले समाधान से भी बदतर नहीं है। राय: इसका उपयोग करना और समझना आसान है।
GMzo

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