The Array.prototype.sort method was mistakenly used by many JavaScript programmers to arrange arrays randomly. Recently done the front-end Star Plan Challenge Project, a realization of blackjack game problem, found that many students used Array.prototype.sort to shuffle. Even one of the articles recommended on the latest issue of JavaScript Weekly made the same mistake.
The following is a common, completely wrong, random permutation algorithm:
function shuffle(arr){ return arr.sort(function(){ return Math.random() - 0.5; });}
The above code seems to have cleverly exploited the Array.prototype.sort to achieve randomness, but there are very serious problems, even a complete error.
Proving the error of Array.prototype.sort stochastic algorithm
To prove the error of this algorithm, we design a test method. Assuming that the sorting algorithm is correct, then the algorithm is used for random arrays [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], and if the algorithm is correct, then each digit will appear in equal probability for each digit. Therefore, the array repeats the shuffle enough times, and then each time the result is added to each bit, the result of each bit is averaged, the average value should be approximately equal (0 + 9) / 2 = 4.5
to the number of tests, the average on each one should be closer 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);
Test the above shuffle
method with this test code in the Chrome browser, you can get the results, found that the results are not randomly distributed, the average of each location is larger, which means that the larger the number of random algorithm is now the more the probability of the later.
Why does it produce this result? We need to understand how Array.prototype.sort really 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. Here, interested students may wish to look at the JavaScriptCore source code implementation:
Sorting is not the subject of our discussion 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 two numbers.
The most basic sort is bubble sort and insert sort, the original bubble or insert sort compares n (n-1)/2 times, which means that any two-position elements are compared once. So in this case, if using the previous sort random algorithm, because each comparison has a 50% chance to exchange and not exchange, the result is random uniform? Let's take a look at the example:
functionBubblesort (Arr, compare) {var len = arr.length;Forvar i =0; I < Len-1; i++) {Forvar 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;}functionShuffleARR) {Return Bubblesort (arr,function){ReturnMath.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 result of the above code is also uneven, and the result of the test mean is greater. (the author did not copy the original array, so the error came to an even conclusion, has been corrected to 2016-05-10)
The bubbling sort always swaps the smaller elements of the comparison with its previous element, and we can think about it, the more the later the element, the smaller the probability of switching to the former position (because each time there is only a 50% chance of "bubbling"), the original array is ordered from small to large, So the result of testing the average is that the farther back is the greater (because the probability of the larger number appearing in the front is lower).
Let's change the algorithm, we'll sort this one by inserting:
functionInsertionsort (Arr, compare) {var len = arr.length;Forvar i =0; i < Len; i++) {Forvar 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;}functionShuffleARR) {Return Insertionsort (arr,function){ReturnMath.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 swapped for the large number behind it and the previous number, this time the result is the opposite of the bubbling sort, and the result of the test mean is naturally smaller and lower. The reason is similar to the above, for the insertion sort, the more backward the number is easier to randomly switch to the front.
So we see that even the 22-exchange sorting algorithm, the random distribution difference is also relatively large. In addition to this sort algorithm that compares each location 22, the time complexity of most sorting algorithms is between O (n) and O (N2), and the number of comparisons between elements is typically much less than n (n-1)/ 2, it means that there is no chance of comparison between some elements (there is no possibility of random exchange), these sort of random sorting algorithms naturally can not be really random.
We'll change the code above to use a quick sort:
functionQuickSort (Arr, compare) {arr = Arr.slice (0);if (Arr.length <=1)return arr;var mid = arr[0], rest = Arr.slice (1);var = [], right = [];Forvar 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));}functionShuffleARR) {Return QuickSort (arr,function){ReturnMath.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 is not compared with 22 elements, and its probability distribution is not random.
So we can conclude that the random permutation of the array by Array.prototype.sort is not necessarily random, but rather depends on how the sorting algorithm is implemented, which is usually not completely random by using JavaScript's built-in sorting algorithm.
The classic random arrangement
The time complexity of all spatial complexity O (1) Sorting algorithms is between O (Nlogn) to O (N2), so it is slow to use sorting to randomly swap without considering the error of the algorithm results. In fact, random array elements have the classic O (n) complexity 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;}
In the algorithm above, each time we loop a random position in the previous len-i element, the element and the Len-i element are exchanged, iterating until I = len-1.
We can also examine the randomness of this algorithm:
functionShuffleARR) {var len = arr.length;Forvar 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 results that the random results of this algorithm should be uniform. However, our test method actually has a small problem, we only test the average value, in fact, the average is close to just the necessary rather than sufficient conditions for uniform distribution, the average approximation is not necessarily uniform distribution. But don't worry, in fact we can simply mathematically prove the randomness of the algorithm.
The mathematical inductive method of randomness proves
The number of n is random:
First we consider the case of n = 2, according to the algorithm, obviously there is a probability of 1/2 two number of exchanges, there is a 1/2 probability two number does not exchange, so for the case of n = 2, the probability that the element appears in each position is 1/2, satisfies the randomness requirement.
Assuming there is a number of I, I >= 2 o'clock, the algorithm randomness meets the requirements, that is, the probability that each position in the I position is now 1/i.
For i + 1 numbers, according to our algorithm, in the first loop, each number has a probability of 1/(i+1) being exchanged to the very end, so the probability of each element appearing at the last bit is 1/(i+1). And each number also has i/(i+1) probability is not exchanged to the end, if not exchanged, starting from the second cycle 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 of each number appearing in the first I bit is (i/(i+1)) * (1/i) = 1/(i+1)
1/(i+1).
Synthesis 1. 2.3. It is concluded that for any n >= 2, the probability of each element appearing at any position in n positions is 1/n, after this algorithm.
Summarize
A good algorithm to satisfy both the correct results and high efficiency. Unfortunately, these two conditions are not satisfied with the Array.prototype.sort method. Therefore, when we need to achieve a similar shuffle function, or should use a clever classic shuffle algorithm, it is not only a complete randomness and high efficiency.
In addition to harvesting such algorithms, we should also take this kind of hands-on analysis and problem-solving ideas, and pick up the mathematics we have learned and forgotten by most people (such as the classical proof of mathematical induction method).
Transfer from https://www.h5jun.com/post/array-shuffle.html
Complete random arrangement of arrays