Después de que Jon ya haya cubierto la teoría , aquí hay una implementación:
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;
}
El algoritmo es O(n)
, mientras que la ordenación debería ser O(n log n)
. Dependiendo de la sobrecarga de ejecutar código JS en comparación con la sort()
función nativa , esto podría conducir a una diferencia notable en el rendimiento que debería aumentar con los tamaños de matriz.
En los comentarios a la respuesta de bobobobo , dije que el algoritmo en cuestión podría no producir probabilidades distribuidas uniformemente (dependiendo de la implementación de sort()
).
Mi argumento sigue estas líneas: un algoritmo de clasificación requiere un cierto número c
de comparaciones, por ejemplo, c = n(n-1)/2
para Bubblesort. Nuestra función de comparación aleatoria hace que el resultado de cada comparación sea igualmente probable, es decir, hay resultados 2^c
igualmente probables . Ahora, cada resultado debe corresponder a una de las n!
permutaciones de las entradas de la matriz, lo que hace imposible una distribución uniforme en el caso general. (Esto es una simplificación, ya que el número real de comparaciones necesarias depende de la matriz de entrada, pero la afirmación aún debería mantenerse).
Como señaló Jon, esto por sí solo no es razón para preferir Fisher-Yates en lugar de usarlo sort()
, ya que el generador de números aleatorios también asignará un número finito de valores pseudoaleatorios a las n!
permutaciones. Pero los resultados de Fisher-Yates aún deberían ser mejores:
Math.random()
produce un número pseudoaleatorio en el rango [0;1[
. Como JS usa valores de coma flotante de doble precisión, esto corresponde a los 2^x
posibles valores donde 52 ≤ x ≤ 63
(soy demasiado vago para encontrar el número real). Una distribución de probabilidad generada usando Math.random()
dejará de comportarse bien si el número de eventos atómicos es del mismo orden de magnitud.
Cuando se utiliza Fisher-Yates, el parámetro relevante es el tamaño de la matriz, que nunca debería acercarse 2^52
debido a limitaciones prácticas.
Al ordenar con una función de comparación aleatoria, la función básicamente solo se preocupa si el valor de retorno es positivo o negativo, por lo que esto nunca será un problema. Pero hay una similar: debido a que la función de comparación se comporta bien, los 2^c
posibles resultados son, como se dijo, igualmente probables. Si es c ~ n log n
así , 2^c ~ n^(a·n)
dónde a = const
, lo que hace al menos posible que 2^c
sea de la misma magnitud que (o incluso menor que) n!
y, por lo tanto, conduzca a una distribución desigual, incluso si el algoritmo de clasificación se asigna en las permutaciones de manera uniforme. Si esto tiene algún impacto práctico está más allá de mí.
El verdadero problema es que no se garantiza que los algoritmos de clasificación se asignen uniformemente a las permutaciones. Es fácil ver que Mergesort hace lo que es simétrico, pero el razonamiento sobre algo como Bubblesort o, lo que es más importante, Quicksort o Heapsort, no lo es.
El resultado final: siempre que sort()
use Mergesort, debe estar razonablemente seguro, excepto en los casos de esquina (al menos espero que 2^c ≤ n!
sea un caso de esquina), si no, todas las apuestas están canceladas.