Nachdem Jon die Theorie bereits behandelt hat , folgt eine Implementierung:
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;
}
Der Algorithmus ist O(n)
, während das Sortieren sein sollte O(n log n)
. Abhängig vom Aufwand für die Ausführung von JS-Code im Vergleich zur nativen sort()
Funktion kann dies zu einem spürbaren Leistungsunterschied führen, der mit der Arraygröße zunehmen sollte.
In den Kommentaren zu Bobobobos Antwort stellte ich fest, dass der betreffende Algorithmus möglicherweise keine gleichmäßig verteilten Wahrscheinlichkeiten erzeugt (abhängig von der Implementierung von sort()
).
Mein Argument geht in diese Richtung: Ein Sortieralgorithmus erfordert eine bestimmte Anzahl c
von Vergleichen, z c = n(n-1)/2
. B. für Bubblesort. Unsere zufällige Vergleichsfunktion macht das Ergebnis jedes Vergleichs gleich wahrscheinlich, dh es gibt 2^c
gleich wahrscheinliche Ergebnisse. Jetzt muss jedes Ergebnis einer der n!
Permutationen der Einträge des Arrays entsprechen, was im allgemeinen Fall eine gleichmäßige Verteilung unmöglich macht. (Dies ist eine Vereinfachung, da die tatsächliche Anzahl der erforderlichen Vergleiche vom Eingabearray abhängt, die Behauptung jedoch weiterhin gelten sollte.)
Wie Jon betonte, ist dies allein kein Grund, Fisher-Yates der Verwendung vorzuziehen sort()
, da der Zufallszahlengenerator den n!
Permutationen auch eine endliche Anzahl von Pseudozufallswerten zuordnet. Aber die Ergebnisse von Fisher-Yates sollten immer noch besser sein:
Math.random()
erzeugt eine Pseudozufallszahl im Bereich [0;1[
. Da JS Gleitkommawerte mit doppelter Genauigkeit verwendet, entspricht dies 2^x
möglichen Werten, bei denen 52 ≤ x ≤ 63
(ich bin zu faul, um die tatsächliche Zahl zu finden). Eine mit erzeugte Wahrscheinlichkeitsverteilung Math.random()
verhält sich nicht mehr gut, wenn die Anzahl der atomaren Ereignisse in der gleichen Größenordnung liegt.
Bei Verwendung von Fisher-Yates ist der relevante Parameter die Größe des Arrays, die sich 2^52
aus praktischen Gründen niemals annähern sollte .
Beim Sortieren mit einer zufälligen Vergleichsfunktion kümmert sich die Funktion grundsätzlich nur darum, ob der Rückgabewert positiv oder negativ ist, sodass dies niemals ein Problem darstellt. Aber es gibt eine ähnliche: Da sich die Vergleichsfunktion gut verhält, sind die 2^c
möglichen Ergebnisse, wie gesagt, ebenso wahrscheinlich. Wenn c ~ n log n
dann 2^c ~ n^(a·n)
wo a = const
, was es zumindest möglich macht, dass 2^c
es die gleiche Größe wie (oder sogar weniger als) hat n!
und somit zu einer ungleichmäßigen Verteilung führt, selbst wenn der Sortieralgorithmus gleichmäßig auf die Permutationen abgebildet werden soll. Ob dies praktische Auswirkungen hat, ist mir ein Rätsel.
Das eigentliche Problem besteht darin, dass nicht garantiert wird, dass die Sortieralgorithmen gleichmäßig auf die Permutationen abgebildet werden. Es ist leicht zu erkennen, dass Mergesort symmetrisch funktioniert, aber wenn man über etwas wie Bubblesort oder, was noch wichtiger ist, Quicksort oder Heapsort nachdenkt, ist dies nicht der Fall.
Fazit: Solange sort()
Sie Mergesort verwenden, sollten Sie einigermaßen sicher sein, außer in Eckfällen (zumindest hoffe ich, dass dies 2^c ≤ n!
ein Eckfall ist). Wenn nicht, sind alle Wetten ungültig.