Eu tenho uma matriz como esta:
var arr1 = ["a", "b", "c", "d"];
Como posso aleatorizar / embaralhar?
javascript
arrays
random
shuffle
Click Voto a favor
fonte
fonte
Respostas:
O algoritmo shuffle imparcial de fato é o Shuffle de Fisher-Yates (também conhecido como Knuth).
Consulte https://github.com/coolaj86/knuth-shuffle
Você pode ver uma ótima visualização aqui (e a postagem original vinculada a isso )
Mais algumas informações sobre o algoritmo usado.
fonte
i--
não deve ser--i
. Além disso, o testeif (i==0)...
é supérfluo, pois sei == 0
o tempo laço nunca será inserido. A chamada paraMath.floor
pode ser feita mais rapidamente usando...| 0
. De qualquer tempi ou tempj pode ser removida e o valor ser directamente atribuídos a Matriz [i] ou j conforme apropriado.0 !== currentIndex
).Aqui está uma implementação em JavaScript do Durstenfeld shuffle , uma versão otimizada do Fisher-Yates:
Ele escolhe um elemento aleatório para cada elemento original da matriz e o exclui do próximo sorteio, como escolher aleatoriamente um baralho de cartas.
Essa exclusão inteligente troca o elemento selecionado pelo atual e, em seguida, escolhe o próximo elemento aleatório do restante, fazendo um loop para trás para obter uma eficiência ideal, garantindo que a seleção aleatória seja simplificada (sempre pode começar em 0) e, assim, pulando o elemento final.
O tempo de execução do algoritmo é
O(n)
. Observe que o shuffle é feito no local, portanto, se você não quiser modificar a matriz original, primeiro faça uma cópia dela.slice(0)
.EDIT: Atualizando para ES6 / ECMAScript 2015
O novo ES6 nos permite atribuir duas variáveis ao mesmo tempo. Isso é especialmente útil quando queremos trocar os valores de duas variáveis, pois podemos fazer isso em uma linha de código. Aqui está uma forma mais curta da mesma função, usando esse recurso.
fonte
return array
pois o JavaScript passa matrizes por referência quando usado como argumentos de função. Suponho que isso economize espaço na pilha, mas é um pequeno recurso interessante. A execução da reprodução aleatória na matriz embaralha a matriz original.Math.random() should not be multiplied with the loop counter + 1, but with
array.lengt () `. Consulte Gerando números inteiros aleatórios em JavaScript em um intervalo específico? para uma explicação muito abrangente.fonte
Pode-se (ou deveria) usá-lo como um protoype da Matriz:
Partida ChristopheD:
fonte
for...in
loops para iterar sobre matrizes.Você pode fazer isso facilmente com o mapa e classificar:
Você pode embaralhar matrizes polimórficas e a classificação é tão aleatória quanto Math.random, o que é bom o suficiente para a maioria dos propósitos.
Como os elementos são classificados com chaves consistentes que não são regeneradas a cada iteração e cada comparação é extraída da mesma distribuição, qualquer não aleatoriedade na distribuição do Math.random é cancelada.
Rapidez
A complexidade do tempo é O (N log N), o mesmo que a classificação rápida. A complexidade do espaço é O (N). Isso não é tão eficiente quanto um embaralhamento Fischer Yates, mas, na minha opinião, o código é significativamente mais curto e mais funcional. Se você possui uma grande variedade, certamente deve usar o Fischer Yates. Se você tiver uma pequena matriz com algumas centenas de itens, faça isso.
fonte
Use a biblioteca underscore.js. O método
_.shuffle()
é bom para este caso. Aqui está um exemplo com o método:fonte
shuffle
função.NOVO!
Algoritmo de embaralhamento Fisher-Yates mais curto e provavelmente mais rápido
tamanho do script (com fy como nome da função): 90bytes
DEMO http://jsfiddle.net/vvpoma8w/
* mais rápido, provavelmente em todos os navegadores, exceto no Chrome.
Se você tiver alguma dúvida, basta perguntar.
EDITAR
sim é mais rapido
DESEMPENHO: http://jsperf.com/fyshuffle
usando as principais funções votadas.
EDITAR Houve um cálculo em excesso (não precisa --c + 1) e ninguém notou
mais curto (4 bytes) e mais rápido (teste!).
Armazenar em cache em outro lugar
var rnd=Math.random
e depois usá-lornd()
também aumentaria um pouco o desempenho em grandes matrizes.http://jsfiddle.net/vvpoma8w/2/
Versão legível (use a versão original. Isso é mais lento, vars são inúteis, como os fechamentos & ";", o próprio código também é mais curto ... talvez leia isto Como 'minificar' o código Javascript , mas você não é capaz de comprima o código a seguir em minificadores de javascript como o acima.)
fonte
fy
eshuffle prototype
, ficofy
consistente na parte inferior do Chrome 37 no OS X 10.9.5 (81% mais lento ~ 20k ops do que ~ 100k) e o Safari 7.1 é até ~ 8% mais lento. YMMV, mas nem sempre é mais rápido. jsperf.com/fyshuffle/3Editar: esta resposta está incorreta
Consulte os comentários e https://stackoverflow.com/a/18650169/28234 . Está sendo deixado aqui para referência, porque a idéia não é rara.
Uma maneira muito simples para pequenas matrizes é simplesmente esta:
Provavelmente não é muito eficiente, mas para pequenas matrizes isso funciona muito bem. Aqui está um exemplo para que você possa ver como é aleatório (ou não) e se ele se encaixa no seu caso de uso ou não.
fonte
Confiável, Eficiente, Curto
Algumas soluções nesta página não são confiáveis (elas apenas aleatoriamente parcialmente a matriz). Outras soluções são significativamente menos eficientes. Com
testShuffleArrayFun
(veja abaixo), podemos testar as funções de embaralhamento da matriz para obter confiabilidade e desempenho. As seguintes soluções são: confiáveis, eficientes e curtas (usando a sintaxe ES6)[Testes de comparação foram feitos com
testShuffleArrayFun
outras soluções, no Google Chrome]Shuffle Array In Place
ES6 Puro, Iterativo
Teste de confiabilidade e desempenho
Como você pode ver nesta página, houve soluções incorretas oferecidas aqui no passado. Eu escrevi e usei a seguinte função para testar qualquer função aleatória pura de matriz (sem efeitos colaterais).
Outras soluções
Outras soluções apenas por diversão.
ES6 Puro, Recursivo
ES6 Pure usando array.map
ES6 Pure usando array.reduce
fonte
[array[i], array[rand]]=[array[rand], array[i]]
? Talvez você possa descrever como isso funciona. Por que você escolhe iterar para baixo?Adicionando à resposta @Laurens Holsts. Isso é 50% compactado.
fonte
var b =
um loop em vez de declarar um loop externo b e atribuí-lo comb =
um loop?Editar: esta resposta está incorreta
Consulte https://stackoverflow.com/a/18650169/28234 . Está sendo deixado aqui para referência, porque a idéia não é rara.
https://javascript.info/task/shuffle
fonte
Com o ES2015, você pode usar este:
Uso:
fonte
n >>> 0
vez de~~n
. Os índices da matriz podem ser maiores que 2³¹-1.Encontrei essa variante nas respostas "excluídas pelo autor" em uma duplicata desta pergunta. Ao contrário de algumas das outras respostas que já têm muitos votos positivos, isso é:
shuffled
nomeshuffle
)Aqui está um jsfiddle mostrando-o em uso .
fonte
[1,2,3,4,5,6].sort(function() { return .5 - Math.random(); });
- ele não lhe dá uma espécie aleatória, e se você usá-lo você pode acabar envergonhado: robweir.com/blog/2010/02/microsoft-random-browser-ballot.html.sort(function(a,b){ return a[0] - b[0]; })
se quiser que a classificação compare valores numericamente. O.sort()
comparador padrão é lexicográfico, o que significa que será considerado10
menor que,2
pois1
é menor que2
.Math.random()
produz. (isto é, a ordem lexicográfica é o mesmo que ordem numérica quando se lida com números de 0 (inclusive) a 1 (exclusive))fonte
fonte
Você pode fazer isso facilmente com:
Consulte em JavaScript Sorting Arrays
fonte
Uma solução recursiva:
fonte
Embaralhamento de Fisher-Yates em javascript. Estou postando isso aqui porque o uso de duas funções utilitárias (swap e randInt) esclarece o algoritmo comparado às outras respostas aqui.
fonte
Antes de tudo, dê uma olhada aqui para uma ótima comparação visual de diferentes métodos de classificação em javascript.
Em segundo lugar, se você der uma olhada rápida no link acima, verá que o
random order
tipo parece ter um desempenho relativamente bom em comparação com outros métodos, além de ser extremamente fácil e rápido de implementar, como mostrado abaixo:Edit : como apontado por @gregers, a função de comparação é chamada com valores em vez de índices, e é por isso que você precisa usar
indexOf
. Observe que essa alteração torna o código menos adequado para matrizes maiores à medida queindexOf
é executado no tempo O (n).fonte
Array.prototype.sort
passa em dois valores comoa
eb
, não o índice. Portanto, esse código não funciona.uma função aleatória que não altera a matriz de origem
Atualização : aqui estou sugerindo um algoritmo relativamente simples (não da perspectiva da complexidade ) e curto que funcionará bem com matrizes de tamanho pequeno, mas definitivamente vai custar muito mais do que o algoritmo clássico de Durstenfeld quando você lida com matrizes enormes. Você pode encontrar o Durstenfeld em uma das principais respostas a esta pergunta.
Resposta original:
Se você não deseja que a função de reprodução aleatória mude a matriz de origem , copie-a para uma variável local e faça o resto com uma lógica de reprodução aleatória simples .
Baralhar lógica : escolha um índice aleatório e adicione o elemento correspondente à matriz de resultados e exclua-o da cópia da matriz de origem . Repita esta ação até que a matriz de origem fique vazia .
E se você realmente quer que ele seja curto, aqui está o quão longe eu poderia chegar:
fonte
splice
sendo você uma maneira terrivelmente ineficiente de fazer o que eles chamavam de "eliminar". Se você não deseja alterar a matriz original, copie-a e embaralhe essa cópia no lugar usando a variante Durstenfeld, muito mais eficiente.splice
método para criar uma cópia da seguinte forma:source = array.slice();
.Aqui está o mais fácil ,
Para mais exemplos, você pode conferir aqui
fonte
mais uma implementação do Fisher-Yates, usando o modo estrito:
fonte
Todas as outras respostas são baseadas em Math.random (), que é rápido, mas não é adequado para a randomização no nível criptográfico.
O código abaixo está usando o conhecido
Fisher-Yates
algoritmo enquanto utilizaWeb Cryptography API
para o nível criptográfico de randomização .fonte
Solução moderna em linha curta usando os recursos do ES6:
(para fins educacionais)
fonte
Uma modificação simples da resposta de CoolAJ86 que não modifica a matriz original:
fonte
Embora já existam várias implementações recomendadas, mas acho que podemos torná-lo mais curto e fácil usando o loop forEach, portanto, não precisamos nos preocupar em calcular o comprimento da matriz e também podemos evitar o uso seguro de uma variável temporária.
fonte
Só para ter um dedo na torta. Aqui apresento uma implementação recursiva do embaralhamento de Fisher Yates (eu acho). Dá aleatoriedade uniforme.
Nota: O
~~
(operador double til) na verdade se comporta comoMath.floor()
para números reais positivos. É só um atalho.Edit: O código acima é O (n ^ 2) devido ao emprego de,
.splice()
mas podemos eliminar emendas e embaralhamento em O (n) pelo truque de swap.O problema é que o JS não pode cooperar com grandes recursões. Nesse caso em particular, o tamanho da sua matriz é limitado a 3000 ~ 7000, dependendo do mecanismo do navegador e de alguns fatos desconhecidos.
fonte
Randomize array
fonte
-1
para n que você usou<
não<=
a
arrayShuffle
função mais curtafonte
Do ponto de vista teórico, a maneira mais elegante de fazê-lo, na minha humilde opinião, é obter um único número aleatório entre 0 e n! -1 e calcular um mapeamento de um para um de
{0, 1, …, n!-1}
todas as permutações de(0, 1, 2, …, n-1)
. Contanto que você possa usar um gerador (pseudo-) aleatório confiável o suficiente para obter um número sem viés significativo, você terá informações suficientes para alcançar o que deseja sem precisar de vários outros números aleatórios.Ao calcular com números flutuantes de precisão dupla IEEE754, você pode esperar que seu gerador aleatório forneça cerca de 15 casas decimais. Como você possui 15! = 1.307.674.368.000 (com 13 dígitos), é possível usar as seguintes funções com matrizes que contêm até 15 elementos e assumir que não haverá viés significativo com matrizes que contêm até 14 elementos. Se você trabalha com um problema de tamanho fixo que exige calcular muitas vezes essa operação de reprodução aleatória, tente o código a seguir, que pode ser mais rápido que outros códigos, pois ele usa
Math.random
apenas uma vez (no entanto, envolve várias operações de cópia).A seguinte função não será usada, mas eu a dou de qualquer maneira; retorna o índice de uma dada permutação de
(0, 1, 2, …, n-1)
acordo com o mapeamento de um para um usado nesta mensagem (o mais natural ao enumerar as permutas); destina-se a trabalhar com até 16 elementos:O recíproco da função anterior (necessário para sua própria pergunta) está abaixo; destina-se a trabalhar com até 16 elementos; retorna a permutação da ordem n de
(0, 1, 2, …, s-1)
:Agora, o que você deseja é apenas:
Deverá funcionar com até 16 elementos com um pequeno viés teórico (embora imperceptível do ponto de vista prático); pode ser visto como totalmente utilizável por 15 elementos; com matrizes contendo menos de 14 elementos, você pode considerar com segurança que não haverá absolutamente nenhum viés.
fonte