J'ai un tableau comme celui-ci:
var arr1 = ["a", "b", "c", "d"];
Comment puis-je le randomiser / le mélanger?
javascript
arrays
random
shuffle
Cliquez Upvote
la source
la source
Réponses:
L'algorithme de shuffle de facto non biaisé est le shuffle de Fisher-Yates (alias Knuth).
Voir https://github.com/coolaj86/knuth-shuffle
Vous pouvez voir une grande visualisation ici (et le post original lié à cela )
Quelques informations supplémentaires sur l'algorithme utilisé.
la source
i--
pas l' être--i
. En outre, le testif (i==0)...
est superflu car sii == 0
l' en boucle ne sera jamais entré. L'appel àMath.floor
peut être fait plus rapidement en utilisant...| 0
. Soit tempi ou tempj peuvent être supprimés et la valeur directement affectée à myArray [i] ou j selon le cas.0 !== currentIndex
).Voici une implémentation JavaScript du shuffle Durstenfeld , une version optimisée de Fisher-Yates:
Il sélectionne un élément aléatoire pour chaque élément du tableau d'origine et l'exclut du prochain tirage, comme la sélection aléatoire d'un jeu de cartes.
Cette exclusion intelligente échange l'élément sélectionné avec l'élément actuel, puis sélectionne l'élément aléatoire suivant dans le reste, en boucle vers l'arrière pour une efficacité optimale, en garantissant que la sélection aléatoire est simplifiée (elle peut toujours commencer à 0), et ainsi sauter l'élément final.
Le temps d'exécution de l'algorithme est
O(n)
. Notez que le shuffle est fait sur place, donc si vous ne voulez pas modifier le tableau d'origine, faites-en d'abord une copie avec.slice(0)
.ÉDITER: Mise à jour vers ES6 / ECMAScript 2015
Le nouveau ES6 nous permet d'affecter deux variables à la fois. C'est particulièrement pratique lorsque nous voulons échanger les valeurs de deux variables, car nous pouvons le faire sur une seule ligne de code. Voici une forme plus courte de la même fonction, en utilisant cette fonction.
la source
return array
car JavaScript transmet les tableaux par référence lorsqu'ils sont utilisés comme arguments de fonction. Je suppose que c'est pour économiser de l'espace sur la pile, mais c'est une petite fonctionnalité intéressante. L'exécution du shuffle sur la baie va mélanger la baie d'origine.Math.random() should not be multiplied with the loop counter + 1, but with
array.lengt () `. Voir Générer des nombres entiers aléatoires en JavaScript dans une plage spécifique? pour une explication très complète.la source
On pourrait (ou devrait) l'utiliser comme un prototype de Array:
De ChristopheD:
la source
for...in
boucles pour parcourir les tableaux.Vous pouvez le faire facilement avec la carte et trier:
Vous pouvez mélanger les tableaux polymorphes et le tri est aussi aléatoire que Math.random, ce qui est assez bon pour la plupart des utilisations.
Comme les éléments sont triés en fonction de clés cohérentes qui ne sont pas régénérées à chaque itération et que chaque comparaison tire de la même distribution, tout caractère non aléatoire dans la distribution de Math.random est annulé.
La vitesse
La complexité temporelle est O (N log N), identique au tri rapide. La complexité de l'espace est O (N). Ce n'est pas aussi efficace qu'un shuffle Fischer Yates mais, à mon avis, le code est nettement plus court et plus fonctionnel. Si vous avez un grand tableau, vous devez certainement utiliser Fischer Yates. Si vous avez un petit tableau avec quelques centaines d'articles, vous pouvez le faire.
la source
Utilisez la bibliothèque underscore.js. La méthode
_.shuffle()
est bien pour ce cas. Voici un exemple avec la méthode:la source
shuffle
fonction.NOUVEAU!
Algorithme de shuffle Fisher-Yates plus court et probablement * plus rapide
taille du script (avec fy comme nom de fonction): 90 octets
DEMO http://jsfiddle.net/vvpoma8w/
* plus rapide probablement sur tous les navigateurs sauf Chrome.
Si vous avez des questions, vous avez juste à me les poser.
ÉDITER
oui c'est plus rapide
PERFORMANCE: http://jsperf.com/fyshuffle
en utilisant les fonctions les plus votées.
EDIT Il y avait un calcul en excès (pas besoin de --c + 1) et personne n'a remarqué
plus court (4 octets) et plus rapide (testez-le!).
La mise en cache ailleurs
var rnd=Math.random
, puis son utilisationrnd()
augmenteraient également légèrement les performances sur les grands tableaux.http://jsfiddle.net/vvpoma8w/2/
Version lisible (utilisez la version originale. C'est plus lent, les variables sont inutiles, comme les fermetures & ";", le code lui-même est également plus court ... peut-être lire ceci Comment "minimiser" le code Javascript , btw vous ne pouvez pas compresser le code suivant dans un minifieur javascript comme celui ci-dessus.)
la source
fy
etshuffle prototype
, j'obtiensfy
toujours le bas dans Chrome 37 sur OS X 10.9.5 (81% plus lent ~ 20k ops comparé à ~ 100k) et Safari 7.1 c'est jusqu'à ~ 8% plus lent. YMMV, mais ce n'est pas toujours plus rapide. jsperf.com/fyshuffle/3Modifier: cette réponse est incorrecte
Voir les commentaires et https://stackoverflow.com/a/18650169/28234 . Il est laissé ici pour référence car l'idée n'est pas rare.
Un moyen très simple pour les petits tableaux est simplement le suivant:
Ce n'est probablement pas très efficace, mais pour les petits tableaux, cela fonctionne très bien. Voici un exemple pour que vous puissiez voir à quel point il est aléatoire (ou non) et s'il correspond à votre cas d'utilisation ou non.
la source
Fiable, efficace, court
Certaines solutions de cette page ne sont pas fiables (elles ne randomisent que partiellement le tableau). D'autres solutions sont nettement moins efficaces. Avec
testShuffleArrayFun
(voir ci-dessous), nous pouvons tester les fonctions de réarrangement des baies pour la fiabilité et les performances. Les solutions suivantes sont: fiables, efficaces et courtes (utilisant la syntaxe ES6)[Des tests de comparaison ont été effectués à l'aide d'
testShuffleArrayFun
autres solutions, dans Google Chrome]Shuffle Array en place
ES6 pur, itératif
Test de fiabilité et de performance
Comme vous pouvez le voir sur cette page, des solutions incorrectes ont été proposées ici par le passé. J'ai écrit et j'ai utilisé la fonction suivante pour tester toutes les fonctions de randomisation de tableau pures (sans effets secondaires).
Autres solutions
D'autres solutions juste pour le plaisir.
ES6 pur, récursif
ES6 Pure à l'aide de array.map
ES6 Pure à l'aide de array.reduce
la source
[array[i], array[rand]]=[array[rand], array[i]]
? Vous pouvez peut-être expliquer comment cela fonctionne. Pourquoi choisissez-vous d'itérer vers le bas?Ajout à la réponse de @Laurens Holsts. Ceci est compressé à 50%.
la source
var b =
dans une boucle au lieu de déclarer b en boucle extérieure et de l'affecter avecb =
dans une boucle?Modifier: cette réponse est incorrecte
Voir https://stackoverflow.com/a/18650169/28234 . Il est laissé ici pour référence car l'idée n'est pas rare.
https://javascript.info/task/shuffle
la source
Avec ES2015, vous pouvez utiliser celui-ci:
Usage:
la source
n >>> 0
place de~~n
. Les indices matriciels peuvent être supérieurs à 2³¹-1.J'ai trouvé cette variante suspendue dans les réponses "supprimé par l'auteur" sur un double de cette question. Contrairement à certaines des autres réponses qui ont déjà beaucoup de votes positifs, c'est:
shuffled
nom plutôt queshuffle
)Voici un jsfiddle le montrant en cours d'utilisation .
la source
[1,2,3,4,5,6].sort(function() { return .5 - Math.random(); });
- il ne donne pas un tri aléatoire, et si vous l'utilisez, vous pouvez vous retrouver gêné: robweir.com/blog/2010/02/microsoft-random-browser-ballot.html.sort(function(a,b){ return a[0] - b[0]; })
si vous souhaitez que le tri compare les valeurs numériquement. Le.sort()
comparateur par défaut est lexicographique, ce qui signifie qu'il considérera10
qu'il est inférieur à2
puisqu'il1
est inférieur à2
.Math.random()
produit. (c'est-à-dire que l'ordre lexicographique est le même que l'ordre numérique lorsqu'il s'agit de nombres de 0 (inclus) à 1 (exclusif))la source
la source
Vous pouvez le faire facilement avec:
Veuillez vous référer aux tableaux de tri JavaScript
la source
Une solution récursive:
la source
Fisher-Yates shuffle en javascript. Je poste ceci ici parce que l'utilisation de deux fonctions utilitaires (swap et randInt) clarifie l'algorithme par rapport aux autres réponses ici.
la source
Tout d'abord, jetez un œil ici pour une grande comparaison visuelle des différentes méthodes de tri en javascript.
Deuxièmement, si vous jetez un coup d'œil au lien ci-dessus, vous constaterez que le
random order
tri semble fonctionner relativement bien par rapport aux autres méthodes, tout en étant extrêmement facile et rapide à mettre en œuvre comme indiqué ci-dessous:Edit : comme l'a souligné @gregers, la fonction de comparaison est appelée avec des valeurs plutôt qu'avec des indices, c'est pourquoi vous devez utiliser
indexOf
. Notez que cette modification rend le code moins adapté aux tableaux plus grands car ilindexOf
s'exécute en temps O (n).la source
Array.prototype.sort
passe en deux valeurs en tant quea
etb
, pas l'index. Donc, ce code ne fonctionne pas.une fonction de lecture aléatoire qui ne change pas le tableau source
Mise à jour : ici, je suggère un algorithme relativement simple (pas du point de vue de la complexité ) et court qui fera très bien l'affaire avec des tableaux de petite taille, mais cela va certainement coûter beaucoup plus cher que l' algorithme Durstenfeld classique lorsque vous traitez avec des tableaux énormes. Vous pouvez trouver le Durstenfeld dans l'une des meilleures réponses à cette question.
Réponse originale:
Si vous ne souhaitez pas que votre fonction de lecture aléatoire mute le tableau source , vous pouvez le copier dans une variable locale, puis faire le reste avec une simple logique de lecture aléatoire .
Shuffling logic : prenez un index aléatoire, puis ajoutez l'élément correspondant au tableau de résultats et supprimez-le de la copie du tableau source . Répétez cette action jusqu'à ce que le tableau source soit vide .
Et si vous voulez vraiment que ce soit court, voici jusqu'où je pourrais aller:
la source
splice
étant horriblement inefficace de faire ce qu'ils ont appelé "supprimer". Si vous ne voulez pas muter le tableau d'origine, copiez-le, puis mélangez cette copie en place en utilisant la variante Durstenfeld beaucoup plus efficace.splice
méthode pour créer une copie comme ceci:source = array.slice();
.Voici le PLUS FACILE ,
pour plus d'exemple, vous pouvez le vérifier ici
la source
encore une autre implémentation de Fisher-Yates, en utilisant le mode strict:
la source
Toutes les autres réponses sont basées sur Math.random () qui est rapide mais ne convient pas pour la randomisation au niveau cryptographique.
Le code ci-dessous utilise l'
Fisher-Yates
algorithme bien connu tout en l'utilisantWeb Cryptography API
pour le niveau cryptographique de randomisation .la source
Solution moderne en ligne courte utilisant les fonctionnalités ES6:
(à des fins éducatives)
la source
Une simple modification de la réponse de CoolAJ86 qui ne modifie pas le tableau d'origine:
la source
Bien qu'il existe un certain nombre d'implémentations déjà recommandées, mais je pense que nous pouvons le rendre plus court et plus facile à utiliser pour la boucle forEach, nous n'avons donc pas à nous soucier du calcul de la longueur du tableau et nous pouvons également éviter en toute sécurité d'utiliser une variable temporaire.
la source
Juste pour avoir un doigt dans la tarte. Ici, je présente une implémentation récursive du shuffle de Fisher Yates (je pense). Il donne un caractère aléatoire uniforme.
Remarque: L'
~~
opérateur (tilde double) se comporte en fait commeMath.floor()
pour les nombres réels positifs. C'est juste un raccourci.Edit: Le code ci-dessus est O (n ^ 2) en raison de l'emploi de
.splice()
mais nous pouvons éliminer l'épissure et le shuffle dans O (n) par le truc de swap.Le problème est que JS ne peut pas coopérer avec de grosses récursions. Dans ce cas particulier, la taille de votre tableau est limitée à 3000 ~ 7000, selon le moteur de votre navigateur et certains faits inconnus.
la source
Tableau aléatoire
la source
-1
for n comme vous<
ne l' avez pas utilisé<=
la
arrayShuffle
fonction la plus courtela source
D'un point de vue théorique, la façon la plus élégante de le faire, à mon humble avis, est d'obtenir un seul nombre aléatoire entre 0 et n! -1 et de calculer un mappage un à un de
{0, 1, …, n!-1}
toutes les permutations de(0, 1, 2, …, n-1)
. Tant que vous pouvez utiliser un générateur (pseudo-) aléatoire suffisamment fiable pour obtenir un tel nombre sans biais significatif, vous disposez de suffisamment d'informations pour obtenir ce que vous voulez sans avoir besoin de plusieurs autres nombres aléatoires.Lorsque vous calculez avec des nombres flottants double précision IEEE754, vous pouvez vous attendre à ce que votre générateur aléatoire fournisse environ 15 décimales. Étant donné que vous avez 15! = 1 307 674 368 000 (avec 13 chiffres), vous pouvez utiliser les fonctions suivantes avec des tableaux contenant jusqu'à 15 éléments et supposer qu'il n'y aura pas de biais significatif avec les tableaux contenant jusqu'à 14 éléments. Si vous travaillez sur un problème de taille fixe nécessitant de calculer plusieurs fois cette opération de lecture aléatoire, vous souhaiterez peut-être essayer le code suivant qui peut être plus rapide que les autres codes car il utilise
Math.random
qu'une seule fois (il implique cependant plusieurs opérations de copie).La fonction suivante ne sera pas utilisée, mais je la donne quand même; il retourne l'index d'une permutation donnée
(0, 1, 2, …, n-1)
selon le mappage un à un utilisé dans ce message (le plus naturel lors de l'énumération des permations); il est destiné à fonctionner avec jusqu'à 16 éléments:La réciproque de la fonction précédente (requise pour votre propre question) est ci-dessous; il est destiné à fonctionner avec jusqu'à 16 éléments; il renvoie la permutation d'ordre n de
(0, 1, 2, …, s-1)
:Maintenant, ce que vous voulez, c'est simplement:
Il devrait fonctionner jusqu'à 16 éléments avec un peu de biais théorique (bien que imperceptible d'un point de vue pratique); il peut être considéré comme pleinement utilisable pour 15 éléments; avec des tableaux contenant moins de 14 éléments, vous pouvez considérer en toute sécurité qu'il n'y aura absolument aucun biais.
la source