Comment randomiser (mélanger) un tableau JavaScript?
j'ai un tableau comme celui-ci:
var arr1 = ["a", "b", "c", "d"];
Comment puis-je le randomiser / le mélanger?
30 réponses
l'algorithme de shuffle impartial de facto est le shuffle de Fisher-Yates (alias Knuth).
voir https://github.com/coolaj86/knuth-shuffle
vous pouvez voir un grande visualisation ici (et le post original lié à ce )
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
// Used like so
var arr = [2, 11, 37, 42];
arr = shuffle(arr);
console.log(arr);
Quelques infos à propos de l'algorithme .
Voici une implémentation JavaScript du Durstenfeld shuffle , une version optimisée par ordinateur de Fisher-Yates:
/**
* Randomize array element order in-place.
* Using Durstenfeld shuffle algorithm.
*/
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
l'algorithme de Fisher-Yates fonctionne en sélectionnant un élément aléatoire pour chaque élément du tableau original, puis en l'excluant du tirage suivant. Tout comme choisissant au hasard à partir d'un jeu de cartes.
cette exclusion est faite d'une manière intelligente (inventé par Durstenfeld pour une utilisation par les ordinateurs) par changer l'élément choisi avec l'élément courant, puis choisir l'élément aléatoire suivant dans le reste. Pour une efficacité optimale, la boucle tourne à l'envers de sorte que le choix aléatoire est simplifié (il peut toujours commencer à 0), et il saute le dernier élément parce qu'il n'y a plus d'autres choix.
Le temps d'exécution de cet algorithme est O(n). Notez que le mélange se fait sur place. Donc, si vous ne voulez pas modifier le tableau d'origine, en faire une copie d'abord avec .slice(0)
.
Updating to ES6 / ECMAScript 2015
le nouveau ES6 nous permet d'assigner deux variables à la fois. C'est particulièrement pratique lorsque nous voulons échanger les valeurs de deux variables, car nous pouvons le faire en une seule ligne de code. Voici une forme plus courte de la même fonction, en utilisant cette fonctionnalité.
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]]; // eslint-disable-line no-param-reassign
}
}
[communauté edit: Cette réponse est incorrecte; voir les commentaires. Il est parti d'ici pour référence future, parce que l'idée n'est pas rare.]
[1,2,3,4,5,6].sort(function() {
return .5 - Math.random();
});
on pourrait (ou devrait) l'utiliser comme un protoype à partir D'un tableau:
De Christophe:
Array.prototype.shuffle = function() {
var i = this.length, j, temp;
if ( i == 0 ) return this;
while ( --i ) {
j = Math.floor( Math.random() * ( i + 1 ) );
temp = this[i];
this[i] = this[j];
this[j] = temp;
}
return this;
}
utilisez le trait de soulignement.bibliothèque js. La méthode _.shuffle()
est bonne pour ce cas.
Voici un exemple avec la méthode:
var _ = require("underscore");
var arr = [1,2,3,4,5,6];
// Testing _.shuffle
var testShuffle = function () {
var indexOne = 0;
var stObj = {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5
};
for (var i = 0; i < 1000; i++) {
arr = _.shuffle(arr);
indexOne = _.indexOf(arr, 1);
stObj[indexOne] ++;
}
console.log(stObj);
};
testShuffle();
nouveau!
plus Court et probablement *plus rapide de Fisher-Yates algorithme de shuffle
- il utilise tout---
- bit-à-bit à l'étage (les nombres jusqu'à 10 chiffres décimaux (32 bits))
- closures inutiles et autres matières enlevées
function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
c=a.length;while(c)b=Math.random()*(--c+1)|0,d=a[c],a[c]=a[b],a[b]=d
}
taille de script (avec l'af comme le nom de la fonction): 90bytes
DEMO http://jsfiddle.net/vvpoma8w /
*plus rapide probablement sur tous les navigateurs sauf chrome.
si vous avez des questions, posez-les.
MODIFIER
oui, c'est plus rapide
PERFORMANCE: http://jsperf.com/fyshuffle
utilisant les fonctions les plus votées.
EDIT Il y avait un calcul en excès (n'a pas besoin --c+1) et personne n'a remarqué
plus courte(4 octets) et plus rapide(à tester!).
function fy(a,b,c,d){//array,placeholder,placeholder,placeholder
c=a.length;while(c)b=Math.random()*c--|0,d=a[c],a[c]=a[b],a[b]=d
}
Caching somewhere else var rnd=Math.random
et ensuite utiliser rnd()
augmenterait aussi légèrement la performance sur les grands tableaux.
http://jsfiddle.net/vvpoma8w/2 /
version lisible (utiliser la version originale. c'est plus lent, vars sont inutiles, comme les fermetures & ";", le code lui-même est également plus courte ... peut-être lire ce comment "minifier" le code Javascript , btw vous n'êtes pas en mesure de compresser le code suivant dans un JavaScript minifiers comme celui ci-dessus.)
function fisherYates( array ){
var count = array.length,
randomnumber,
temp;
while( count ){
randomnumber = Math.random() * count-- | 0;
temp = array[count];
array[count] = array[randomnumber];
array[randomnumber] = temp
}
}
vous pouvez le faire facilement avec la carte et le tri:
let unshuffled = ['hello', 'a', 't', 'q', 1, 2, 3, {cats: true}]
let shuffled = unshuffled
.map((a) => ({sort: Math.random(), value: a}))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value)
- nous mettons chaque élément dans le tableau dans un objet, et lui donnons une clé de tri aléatoire
- nous trions en utilisant la clé aléatoire
- Nous détacher pour obtenir les objets d'origine
vous pouvez mélanger des tableaux polymorphiques, et le tri est aussi aléatoire que les mathématiques.aléatoire, ce qui est suffisant pour la plupart des buts.
depuis le les éléments sont triés par rapport à des clés cohérentes qui ne sont pas régénérées à chaque itération, et chaque comparaison tire de la même distribution, toute non-aléatoire dans la distribution des mathématiques.aléatoire est annulée.
une façon très simple pour les petits tableaux est tout simplement ceci:
const someArray = [1, 2, 3, 4, 5];
someArray.sort(() => Math.random() - 0.5);
Ce n'est probablement pas très efficace, mais pour de petits tableaux cela fonctionne bien. Voici un exemple pour que vous puissiez voir comment aléatoire (ou non) il est, et si elle correspond à votre usecase ou non.
const resultsEl = document.querySelector('#results');
const buttonEl = document.querySelector('#trigger');
const generateArrayAndRandomize = () => {
const someArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
someArray.sort(() => Math.random() - 0.5);
return someArray;
};
const renderResultsToDom = (results, el) => {
el.innerHTML = results.join(' ');
};
buttonEl.addEventListener('click', () => renderResultsToDom(generateArrayAndRandomize(), resultsEl));
<h1>Randomize!</h1>
<button id="trigger">Generate</button>
<p id="results">0 1 2 3 4 5 6 7 8 9</p>
ajoute à la réponse de @Laurens Holsts. C'est comprimé à 50%.
function shuffleArray(d) {
for (var c = d.length - 1; c > 0; c--) {
var b = Math.floor(Math.random() * (c + 1));
var a = d[c];
d[c] = d[b];
d[b] = a;
}
return d
};
avec ES2015 vous pouvez utiliser celui-ci:
Array.prototype.shuffle = function() {
let m = this.length, i;
while (m) {
i = (Math.random() * m--) >>> 0;
[this[m], this[i]] = [this[i], this[m]]
}
return this;
}
Utilisation:
[1, 2, 3, 4, 5, 6, 7].shuffle();
var shuffle = function(array) {
temp = [];
for (var i = 0; i < array.length ; i++) {
temp.push(array.splice(Math.floor(Math.random()*array.length),1));
}
return temp;
};
j'ai trouvé cette variante accrochée dans les réponses" supprimé par l'auteur " sur une copie de cette question. À la différence de certaines autres réponses qui ont déjà de nombreux upvotes, c'est:
- aléatoire
- non en place (d'où le nom
shuffled
plutôt queshuffle
) - pas déjà présent ici avec plusieurs variantes
voici un jsfiddle qui le montre en cours d'utilisation .
Array.prototype.shuffled = function() {
return this.map(function(n){ return [Math.random(), n] })
.sort().map(function(n){ return n[1] });
}
Une solution récursive:
function shuffle(a,b){
return a.length==0?b:function(c){
return shuffle(a,(b||[]).concat(c));
}(a.splice(Math.floor(Math.random()*a.length),1));
};
avec ES6, 2018
certaines des réponses pourraient être abrégées avec le dernier ES6.
Shuffle réseau En place
function shuffleArray (array){
for (let i = array.length - 1; i > 0; i--) {
const rand = Math.floor(Math.random() * (i + 1));
[array[i], array[rand]] = [array[rand], array[i]];
}
}
avec ES6 nous pouvons attribuer deux valeurs à la fois. Ceci est particulièrement pratique dans la ligne 4 ci-dessus, où deux variables sont échangées dans une ligne de code.
laisser le tableau d'origine intact et retourner un tableau mélangé
Si vous voulez faire un plus pur fonction, et laisser le tableau original intact, vous pouvez simplement dupliquer le tableau et ensuite exécuter le même algorithme.
function getShuffledArray (arr){
let newArr = [...arr]
for (let i = newArr.length - 1; i > 0; i--) {
const rand = Math.floor(Math.random() * (i + 1));
[newArr[i], newArr[rand]]=[newArr[rand], newArr[i]];
}
return newArr;
}
un algorithme Ascendant
l'algorithme ci-dessous utilise une boucle ascendante. Il est moins intuitif, mais court et valide.
function getShuffledArrayAsc (arr){
let newArr = [...arr];
for (let i = 1; i < newArr.length ; i++) {
const rand = Math.floor( Math.random() * (i + 1) );
[newArr[i], newArr[rand]] = [newArr[rand], newArr[i]];
}
return newArr;
}
test de fiabilité de la fonction de randomisation
les fonctions ci-dessus ont présenté une distribution uniforme lors du passage à la 'testShuffledArrayFun' ci-dessous, à la fois dans Chrome, et dans Node. Ceci est en accord avec ce que nous attendons d'une fonction de randomisation.
function testShuffledArrayFun(getShuffledArrayFun){
// Tests the reliability of the suffleArrayFunction, by callying it 1,000 times and presenting the distribution.
const testArr = [0,1,2,3,4];
const countArr = testArr.map( position => // for for each possible position in the shuffledArr, for each possible value, we'll create a counter. the counter of value 0 in position 0 will be countArr[0][0]
testArr.map( value => 0)
)
const n = 10000;
for (var i=0 ; i<n ; i++){
// We'll call getShuffledArrayFun for n times. For each shuffledArray we receive we'll increment the counterArr accordingly. At the end we'll print the distribution.
var shuffledArr = getShuffledArrayFun(testArr);
shuffledArr.forEach(
(value, key) => {countArr[key][value]++}
);
}
countArr.forEach(
(valueCountArr,key) => {
console.log(`Position ${key}:`);
valueCountArr.forEach(
(count,originalValue) => {
console.log(`The Value ${originalValue} appeared ${count*100/n}% `);
}
);
}
);
}
de Fisher-Yates shuffle en javascript. Je poste ici parce que l'utilisation de deux fonctions utilitaires (swap et randInt) précise l'algorithme par rapport aux autres réponses ici.
function swap(arr, i, j) {
// swaps two elements of an array in place
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function randInt(max) {
// returns random integer between 0 and max-1 inclusive.
return Math.floor(Math.random()*max);
}
function shuffle(arr) {
// For each slot in the array (starting at the end),
// pick an element randomly from the unplaced elements and
// place it in the slot, exchanging places with the
// element in the slot.
for(var slot = arr.length - 1; slot > 0; slot--){
var element = randInt(slot+1);
swap(arr, element, slot);
}
}
tout d'abord, avoir un look ici pour une grande comparaison visuelle des différentes méthodes de tri en javascript.
deuxièmement, si vous jetez un coup d'oeil sur le lien ci-dessus, vous constaterez que le tri random order
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:
function shuffle(array) {
var random = array.map(Math.random);
array.sort(function(a, b) {
return random[array.indexOf(a)] - random[array.indexOf(b)];
});
}
Edit : comme le souligne @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 ce changement rend le code moins approprié pour les tableaux plus grands comme indexOf
exécute dans le temps O(n).
encore une autre mise en œuvre de Fisher-Yates, en utilisant le mode strict:
function shuffleArray(a) {
"use strict";
var i, t, j;
for (i = a.length - 1; i > 0; i -= 1) {
t = a[i];
j = Math.floor(Math.random() * (i + 1));
a[i] = a[j];
a[j] = t;
}
return a;
}
Vous pouvez le faire facilement avec:
// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);
s'il vous Plaît référence à JavaScript Tri des Tableaux
une fonction de lecture aléatoire qui ne change pas le tableau source
Update : ici je suggère un relativement simple (pas de complexité perspective) et court algorithme qui fera très bien avec des tableaux de petite taille, mais il va certainement coûter beaucoup plus que le classique Durstenfeld algorithme quand vous traitez avec d'énormes tableaux. Vous vous pouvez trouver le Durstenfeld dans l'une des principales réponses à cette question.
réponse originale:
si vous ne souhaitez pas votre fonction shuffle pour muter le tableau source , vous pouvez le copier dans une variable locale, puis faire le reste avec un simple logique shuffling .
function shuffle(array) {
var result = [], source = array.concat([]);
while (source.length) {
let index = Math.floor(Math.random() * source.length);
result.push(source[index]);
source.splice(index, 1);
}
return result;
}
Brassage logique : récupérez un index aléatoire, puis ajoutez l'élément correspondant au result array et supprimez-le de la source array copy . Répétez cette action jusqu'à ce que le tableau source obtienne vide .
et si vous le voulez vraiment court, voici jusqu'où je pourrais aller:
function shuffle(array) {
var result = [], source = array.concat([]);
while (source.length) {
let index = Math.floor(Math.random() * source.length);
result.push(source.splice(index, 1)[0]);
}
return result;
}
Toutes les autres réponses sont basées sur les Mathématiques.random() qui est rapide mais ne convient pas pour la randomisation cryptographique de niveau.
le code ci-dessous utilise l'algorithme bien connu Fisher-Yates
tout en utilisant Web Cryptography API
pour niveau cryptographique de randomisation .
var d = [1,2,3,4,5,6,7,8,9,10];
function shuffle(a) {
var x, t, r = new Uint32Array(1);
for (var i = 0, c = a.length - 1, m = a.length; i < c; i++, m--) {
crypto.getRandomValues(r);
x = Math.floor(r / 65536 / 65536 * m) + i;
t = a [i], a [i] = a [x], a [x] = t;
}
return a;
}
console.log(shuffle(d));
Juste pour avoir un doigt dans la tarte. Je présente ici une implémentation récursive de Fisher Yates shuffle (je pense). Ça donne un caractère aléatoire uniforme.
Note: le ~~
(opérateur double tilde) se comporte en fait comme Math.floor()
pour des nombres réels positifs. Juste une courte distance de coupe, c'est.
var shuffle = a => a.length ? a.splice(~~(Math.random()*a.length),1).concat(shuffle(a))
: a;
console.log(JSON.stringify(shuffle([0,1,2,3,4,5,6,7,8,9])));
bien qu'il y ait un certain nombre d'implémentations déjà conseillées mais je pense que nous pouvons le rendre plus court et plus facile en utilisant la boucle forEach, donc nous n'avons pas besoin de nous soucier de calculer la longueur du tableau et aussi nous pouvons éviter en toute sécurité d'utiliser une variable temporaire.
var arr1 = ["a", "b", "c", "d"];
arr1.forEach((val, key) => {
randomIndex = Math.ceil(Math.random()*(key + 1));
arr1[key] = arr1[randomIndex];
arr1[randomIndex] = val;
});
la plus courte arrayShuffle
fonction
function arrayShuffle(o) {
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
}
d'un point de vue théorique, la façon la plus élégante de le faire, à mon humble avis, est d'obtenir un simple nombre aléatoire entre 0 et n!-1 et de calculer un à un mappage de {0, 1, …, n!-1}
à toutes les permutations de (0, 1, 2, …, n-1)
. Tant que vous pouvez utiliser une (pseudo-)aléatoire générateur suffisamment fiable pour obtenir un tel numéro, sans aucun biais important, vous avez suffisamment d'informations pour atteindre ce que vous voulez sans avoir besoin de plusieurs autres nombres aléatoires.
lorsque vous calculez avec IEEE754 des nombres flottants de double précision, vous pouvez vous attendre à ce que votre générateur aléatoire fournisse environ 15 décimales. Depuis 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 en supposant qu'il n'y aura pas de biais significatif avec des 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 mélange, vous pouvez essayer le code suivant qui peut être plus rapide que d'autres codes car il utilise Math.random
seulement une fois (il implique plusieurs opérations de copie cependant).
La fonction suivante ne sera pas utilisé, mais je donne quand même; il renvoie l'index d'une permutation de (0, 1, 2, …, n-1)
, selon l'association utilisées dans ce message (le plus naturel quand énumérer les permutations); il est prévu de travailler avec un maximum de 16 éléments:
function permIndex(p) {
var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000];
var tail = [];
var i;
if (p.length == 0) return 0;
for(i=1;i<(p.length);i++) {
if (p[i] > p[0]) tail.push(p[i]-1);
else tail.push(p[i]);
}
return p[0] * fact[p.length-1] + permIndex(tail);
}
la réciproque de la fonction précédente (requise pour votre propre question) Est ci-dessous; elle est destinée à fonctionner avec un maximum de 16 éléments; elle renvoie la permutation de l'ordre n de (0, 1, 2, …, s-1)
:
function permNth(n, s) {
var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000];
var i, j;
var p = [];
var q = [];
for(i=0;i<s;i++) p.push(i);
for(i=s-1; i>=0; i--) {
j = Math.floor(n / fact[i]);
n -= j*fact[i];
q.push(p[j]);
for(;j<i;j++) p[j]=p[j+1];
}
return q;
}
maintenant, ce que vous voulez simplement est:
function shuffle(p) {
var fact = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000];
return permNth(Math.floor(Math.random()*fact[p.length]), p.length).map(
function(i) { return p[i]; });
}
il devrait fonctionner pour un maximum de 16 éléments avec un peu biais théorique (bien que non perceptible 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 sans risque qu'il n'y aura absolument aucun biais.
Moderne court inline solution à l'aide de ES6 caractéristiques:
['a','b','c','d'].map(x => [Math.random(), x]).sort(([a], [b]) => a - b).map(([_, x]) => x);
(à des fins éducatives)
Une simple modification de CoolAJ86 réponse que de ne pas modifier le tableau d'origine
/**
* Returns a new array whose contents are a copy shuffled of the array.
* @param {Array} a items to shuffle.
* https://stackoverflow.com/a/2450976/1673761
* https://stackoverflow.com/a/44071316/1673761
*/
const shuffle = (array) => {
let currentIndex = array.length;
let temporaryValue;
let randomIndex;
const newArray = array.slice();
// While there remain elements to shuffle...
while (currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = newArray[currentIndex];
newArray[currentIndex] = newArray[randomIndex];
newArray[randomIndex] = temporaryValue;
}
return newArray;
};
Array.prototype.shuffle=function(){
var len = this.length,temp,i
while(len){
i=Math.random()*len-- |0;
temp=this[len],this[len]=this[i],this[i]=temp;
}
return this;
}
Randomize tableau à l'aide du tableau.splice ()
function shuffleArray(array) {
var temp = [];
var len=array.length;
while(len){
temp.push(array.splice(Math.floor(Math.random()*array.length),1)[0]);
len--;
}
return temp;
}
//console.log("Here >>> "+shuffleArray([4,2,3,5,8,1,0]));
Randomize tableau
var arr = ['apple','cat','Adam','123','Zorro','petunia'];
var n = arr.length; var tempArr = [];
for ( var i = 0; i < n-1; i++ ) {
// The following line removes one random element from arr
// and pushes it onto tempArr
tempArr.push(arr.splice(Math.floor(Math.random()*arr.length),1)[0]);
}
// Push the remaining item onto tempArr
tempArr.push(arr[0]);
arr=tempArr;
var shuffledArray = function(inpArr){
//inpArr - is input array
var arrRand = []; //this will give shuffled array
var arrTempInd = []; // to store shuffled indexes
var max = inpArr.length;
var min = 0;
var tempInd;
var i = 0;
do{
//generate random index between range
tempInd = Math.floor(Math.random() * (max - min));
//check if index is already available in array to avoid repetition
if(arrTempInd.indexOf(tempInd)<0){
//push character at random index
arrRand[i] = inpArr[tempInd];
//push random indexes
arrTempInd.push(tempInd);
i++;
}
}
// check if random array length is equal to input array length
while(arrTempInd.length < max){
return arrRand; // this will return shuffled Array
}
};
il suffit de passer le tableau pour fonctionner et en retour obtenir le tableau mélangé