Une fois que Jon a déjà couvert la théorie , voici une implémentation:
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;
}
L'algorithme est O(n)
, alors que le tri devrait l'être O(n log n)
. En fonction de la surcharge de l'exécution du code JS par rapport à la sort()
fonction native , cela peut entraîner une différence notable de performances qui devrait augmenter avec la taille des tableaux.
Dans les commentaires à la réponse de bobobobo , j'ai déclaré que l'algorithme en question pourrait ne pas produire des probabilités uniformément réparties (en fonction de l'implémentation de sort()
).
Mon argument va dans ce sens: Un algorithme de tri nécessite un certain nombre c
de comparaisons, par exemple c = n(n-1)/2
pour Bubblesort. Notre fonction de comparaison aléatoire rend le résultat de chaque comparaison également probable, c'est-à-dire que les résultats sont 2^c
également probables . Désormais, chaque résultat doit correspondre à l'une des n!
permutations des entrées du tableau, ce qui rend impossible une distribution uniforme dans le cas général. (Ceci est une simplification, car le nombre réel de comparaisons nécessaires dépend du tableau d'entrée, mais l'assertion doit toujours être valable.)
Comme Jon l'a souligné, cela seul n'est pas une raison pour préférer Fisher-Yates à l'utilisation sort()
, car le générateur de nombres aléatoires mappera également un nombre fini de valeurs pseudo-aléatoires aux n!
permutations. Mais les résultats de Fisher-Yates devraient encore être meilleurs:
Math.random()
produit un nombre pseudo-aléatoire dans la plage [0;1[
. Comme JS utilise des valeurs à virgule flottante double précision, cela correspond aux 2^x
valeurs possibles où 52 ≤ x ≤ 63
(je suis trop paresseux pour trouver le nombre réel). Une distribution de probabilité générée en utilisant Math.random()
cessera de bien se comporter si le nombre d'événements atomiques est du même ordre de grandeur.
Lors de l'utilisation de Fisher-Yates, le paramètre pertinent est la taille du tableau, qui ne devrait jamais s'approcher en 2^52
raison de limitations pratiques.
Lors du tri avec une fonction de comparaison aléatoire, la fonction ne se soucie essentiellement que si la valeur de retour est positive ou négative, ce ne sera donc jamais un problème. Mais il y en a un similaire: parce que la fonction de comparaison se comporte bien, les 2^c
résultats possibles sont, comme indiqué, également probables. Si c ~ n log n
alors 2^c ~ n^(a·n)
où a = const
, ce qui rend au moins possible qui 2^c
est de la même grandeur que (ou même inférieure à) n!
et conduisant ainsi à une distribution inégale, même si l'algorithme de tri où mapper sur les permutaions de manière uniforme. Si cela a un impact pratique, cela me dépasse.
Le vrai problème est que les algorithmes de tri ne sont pas garantis de mapper uniformément sur les permutations. Il est facile de voir que Mergesort fait comme il est symétrique, mais le raisonnement sur quelque chose comme Bubblesort ou, plus important encore, Quicksort ou Heapsort, ne l'est pas.
La ligne du bas: tant que sort()
vous utilisez Mergesort, vous devriez être raisonnablement en sécurité sauf dans les cas de coin (du moins j'espère que 2^c ≤ n!
c'est un cas de coin), sinon, tous les paris sont ouverts.