क्या फेरबदल के लिए जावास्क्रिप्ट Array.sort () विधि का उपयोग करना सही है?


126

मैं किसी को उसके जावास्क्रिप्ट कोड के साथ मदद कर रहा था और मेरी आँखें एक अनुभाग द्वारा पकड़ी गई थीं जो इस तरह दिखती थीं:

function randOrd(){
  return (Math.round(Math.random())-0.5);
}
coords.sort(randOrd);
alert(coords);

हालांकि मेरा पहला था: हे, यह संभवतः काम नहीं कर सकता है! लेकिन फिर मैंने कुछ प्रयोग किए और पाया कि यह वास्तव में कम से कम यादृच्छिक रूप से परिणाम प्रदान करता है।

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

लेकिन मेरी आंत भावना मुझे बताती है, कि यह गलत होना चाहिए। विशेष रूप से छँटाई एल्गोरिथ्म ईसीएमए मानक द्वारा निर्दिष्ट नहीं है। मुझे लगता है कि अलग-अलग छंटनी वाले अल्गोरिथम के परिणामस्वरूप अलग-अलग गैर-समान फेरबदल होंगे। कुछ छँटाई एल्गोरिदम शायद भी असीम पाश कर सकते हैं ...

लेकिन आप क्या सोचते हैं?

और एक अन्य प्रश्न के रूप में ... अब मैं कैसे जाऊंगा और मापूंगा कि इस फेरबदल तकनीक के परिणाम कितने यादृच्छिक हैं?

अद्यतन: मैंने कुछ माप किए और उत्तर में से एक के रूप में नीचे दिए गए परिणामों को पोस्ट किया।


सिर्फ यह ध्यान देने के लिए कि परिणाम केवल साइन काउंट को राउंड करना व्यर्थ है
बोरमट

2
" मैंने पाया कि यह अच्छी तरह से यादृच्छिक परिणाम प्रदान करता है। " - सच में ???
बरगी

जवाबों:


109

यह उथल की मेरी पसंदीदा तरीके से किया गया कभी नहीं किया है, आंशिक रूप से, क्योंकि यह है कार्यान्वयन-विशिष्ट के रूप में आप कहते हैं। विशेष रूप से, मुझे याद है कि जावा या .NET (जो निश्चित नहीं है) से छँटनी वाला मानक पुस्तकालय अक्सर पता लगा सकता है कि क्या आप कुछ तत्वों के बीच असंगत तुलना के साथ समाप्त होते हैं (जैसे आप पहले दावा करते हैं A < Bऔर B < C, लेकिन फिर C < A)।

यह वास्तव में ज़रूरत से ज़्यादा जटिल (निष्पादन समय के मामले में) फेरबदल के रूप में भी समाप्त होता है।

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

यह O (n) है और केवल यादृच्छिक संख्या जनरेटर के लिए n-1 कॉल की आवश्यकता है, जो अच्छा है। यह एक वास्तविक फेरबदल भी पैदा करता है - किसी भी तत्व की मूल स्थिति की परवाह किए बिना (उचित RNG मानकर) प्रत्येक अंतरिक्ष में समाप्त होने का 1 / n मौका होता है। सॉर्ट किए गए संस्करण एक समान वितरण के लिए अनुमानित है (यह मानते हुए कि यादृच्छिक संख्या जनरेटर दो बार एक ही मूल्य नहीं लेता है, जो बहुत ही कम संभावना है अगर यह यादृच्छिक युगल लौटा रहा है), लेकिन मुझे फेरबदल संस्करण के बारे में तर्क करना आसान लगता है :)

इस दृष्टिकोण को फिशर-येट्स फेरबदल कहा जाता है ।

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

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


5
रेमंड चेन इस महत्व पर गहराई से जाता है कि तुलनात्मक प्रकार के नियम नियमों का पालन करते हैं: blogs.msdn.com/oldnewthing/archive/2009/05/08/9595334.aspx
जेसन क्रसोवेती

1
अगर मेरे तर्क सही है, तो क्रमबद्ध संस्करण नहीं है एक 'वास्तविक' फेरबदल का उत्पादन!
क्रिस्टोफ

@ क्रिश्चोफ: इसके बारे में सोचते हुए, यहां तक ​​कि फिशर-येट्स केवल "सही" वितरण देगा यदि रैंड (एक्स) को इसकी सीमा पर भी ठीक होने की गारंटी है । यह देखते हुए कि आम तौर पर कुछ एक्स के लिए RNG के लिए 2 ^ x संभव राज्य हैं, मुझे नहीं लगता कि यह बिल्कुल रैंड (3) के लिए भी होगा ।
जॉन स्कीट

@Jon: लेकिन फिशर-येट्स 2^xप्रत्येक ऐरे इंडेक्स के लिए स्टेट्स बनाएंगे , यानी कुल मिलाकर 2 ^ (xn) स्टेट्स होंगे, जो कि थोड़ा बड़ा होना चाहिए, 2 ^ c - विवरणों के लिए मेरा संपादित उत्तर देखें
Christoph

@Christoph: मैंने खुद को ठीक से नहीं समझाया होगा। मान लीजिए कि आपके पास सिर्फ 3 तत्व हैं। आप पहले तत्व को बेतरतीब ढंग से उठाते हैं, 3. पूरी तरह से समान वितरण पाने के लिए , आपको पूरी तरह से समान रूप से [0,3] रेंज में एक यादृच्छिक संख्या चुनने में सक्षम होना होगा - और अगर PRNG में 2 ^ n है संभावित स्थिति, आप ऐसा नहीं कर सकते हैं - एक या दो संभावनाओं के होने की थोड़ी अधिक संभावना होगी ।
जॉन स्कीट

118

जॉन ने पहले ही सिद्धांत को कवर कर लिया है , यहाँ एक कार्यान्वयन है:

function shuffle(array) {
    var tmp, current, top = array.length;

    if(top) while(--top) {
        current = Math.floor(Math.random() * (top + 1));
        tmp = array[current];
        array[current] = array[top];
        array[top] = tmp;
    }

    return array;
}

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


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

मेरा तर्क इन पंक्तियों के साथ जाता है: एक छँटाई एल्गोरिथ्म के लिए निश्चित संख्या cमें तुलना की आवश्यकता होती है , उदाहरण के c = n(n-1)/2लिए बुलबुले। हमारा यादृच्छिक तुलनात्मक फ़ंक्शन प्रत्येक तुलना के परिणाम को समान रूप से संभव बनाता है, अर्थात 2^c समान रूप से संभावित परिणाम होते हैं। अब, प्रत्येक परिणाम को n!सरणी की प्रविष्टियों के क्रमपरिवर्तन में से एक के अनुरूप होना है , जो सामान्य मामले में एक समान वितरण को असंभव बनाता है। (यह एक सरलीकरण है, क्योंकि तुलना की गई वास्तविक संख्या इनपुट सरणी पर निर्भर करती है, लेकिन मुखरता होनी चाहिए।)

जैसा कि जॉन ने बताया, यह अकेले फिशर-येट्स को पसंद करने का कोई कारण नहीं है sort(), क्योंकि यादृच्छिक संख्या जनरेटर भी n!क्रमपरिवर्तन के लिए छद्म यादृच्छिक मूल्यों की एक सीमित संख्या को मैप करेगा । लेकिन फिशर-येट्स के परिणाम अभी भी बेहतर होने चाहिए:

Math.random()रेंज में एक छद्म यादृच्छिक संख्या पैदा करता है [0;1[। जेएस डबल-सटीक फ़्लोटिंग पॉइंट मूल्यों का उपयोग करता है, यह 2^xसंभव मूल्यों से मेल खाता है जहां 52 ≤ x ≤ 63(मैं वास्तविक संख्या खोजने के लिए बहुत आलसी हूं)। Math.random()अगर परमाणु घटनाओं की संख्या परिमाण के समान क्रम की है, तो उपयोग करने से उत्पन्न संभावना वितरण अच्छी तरह से व्यवहार करना बंद कर देगा।

फिशर-येट्स का उपयोग करते समय, प्रासंगिक पैरामीटर सरणी का आकार है, जिसे 2^52व्यावहारिक सीमाओं के कारण कभी भी दृष्टिकोण नहीं करना चाहिए ।

जब एक यादृच्छिक तुलना फ़ंक्शन के साथ छँटाई होती है, तो फ़ंक्शन मूल रूप से केवल परवाह करता है यदि रिटर्न वैल्यू सकारात्मक या नकारात्मक है, तो यह कभी भी समस्या नहीं होगी। लेकिन एक समान है: क्योंकि तुलनात्मक कार्य अच्छी तरह से व्यवहार किया जाता है, 2^cसंभावित परिणाम समान रूप से संभावित हैं। यदि c ~ n log nतब 2^c ~ n^(a·n)कहाँ a = const, जो इसे कम से कम संभव बनाता 2^cहै जो समान परिमाण का है (या उससे भी कम) n!और इस प्रकार असमान वितरण के लिए अग्रणी है, भले ही छँटाई एल्गोरिथ्म जहां समान रूप से क्रमपरिवर्तन पर मैप करना है। यदि इसका कोई व्यावहारिक प्रभाव मेरे से परे है।

वास्तविक समस्या यह है कि छंटाई एल्गोरिदम समान रूप से क्रमपरिवर्तन पर मैप करने की गारंटी नहीं है। यह देखना आसान है कि मेरजसोर्ट सममित रूप में करता है, लेकिन बुलबुले के बारे में कुछ तर्क करना या, अधिक महत्वपूर्ण बात, क्विकॉर्ट या हीप्सॉर्ट, ऐसा नहीं है।


लब्बोलुआब यह है कि जब तक sort()आप मर्जेसॉर्ट का उपयोग करते हैं, तब तक आपको कोने के मामलों को छोड़कर काफी सुरक्षित होना चाहिए (कम से कम मुझे उम्मीद है कि 2^c ≤ n!एक कोने का मामला है), यदि नहीं, तो सभी दांव बंद हैं।


कार्यान्वयन के लिए धन्यवाद। यह बहुत तेजी से है! विशेष रूप से उस धीमी बकवास की तुलना में जो मैंने इस बीच खुद से लिखी थी।
रेने सरसो

1
यदि आप underscore.js लाइब्रेरी का उपयोग कर रहे हैं, तो यहां उपरोक्त फ़िशर-येट्स फेरबदल विधि के साथ इसका विस्तार करने का तरीका बताया गया है: github.com/ryantenney/underscore/commit/…
स्टीव

इसके लिए आपका बहुत-बहुत धन्यवाद, आपके और जॉन्स जवाब के संयोजन ने मुझे एक समस्या को ठीक करने में मदद की, जिस पर मुझे और एक सहकर्मी ने लगभग 4 घंटे संयुक्त रूप से बिताए! हम मूल रूप से ओपी के लिए एक समान विधि थे, लेकिन पाया कि यादृच्छिककरण बहुत परतदार था, इसलिए हमने कुछ प्राप्त करने के लिए छवियों की एक सूची को जंबरी करने के लिए थोड़ी सी jquery के साथ काम करने के लिए आपका तरीका बदल दिया और इसे थोड़ा बदल दिया। भयानक यादृच्छिकरण।
नमस्ते वर्ल्ड

16

मैंने कुछ माप किया कि इस यादृच्छिक प्रकार के परिणाम कितने यादृच्छिक हैं ...

मेरी तकनीक एक छोटी सी सरणी [1,2,3,4] लेने की थी और इसके सभी (4! = 24) क्रमचय बनाने की थी। फिर मैं फेरबदल फ़ंक्शन को सरणी में बड़ी संख्या में लागू करूंगा और गिनूंगा कि प्रत्येक क्रमचय कितनी बार उत्पन्न होता है। एक अच्छा फेरबदल algoritm सभी क्रमपरिवर्तन पर काफी समान रूप से परिणाम वितरित करेगा, जबकि एक बुरा एक समान परिणाम नहीं बनाएगा।

नीचे दिए गए कोड का उपयोग करके मैंने फ़ायरफ़ॉक्स, ओपेरा, क्रोम, IE6 / 7/8 में परीक्षण किया।

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

संपादित करें: इस परीक्षण को वास्तव में यादृच्छिकता या उसके अभाव को सही ढंग से नहीं मापा गया। मेरे द्वारा पोस्ट किया गया अन्य उत्तर देखें।

लेकिन प्रदर्शन पक्ष में क्रिस्टोफ द्वारा दिया गया फेरबदल एक स्पष्ट विजेता था। यहां तक ​​कि छोटे चार-तत्व सरणियों के लिए वास्तविक फेरबदल के बारे में दो बार के रूप में तेजी से यादृच्छिक-प्रकार का प्रदर्शन किया!

// क्रिस्टोफ द्वारा पोस्ट किए गए फेरबदल समारोह।
var फेरबदल = फ़ंक्शन (सरणी) {
    var tmp, current, top = array.length;

    अगर (शीर्ष) जबकि (- शीर्ष) {
        current = Math.floor (Math.random () * (top + 1));
        tmp = array [करंट];
        सरणी [वर्तमान] = सरणी [शीर्ष];
        सरणी [शीर्ष] = tmp;
    }

    वापसी सरणी;
};

// रैंडम सॉर्ट फ़ंक्शन
var rnd = function () {
  वापसी Math.round (Math.random ()) - 0.5;
};
var randSort = function (A) {
  वापसी A.sort (rnd);
};

var क्रमपरिवर्तन = फ़ंक्शन (A) {
  यदि (A.length == 1) {
    वापसी [ए];
  }
  अन्य {
    var perms = [];
    के लिए (var i = 0; मैं <ढलान; i ++) {
      var x = A.slice (i, i + 1);
      var xs = A.slice (0, i) .concat (A.slice (i + 1));
      var उपप्रकार = क्रमपरिवर्तन (xs);
      के लिए (var j = 0; j <subperms.length; j ++) {
        perms.push (x.concat (subperms [जे]));
      }
    }
    वापसी की अनुमति;
  }
};

var परीक्षण = फ़ंक्शन (ए, पुनरावृत्तियों, फ़ंक) {
  // init क्रमपरिवर्तन
  var आँकड़े = {};
  var perms = क्रमपरिवर्तन (ए);
  के लिए (var मैं परमिट में) {
    आँकड़े ["" + परमिट [i] = 0;
  }

  // कई बार फेरबदल करें और आंकड़े इकट्ठा करें
  var start = new Date ();
  के लिए (var i = 0; मैं <पुनरावृत्तियों? i ++) {
    var फेरबदल = फंक (ए);
    आँकड़े [ "" + फेरबदल] ++;
  }
  var अंत = नई तिथि ();

  // प्रारूप परिणाम
  var arrest = [];
  के लिए (आँकड़े में var मैं) {
    arr.push (i + "" + आँकड़े [i]);
  }
  वापसी arr.join ("\ n") + "\ n \ n समय लिया:" + ((अंत - प्रारंभ) / 1000) + "सेकंड";
};

चेतावनी ("यादृच्छिक प्रकार:" + परीक्षण ([1,2,3,4], 100000, रैंडोर्ट));
चेतावनी ("फेरबदल:" + परीक्षण ([1,2,3,4], 100000, फेरबदल));

11

दिलचस्प बात यह है कि Microsoft ने अपने पिक-रैंडम-ब्राउज़र-पेज में एक ही तकनीक का उपयोग किया था

उन्होंने कुछ भिन्न तुलनात्मक कार्य का उपयोग किया:

function RandomSort(a,b) {
    return (0.5 - Math.random());
}

मुझे लगभग वही दिखता है, लेकिन यह इतना यादृच्छिक नहीं है ...

इसलिए मैंने लिंक किए गए लेख में उसी पद्धति के साथ फिर से कुछ परीक्षण किए, और वास्तव में - यह निकला कि यादृच्छिक-छंटाई-विधि ने त्रुटिपूर्ण परिणाम उत्पन्न किए। यहां नया परीक्षण कोड:

function shuffle(arr) {
  arr.sort(function(a,b) {
    return (0.5 - Math.random());
  });
}

function shuffle2(arr) {
  arr.sort(function(a,b) {
    return (Math.round(Math.random())-0.5);
  });
}

function shuffle3(array) {
  var tmp, current, top = array.length;

  if(top) while(--top) {
    current = Math.floor(Math.random() * (top + 1));
    tmp = array[current];
    array[current] = array[top];
    array[top] = tmp;
  }

  return array;
}

var counts = [
  [0,0,0,0,0],
  [0,0,0,0,0],
  [0,0,0,0,0],
  [0,0,0,0,0],
  [0,0,0,0,0]
];

var arr;
for (var i=0; i<100000; i++) {
  arr = [0,1,2,3,4];
  shuffle3(arr);
  arr.forEach(function(x, i){ counts[x][i]++;});
}

alert(counts.map(function(a){return a.join(", ");}).join("\n"));

मैं यह नहीं देखता कि इसे 0.5 क्यों होना है - Math.random (), सिर्फ Math.random () क्यों नहीं?
अलेक्जेंडर मिल्स

1
@AlexanderMills: तुलनित्र समारोह के लिए पारित sort()से बड़ी संख्या वापस जाने के लिए माना जाता है, कम से कम, या की तुलना के आधार पर शून्य के बराबर aऔर b। ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
LarsH

@ लार्स हाँ जो समझ में आता है
अलेक्जेंडर मिल्स

9

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

आप देख सकते हैं कि कुछ ब्राउज़रों पर 50% संभावना है कि कुछ तत्व 'फेरबदल' के दौरान जगह नहीं बदलेंगे!

नोट: आप फिशर-येट्स के फेरबदल को @Christoph द्वारा सफारी के लिए कोड को बदलकर थोड़ा तेज कर सकते हैं:

function shuffle(array) {
  for (var tmp, cur, top=array.length; top--;){
    cur = (Math.random() * (top + 1)) << 0;
    tmp = array[cur]; array[cur] = array[top]; array[top] = tmp;
  }
  return array;
}

परीक्षा परिणाम: http://jsperf.com/optimized-fisher-yates


5

मुझे लगता है कि यह उन मामलों के लिए ठीक है, जहाँ आप वितरण के बारे में नहीं चुन रहे हैं और आप चाहते हैं कि स्रोत कोड छोटा हो।

जावास्क्रिप्ट में (जहां स्रोत लगातार प्रसारित होता है), छोटे बैंडविड्थ की लागत में अंतर करता है।


2
बात यह है कि आप वितरण के बारे में हमेशा से ही चुन रहे हैं जितना आप सोचते हैं, और "छोटे कोड" के लिए, हमेशा होता है arr = arr.map(function(n){return [Math.random(),n]}).sort().map(function(n){return n[1]});, जिसका फायदा यह है कि यह बहुत लंबा नहीं है और वास्तव में ठीक से वितरित नहीं है। बहुत संकुचित Knuth / FY फेरबदल वेरिएंट भी हैं।
डैनियल मार्टिन

@DanielMartin कि एक-लाइनर एक जवाब होना चाहिए। इसके अलावा, पार्सिंग त्रुटियों से बचने के लिए, दो अर्धविरामों को जोड़ने की आवश्यकता है ताकि यह इस तरह दिखाई दे arr = arr.map(function(n){return [Math.random(),n];}).sort().map(function(n){return n[1];});:।
Giacomo1968

2

यह हैक है, निश्चित रूप से। व्यवहार में, एक अनन्त रूप से लूपिंग एल्गोरिथ्म की संभावना नहीं है। यदि आप वस्तुओं को छांट रहे हैं, तो आप कोर्ड सरणी के माध्यम से लूप कर सकते हैं और कुछ ऐसा कर सकते हैं:

for (var i = 0; i < coords.length; i++)
    coords[i].sortValue = Math.random();

coords.sort(useSortValue)

function useSortValue(a, b)
{
  return a.sortValue - b.sortValue;
}

(और फिर उनके माध्यम से लूप को फिर से सॉर्टवेल्यू हटाने के लिए)

हालांकि अभी भी एक हैक। यदि आप इसे अच्छी तरह से करना चाहते हैं, तो आपको इसे कठिन तरीके से करना होगा :)


2

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

सबूत:

  1. nतत्वों की एक सरणी के लिए , बिल्कुल n!क्रमपरिवर्तन (यानी संभव फेरबदल) हैं।
  2. फेरबदल के दौरान हर तुलना क्रमपरिवर्तन के दो सेटों के बीच एक विकल्प है। एक यादृच्छिक तुलनित्र के लिए, प्रत्येक सेट को चुनने का 1/2 मौका है।
  3. इस प्रकार, प्रत्येक क्रमपरिवर्तन p के लिए, क्रमपरिवर्तन p के साथ समाप्त होने की संभावना भाजक 2 ^ k (कुछ k के लिए) के साथ एक अंश है, क्योंकि यह इस तरह के भिन्न का योग है (जैसे 1/8 + 1/16 = 3/16 )।
  4. N = 3 के लिए, छह समान रूप से संभावित क्रमपरिवर्तन हैं। प्रत्येक क्रमपरिवर्तन का मौका, तब 1/6 है। 1/6 को इसके हर के रूप में 2 की शक्ति के साथ एक अंश के रूप में व्यक्त नहीं किया जा सकता है।
  5. इसलिए, सिक्का फ्लिप प्रकार में फेरबदल का उचित वितरण कभी नहीं होगा।

एकमात्र आकार जो संभवतः सही ढंग से वितरित किए जा सकते हैं वे n = 0,1,2 हैं।


एक अभ्यास के रूप में, n = 3 के लिए अलग-अलग प्रकार के एल्गोरिदम के निर्णय पेड़ को खींचने का प्रयास करें।


प्रमाण में एक अंतर है: यदि एक सॉर्ट एल्गोरिथ्म तुलनित्र की स्थिरता पर निर्भर करता है, और एक असंगत तुलनित्र के साथ अनबाउंड रनटाइम है, तो इसमें अनंत संभावनाएं हो सकती हैं, जिसे 1/3 तक जोड़ने की अनुमति है योग में हर भाजक की एक शक्ति है 2. एक को खोजने का प्रयास करें।

इसके अलावा, यदि एक तुलनित्र के पास उत्तर देने का एक निश्चित मौका है (जैसे (Math.random() < P)*2 - 1, स्थिर के लिए P), तो उपरोक्त प्रमाण है। यदि तुलनित्र इसके बजाय पिछले उत्तरों के आधार पर अपनी बाधाओं को बदलता है, तो उचित परिणाम उत्पन्न करना संभव हो सकता है। दिए गए छँटाई एल्गोरिथ्म के लिए इस तरह के तुलनित्र को खोजना एक शोध पत्र हो सकता है।


1

यदि आप डी 3 का उपयोग कर रहे हैं, तो एक अंतर्निहित फेरबदल फ़ंक्शन (फिशर-येट्स का उपयोग करके) है:

var days = ['Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi','Dimanche'];
d3.shuffle(days);

और यहाँ माइक इसके बारे में विवरण में जा रहा है:

http://bost.ocks.org/mike/shuffle/


0

यहाँ एक दृष्टिकोण है जो एकल सरणी का उपयोग करता है:

मूल तर्क है:

  • एन तत्वों की एक सरणी के साथ शुरू
  • सरणी से एक यादृच्छिक तत्व निकालें और इसे सरणी पर धक्का दें
  • सरणी के पहले n - 1 तत्वों से एक यादृच्छिक तत्व निकालें और इसे सरणी पर धक्का दें
  • सरणी के पहले n - 2 तत्वों से एक यादृच्छिक तत्व निकालें और इसे सरणी पर धक्का दें
  • ...
  • सरणी का पहला तत्व निकालें और इसे सरणी पर पुश करें
  • कोड:

    for(i=a.length;i--;) a.push(a.splice(Math.floor(Math.random() * (i + 1)),1)[0]);

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

    @KirKanos, मुझे यकीन नहीं है कि मैं आपकी टिप्पणी को समझ सकता हूँ। I का प्रस्ताव I O (n) है। यह निश्चित रूप से हर तत्व को "स्पर्श" करने वाला है। यहाँ एक फिडेल को प्रदर्शित करना है।
    ic3b3rg

    0

    क्या आप Array.sort()किसी सरणी में फेरबदल करने के लिए फ़ंक्शन का उपयोग कर सकते हैं - हां।

    क्या परिणाम काफी यादृच्छिक हैं - नहीं।

    निम्नलिखित कोड स्निपेट पर विचार करें:

    var array = ["a", "b", "c", "d", "e"];
    var stats = {};
    array.forEach(function(v) {
      stats[v] = Array(array.length).fill(0);
    });
    //stats = {
    //    a: [0, 0, 0, ...]
    //    b: [0, 0, 0, ...]
    //    c: [0, 0, 0, ...]
    //    ...
    //    ...
    //}
    var i, clone;
    for (i = 0; i < 100; i++) {
      clone = array.slice(0);
      clone.sort(function() {
        return Math.random() - 0.5;
      });
      clone.forEach(function(v, i) {
        stats[v][i]++;
      });
    }
    
    Object.keys(stats).forEach(function(v, i) {
      console.log(v + ": [" + stats[v].join(", ") + "]");
    })

    नमूना उत्पादन:

    a [29, 38, 20,  6,  7]
    b [29, 33, 22, 11,  5]
    c [17, 14, 32, 17, 20]
    d [16,  9, 17, 35, 23]
    e [ 9,  6,  9, 31, 45]

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

    इस लेख में अधिक जानकारी दी गई है:
    सरणी को फेरबदल करने के लिए Array.sort () का उपयोग नहीं किया जाना चाहिए


    -3

    इसमें कुछ भी गलत नहीं है।

    आपके द्वारा .sort () में जाने वाला फ़ंक्शन आमतौर पर कुछ ऐसा दिखता है

    समारोह छँटाई (पहले, दूसरे)
    {
      // उदाहरण:
      पहला - दूसरा;
    }
    

    आपकी नौकरी छाँटने में है

    • एक ऋणात्मक संख्या यदि पहले दूसरे से पहले जाती है
    • एक सकारात्मक संख्या अगर पहले दूसरे के बाद जाना चाहिए
    • और 0 यदि वे पूरी तरह से बराबर हैं

    उपरोक्त सॉर्टिंग फ़ंक्शन चीजों को क्रम में रखता है।

    यदि आप बेतरतीब ढंग से रिटर्न करते हैं और जैसा कि आपके पास है, तो आपको एक यादृच्छिक ऑर्डर मिलता है।

    जैसे MySQL में:

    रैंड () से तालिका ORDER से चुनें *
    

    5
    वहाँ है इस दृष्टिकोण के साथ कुछ गलत: जे एस कार्यान्वयन द्वारा उपयोग में छँटाई कलन विधि के आधार पर, संभावनाओं समान रूप से वितरित नहीं किया जाएगा!
    क्रिस्टोफ

    क्या ऐसा कुछ है जिसकी हम व्यावहारिक रूप से चिंता करते हैं?
    बोब्बोबो

    4
    @ बोबोबोबो: आवेदन के आधार पर, हाँ, कभी-कभी हम करते हैं; इसके अलावा, एक सही ढंग से काम करने के लिए shuffle()केवल एक बार लिखा जाना चाहिए, इसलिए यह वास्तव में कोई मुद्दा नहीं है: बस स्निपेट को अपने कोड तिजोरी में रखें और जब भी आपको इसकी आवश्यकता हो, इसका पता लगाएं
    क्रिस्टोफ
    हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
    Licensed under cc by-sa 3.0 with attribution required.