Depois que Jon já cobriu a teoria , aqui está uma implementação:
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;
}
O algoritmo é O(n), enquanto a classificação deve ser O(n log n). Dependendo da sobrecarga de execução do código JS em comparação com a sort()função nativa , isso pode levar a uma diferença notável no desempenho, que deve aumentar com o tamanho da matriz.
Nos comentários à resposta de bobobobo , afirmei que o algoritmo em questão pode não produzir probabilidades distribuídas uniformemente (dependendo da implementação de sort()).
Meu argumento segue estas linhas: Um algoritmo de classificação requer um certo número cde comparações, por exemplo, c = n(n-1)/2para Bubblesort. Nossa função de comparação aleatória torna o resultado de cada comparação igualmente provável, ou seja, há resultados 2^c igualmente prováveis . Agora, cada resultado deve corresponder a uma das n!permutações das entradas da matriz, o que impossibilita uma distribuição uniforme no caso geral. (Isso é uma simplificação, pois o número real de comparações necessárias depende da matriz de entrada, mas a asserção ainda deve ser mantida.)
Como Jon apontou, isso por si só não é motivo para preferir o uso de Fisher-Yates sort(), pois o gerador de números aleatórios também mapeará um número finito de valores pseudo-aleatórios para as n!permutações. Mas os resultados de Fisher-Yates ainda devem ser melhores:
Math.random()produz um número pseudo-aleatório no intervalo [0;1[. Como o JS usa valores de ponto flutuante de precisão dupla, isso corresponde aos 2^xvalores possíveis em que 52 ≤ x ≤ 63(estou com preguiça de encontrar o número real). Uma distribuição de probabilidade gerada usando Math.random()parará de se comportar bem se o número de eventos atômicos for da mesma ordem de magnitude.
Ao usar Fisher-Yates, o parâmetro relevante é o tamanho da matriz, que nunca deve ser abordada 2^52devido a limitações práticas.
Ao classificar com uma função de comparação aleatória, a função basicamente se importa apenas se o valor de retorno for positivo ou negativo, portanto isso nunca será um problema. Mas existe uma similar: como a função de comparação é bem comportada, os 2^cpossíveis resultados são, como afirmado, igualmente prováveis. Se c ~ n log nentão , 2^c ~ n^(a·n)onde a = const, o que torna pelo menos possível que 2^cseja da mesma magnitude que (ou até menor que) n!e, assim, leve a uma distribuição desigual, mesmo que o algoritmo de classificação seja mapeado uniformemente nas permutações. Se isso tem algum impacto prático está além de mim.
O verdadeiro problema é que não é garantido que os algoritmos de classificação sejam mapeados uniformemente nas permutações. É fácil ver que o Mergesort faz o que é simétrico, mas raciocinar sobre algo como Bubblesort ou, mais importante, Quicksort ou Heapsort, não é.
Conclusão: Enquanto sort()usar o Mergesort, você deverá estar razoavelmente seguro, exceto nos casos de canto (pelo menos, espero que 2^c ≤ n!seja um caso de canto), se não, todas as apostas serão desativadas.