Array.sort() should not be used to shuffle an array
When a developer searches the web for an algorithm to shuffle an array in JavaScript or ActionScript, he will surely find a way to achieve that using the Array.sort() method (see references : JavaScript, ActionScript 3). While this method is intended to sort an array, it can also be used to randomize it (!). In general, here and there, the following algorithm comes forth :/**
* Add a randomize method to the Array object prototype
* Usage :
* var tmpArray = ["a", "b", "c", "d", "e"];
* tmpArray.randomize();
*/
Array.prototype.randomize = function (){
this.sort(
function(a,b) {
return Math.round( Math.random() * 2 ) - 1;
}
);
};
* Add a randomize method to the Array object prototype
* Usage :
* var tmpArray = ["a", "b", "c", "d", "e"];
* tmpArray.randomize();
*/
Array.prototype.randomize = function (){
this.sort(
function(a,b) {
return Math.round( Math.random() * 2 ) - 1;
}
);
};
Shuffling an array with Array.sort() results in… a not so shuffled array
Implementing a shuffle function on arrays, this way, is bad. At first glance it seems to produce well randomized arrays, but the random distribution is not uniform. I will illustrate this in a minute. Before that, give a look at the table below. It’s interactive. When you click start, it will run the following process :-
Create an array with ten letters in alphabetical orders from A to J
var simpleArray = ["A","B","C","D","E","F","G","H","I","J"];
- Copy this array in a second array
- Run a shuffle algorithm against the second array
- Look where the letters are in the second array and record their position in the table columns
- Return to the step 2 and start the process again until a certain number of iterations has been reached
How to better shuffle an array with Array.sort() ?
We said the comparison function returned randomly either -1, 0 or 1. What exactly happens in each case ? Let’s quote the Mozilla developer Center for the Array.sort() comparison function description
If compareFunction is supplied, the array elements are sorted according to the return value of the compare function. If a and b are two elements being compared, then:
The Array.sort() method uses a Bubble Sort algorithm. Roughly, it steps through array elements and compare each one with its next adjacent element. It swaps the elements when the comparison function return 1. What does it do when the comparison function return 0 or -1 ? Actually nothing and here is the issue ! As the comparison function randomly generate -1, 0 or 1, each of those numbers has the same chance to arise. Unfortunately a letter can change its position only if 1 arises. It means that a letter has twice the chance not to move than to move !
So to resolve the issue the comparison function must not generate 3 numbers randomly but only two : 0 or 1. Thus a letter has the same chance to move than to stay in place. Below is the new implementation (Note that it is simpler than the previous one).
- If compareFunction(a, b) is less than 0, sort a to a lower index than b.
- If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAscript standard does not guarantee this behaviour, and thus not all browsers (e.g. Mozilla versions dating back to at least 2003) respect this.
- If compareFunction(a, b) is greater than 0, sort b to a lower index than a.
/**
* Add a randomize method to the Array object prototype
* Usage :
* var tmpArray = ["a", "b", "c", "d", "e"];
* tmpArray.randomize2();
*/
Array.prototype.randomize2 = function (){
this.sort(
function(a,b) {
return Math.round( Math.random() );;
}
);
};
* Add a randomize method to the Array object prototype
* Usage :
* var tmpArray = ["a", "b", "c", "d", "e"];
* tmpArray.randomize2();
*/
Array.prototype.randomize2 = function (){
this.sort(
function(a,b) {
return Math.round( Math.random() );;
}
);
};
Shuffle arrays the good way
Fortunately it is not required to use Array.sort() to shuffle an array. Implement a shuffle function using Fisher-Yates algorithm is better and still easy. Below is the code implementing a shuffle function using the Fisher-Yates algorithm. This algorithm provides uniform random./*
* Add a shuffle function to Array object prototype
* Usage :
* var tmpArray = ["a", "b", "c", "d", "e"];
* tmpArray.shuffle();
*/
Array.prototype.shuffle = function (){
var i = this.length, j, temp;
if ( i == 0 ) return;
while ( --i ) {
j = Math.floor( Math.random() * ( i + 1 ) );
temp = this[i];
this[i] = this[j];
this[j] = temp;
}
};
* Add a shuffle function to Array object prototype
* Usage :
* var tmpArray = ["a", "b", "c", "d", "e"];
* tmpArray.shuffle();
*/
Array.prototype.shuffle = function (){
var i = this.length, j, temp;
if ( i == 0 ) return;
while ( --i ) {
j = Math.floor( Math.random() * ( i + 1 ) );
temp = this[i];
this[i] = this[j];
this[j] = temp;
}
};
Tags: ActionScript, Array, JavaScript, Random, Randomize, Sort
Comment 380
[...] e spiega ai tecnicamente inclinati come andrebbe evitato. Per superprogrammatori si trova anche una descrizione teorica e pratica della [...]
Comment 372
Stéphane, on http://opentochoice.org you posted about your page on Array.sort().
It’s not clear to me why.
Are you implying that Mozilla is doing something unfair with it? and how would they benefit from it???
Comment 373
Not at all. The European Commission forces Microsoft to show Windows users a screen asking to choose fro a browser if they still use the one by default. The web site http://opentochoice.org is the Mozilla website to help people make a decision. The window which will be displayed to Windows users to ask them to make their choice is a web page developed by and hosted at Microsoft’s : http://www.browserchoice.eu (Not to be confused with the previous one). The Microsoft web page at http://www.browserchoice.eu should display browsers in random order. This is where they use a bad implementation of a random Algorithm. I don’t think this is for anybody a way to benefit to something. It’s just the result of the web spreading some bad habits and developers to appropriate them. Even those at Microsoft’s.
Comment 364
Perhaps I’m missing something, but this seems an awfully clumsy way of doing things – using a deliberately broken comparison function to make the sort do something other than sort.
If you have access to a sort function and a random generator, why not just assign a large random integer to each item in the set, then sort the resulting pairs by the numeric order?
Comment 365
It’s always a matter of efficiency. The comparison function is not “deliberately broken”. It is built to be efficient at what it is intended to, ie sort things. Using it to shuffle things is, at a first glance, a clever way to use its built-in speediness but it turns that it’s a bad idea.
Comment 366
You misunderstand – the user-provided comparison function *is* deliberately broken, returning a random value in order to break the sort algorithm, trying to force it to un-sort the array instead. Even without the statistical demonstration of why it’s invalid, this approach is screaming out ‘bad idea’ to me…
Comment 369
You right. That’s intellectually weird, but well spread across the web unfortunately.
Comment 363
Simply excellent. Great explanation and good algorithms !
Comment 131
Very nice explanation and demos! (Chanced upon this while looking for info on shuffling arrays without consecutive repeats.)