Complete random permutation algorithm for JS array _javascript tips

Source: Internet
Author: User
Tags arrays shuffle

The Array.prototype.sort method is mistakenly used by many JavaScript programmers to randomly arrange arrays. Recently done in front-end Star Program Challenge Project, together to achieve blackjack game problem, found that many students use Array.prototype.sort to shuffle.

Shuffle

The following are common, completely wrong random permutation algorithms:

Function Shuffle (arr) {return
 Arr.sort (function () {return
 math.random ()-0.5;
 });

The above code seems to use the Array.prototype.sort to achieve random, but there are very serious problems, even completely wrong.

Proving the error of Array.prototype.sort stochastic algorithm

To prove the error of this algorithm, we design a test method. Assuming that this sort algorithm is correct, then use this algorithm for random arrays [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], and if the algorithm is correct, then each digit appears in every bit of equal probability. Therefore, repeat the array repeatedly enough to shuffle, each result is then added to each digit, and the result of each bit is averaged, which should be about equal to (0 + 9)/2 = 4.5, the more times the test is taken, the closer the average on each one should be to 4.5. So we simply implement the test code as follows:

var arr = [0,1,2,3,4,5,6,7,8,9];
var res = [0,0,0,0,0,0,0,0,0,0];
var t = 10000;
for (var i = 0; i < T; i++) {
 var sorted = Shuffle (Arr.slice (0));
 Sorted.foreach (function (o,i) {
 res[i] = o;
 });
res = Res.map (function (o) {return
 o/t;
});
Console.log (RES);

The above shuffle method with this test code in the Chrome browser test, you can draw the results, found that the results are not randomly distributed, the average value of each position more and more, which means that the greater the number of the random algorithm is now more than the probability of the future.

Why does it have this result? We need to understand how Array.prototype.sort works.

First we know that there are many kinds of sorting algorithms, and ECMAScript does not specify what sort algorithm Array.prototype.sort must use.

Sorting is not the topic we are discussing today, but regardless of the sort algorithm, it is necessary to compare and exchange between two numbers, and the efficiency of the sorting algorithm is related to the number of comparisons and exchanges between the two numbers.

The most basic sort has bubble sort and insert sort, the original bubble or insert sort all compares N (n-1)/2 times, that is to say, the elements of any two positions are compared. So in this case, if the previous sort stochastic algorithm is used, and because each comparison has a 50% chance of switching and not swapping, is the result random and homogeneous? We can take a look at the example:

function Bubblesort (arr, compare) {
 var len = arr.length;
 for (var i = 0; i < len-1; i++) {for
 (var j = 0; J < len-1-I; j +) {
 var k = j + 1;
 if (compare (Arr[j], arr[k]) > 0) {
 var tmp = arr[j];
 ARR[J] = arr[k];
 ARR[K] = tmp;
 }}} return arr;
}
Function Shuffle (arr) {return
 bubblesort (arr, function () {return
 math.random ()-0.5;
 });
var arr = [0,1,2,3,4,5,6,7,8,9];
var res = [0,0,0,0,0,0,0,0,0,0];
var t = 10000;
for (var i = 0; i < T; i++) {
 var sorted = Shuffle (Arr.slice (0));
 Sorted.foreach (function (o,i) {
 res[i] = o;
 });
res = Res.map (function (o) {return
 o/t;
});
Console.log (RES);

The random results of the above code are also uneven , and the results of the test averages become larger. (the author did not copy the original array before so the error came evenly to the conclusion that was corrected in 2016-05-10)

The bubbling sort always swaps the less-than-expected element with its previous element, we can think about it, the more the next element of the algorithm, the smaller the probability of switching to the position of the previous (because there is only 50% chance of "bubbling" each time), the original array is ordered from small to large, So the result of the Test average is the larger the future (because the probability of the larger number appearing in front is smaller).

Let's switch to another algorithm, and we'll use the insertion order this time:

function Insertionsort (arr, compare) {
 var len = arr.length;
 for (var i = 0; i < len; i++) {for
 (var j = i + 1; j < Len; J +) {
 if (compare (Arr[i), arr[j]) > 0) {
 var TMP = Arr[i];
 Arr[i] = arr[j];
 ARR[J] = tmp;
 }}} return arr;
}
Function Shuffle (arr) {return
 insertionsort (arr, function () {return
 math.random ()-0.5;
 });
var arr = [0,1,2,3,4,5,6,7,8,9];
var res = [0,0,0,0,0,0,0,0,0,0];
var t = 10000;
for (var i = 0; i < T; i++) {
 var sorted = Shuffle (Arr.slice (0));
 Sorted.foreach (function (o,i) {
 res[i] = o;
 });
res = Res.map (function (o) {return
 o/t;
});
Console.log (RES);

Because the insertion sort is looking for a large number to be followed by an exchange of the previous number, this time the result is the same as the bubble sort, and the result of the Test average is naturally smaller. The reason is similar to the above, for the insertion sort, the more backward the number of the more easy to randomly switch to the front.

So we see that even a 22-switched sorting algorithm, the random distribution difference is also relatively large. In addition to this sort algorithm, which is compared to each position 22, the time complexity of most sorting algorithms is between O (n) to O (N2), and the number of comparisons between elements is usually much less than n (n-1)/ 2, it means that there is no chance of comparison between some elements (and there is no possibility of random exchange), these sort random sorting algorithm can not really random.

We'll change the code above to use a quick sort:

function QuickSort (arr, compare) {
 arr = arr.slice (0);
 if (arr.length <= 1) return arr;
 var mid = arr[0], rest = Arr.slice (1);
 var left = [], right = [];
 for (var i = 0; i < rest.length i++) {
 if (compare (Rest[i), Mid) > 0) {
 right.push (rest[i));
 else{
 Left.push (Rest[i]);
 }
 Return QuickSort (left, compare). Concat ([mid])
 . Concat (QuickSort (right, compare));
Function Shuffle (arr) {return
 quickSort (arr, function () {return
 math.random ()-0.5;
 });
var arr = [0,1,2,3,4,5,6,7,8,9];
var res = [0,0,0,0,0,0,0,0,0,0];
var t = 10000;
for (var i = 0; i < T; i++) {
 var sorted = Shuffle (Arr.slice (0));
 Sorted.foreach (function (o,i) {
 res[i] = o;
 });
res = Res.map (function (o) {return
 o/t;
});
Console.log (RES);

The quick sort has no 22 elements to compare, and its probability distribution is not random.

So we can come to the conclusion that the random permutation of the array by Array.prototype.sort random, the result is not necessarily random, but depends on how the sorting algorithm is implemented, with JavaScript built-in sorting algorithm so sorted, usually certainly is Not completely random .

The classic random arrangement

All spatial Complexity O (1) Sorting algorithms have a time complexity between O (Nlogn) and O (N2), so it is slow to use sorting to randomly exchange without considering the error of algorithm results. In fact, random array elements have classical O (n) Complexity algorithms:

Function Shuffle (arr) {
 var len = arr.length;
 for (var i = 0; i < len-1 i++) {
 var idx = Math.floor (Math.random () * (len-i));
 var temp = Arr[idx];
 ARR[IDX] = arr[len-i-1];
 Arr[len-i-1] = temp;
 }
 return arr;
}

In the above algorithm, each time we loop a random position in the previous len-i element, the element is exchanged with the len-i element, iterating until I = len-1.

We can also examine the randomness of this algorithm:

Function Shuffle (arr) {
 var len = arr.length;
 for (var i = 0; i < len-1 i++) {
 var idx = Math.floor (Math.random () * (len-i));
 var temp = Arr[idx];
 ARR[IDX] = arr[len-i-1];
 Arr[len-i-1] = temp;
 }
 return arr;
}
var arr = [0,1,2,3,4,5,6,7,8,9];
var res = [0,0,0,0,0,0,0,0,0,0];
var t = 10000;
for (var i = 0; i < T; i++) {
 var sorted = Shuffle (Arr.slice (0));
 Sorted.foreach (function (o,i) {
 res[i] = o;
 });
res = Res.map (function (o) {return
 o/t;
});
Console.log (RES);

It can be seen from the result that the random result of this algorithm should be homogeneous. But our test method actually has a small problem, we only tested the average, in fact, the average is close to only the necessary rather than sufficient conditions, the average is not necessarily uniform distribution. But don't worry, actually we can simply mathematically prove the randomness of the algorithm.

The mathematical inductive method of randomness proves

The number of n is random:

First of all, we consider the case of n = 2, according to the algorithm, it is obvious that 1/2 of the probability of two exchange, 1/2 of the probability of two numbers do not exchange, so for n = 2, the probability of elements appearing in each position is 1/2, to meet the randomness requirements.

Given the number of I, I >= 2 o'clock, the algorithm randomness meets the requirements, that is, the probability of each number in the position I place is 1/i.

For i + 1 numbers, according to our algorithm, the probability of each number having 1/(i+1) is exchanged to the very end in the first cycle, so the probability of each element appearing at the last digit is 1/(i+1). And each number also has a i/(i+1) probability is not exchanged to the end, if not exchanged, from the second cycle start to restore the number of I random, according to 2. hypothesis, the probability that they appear in the I position is 1/i. So the probability that each number appears in the first I bit is (i/(i+1)) * (1/i) = 1/(i+1) and 1/(i+1).

Synthesis 1. 2.3. It is concluded that for any n >= 2, after this algorithm, the probability of each element appearing in any position in n position is 1/n.

Summarize

An excellent algorithm is to satisfy the results correctly and efficiently. Unfortunately, the use of the Array.prototype.sort method is not satisfying either of these conditions. Therefore, when we need to implement a similar shuffle function, or should be the use of clever classic shuffle algorithm, it is not only completely random and very high efficiency.

In addition to harvesting such algorithms, we should take this hands-on analysis and problem-solving approach seriously, and pick up math that we've learned and forgotten by most people (such as the classical method of mathematical induction).

Have any questions welcome to discuss with the author ~

The above is the entire content of this article, I hope the content of this article for everyone's study or work can bring some help, but also hope that a lot of support cloud Habitat community!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.