Ho un array come questo:
var arr1 = ["a", "b", "c", "d"];
Come posso randomizzarlo / mescolarlo?
Ho un array come questo:
var arr1 = ["a", "b", "c", "d"];
Come posso randomizzarlo / mescolarlo?
Risposte:
L'algoritmo shuffle di fatto imparziale è Shuffle Fisher-Yates (aka Knuth).
Vedi https://github.com/coolaj86/knuth-shuffle
Puoi vedere un'ottima visualizzazione qui (e il post originale collegato a questo )
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];
shuffle(arr);
console.log(arr);
Qualche informazione in più sull'algoritmo utilizzato.
i--
non --i
. Inoltre, il test if (i==0)...
è superfluo poiché se i == 0
il ciclo while non verrà mai inserito. La chiamata a Math.floor
può essere effettuata più velocemente usando ...| 0
. I tempi o tempj possono essere rimossi e il valore può essere assegnato direttamente a myArray [i] o j come appropriato.
0 !== currentIndex
).
Ecco un'implementazione JavaScript del Durstenfeld shuffle , una versione ottimizzata di Fisher-Yates:
/* Randomize array 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;
}
}
Prende un elemento casuale per ogni elemento originale dell'array e lo esclude dall'estrazione successiva, come scegliere casualmente da un mazzo di carte.
Questa intelligente esclusione scambia l'elemento selezionato con quello corrente, quindi seleziona l'elemento casuale successivo dal resto, eseguendo il ciclo indietro per un'efficienza ottimale, assicurando che il prelievo casuale sia semplificato (può sempre iniziare da 0) e quindi saltare l'elemento finale.
Il tempo di esecuzione dell'algoritmo è O(n)
. Si noti che lo shuffle viene eseguito sul posto, quindi se non si desidera modificare l'array originale, innanzitutto eseguirne una copia .slice(0)
.
Il nuovo ES6 ci consente di assegnare due variabili contemporaneamente. Ciò è particolarmente utile quando vogliamo scambiare i valori di due variabili, in quanto possiamo farlo in una riga di codice. Ecco una forma più breve della stessa funzione, usando questa funzione.
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]];
}
}
return array
poiché JavaScript passa array per riferimento quando utilizzato come argomento di funzione. Presumo che questo sia per risparmiare spazio sullo stack, ma è una piccola caratteristica interessante. L'esecuzione della riproduzione casuale sull'array consente di riprodurre in ordine casuale l'array originale.
Math.random() should not be multiplied with the loop counter + 1, but with
array.lengt () `. Vedi Generazione di numeri interi casuali in JavaScript in un intervallo specifico? per una spiegazione molto completa.
Avvertimento!
L'uso di questo algoritmo non è raccomandato , perché lo è inefficiente e fortemente distorto ; vedi commenti. Viene lasciato qui per riferimento futuro, perché l'idea non è così rara.
[1,2,3,4,5,6].sort(function() {
return .5 - Math.random();
});
Uno potrebbe (o dovrebbe) usarlo come protoipo da Array:
Da ChristopheD:
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;
}
for...in
loop per scorrere su array.
Puoi farlo facilmente con la mappa e ordinare:
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)
Puoi mescolare le matrici polimorfiche e l'ordinamento è casuale come Math.random, che è abbastanza buono per la maggior parte degli scopi.
Poiché gli elementi vengono ordinati in base a chiavi coerenti che non vengono rigenerate per ogni iterazione e ogni confronto viene tolto dalla stessa distribuzione, qualsiasi non casualità nella distribuzione di Math.random viene annullata.
Velocità
La complessità temporale è O (N log N), uguale all'ordinamento rapido. La complessità dello spazio è O (N). Questo non è efficiente come un shuffle Fischer Yates ma, a mio avviso, il codice è significativamente più breve e più funzionale. Se disponi di una vasta gamma, dovresti sicuramente usare Fischer Yates. Se hai un piccolo array con poche centinaia di elementi, potresti farlo.
Utilizzare la libreria underscore.js. Il metodo _.shuffle()
è carino per questo caso. Ecco un esempio con il metodo:
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();
shuffle
funzione.
NUOVO!
Algoritmo shuffle Fisher-Yates più corto e probabilmente * più veloce
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
}
dimensione dello script (con fy come nome della funzione): 90 byte
DEMO http://jsfiddle.net/vvpoma8w/
* probabilmente più veloce su tutti i browser tranne Chrome.
Se hai domande, chiedi pure.
MODIFICARE
si è più veloce
PRESTAZIONI: http://jsperf.com/fyshuffle
utilizzando le funzioni più votate.
EDIT C'è stato un calcolo in eccesso (non è necessario --c + 1) e nessuno se ne è accorto
più corto (4 byte) e più veloce (testalo!).
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
}
La memorizzazione nella cache da qualche altra parte var rnd=Math.random
e quindi l'utilizzo rnd()
aumenterebbe leggermente le prestazioni su array di grandi dimensioni.
http://jsfiddle.net/vvpoma8w/2/
Versione leggibile (usa la versione originale. Questa è più lenta, i var sono inutili, come le chiusure & ";", il codice stesso è anche più corto ... forse leggi questo Come 'minimizzare' il codice Javascript , ma non puoi comprimere il seguente codice in un minifigure javascript come quello sopra.)
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
}
}
fy
e shuffle prototype
, ottengo fy
costantemente il fondo in Chrome 37 su OS X 10.9.5 (81% più lento ~ 20k op rispetto a ~ 100k) e Safari 7.1 è fino a ~ 8% più lento. YMMV, ma non è sempre più veloce. jsperf.com/fyshuffle/3
Modifica: questa risposta non è corretta
Vedi commenti e https://stackoverflow.com/a/18650169/28234 . Viene lasciato qui come riferimento perché l'idea non è rara.
Un modo molto semplice per piccoli array è semplicemente questo:
const someArray = [1, 2, 3, 4, 5];
someArray.sort(() => Math.random() - 0.5);
Probabilmente non è molto efficiente, ma per piccoli array funziona bene. Ecco un esempio in modo da poter vedere quanto è casuale (o meno) e se si adatta al tuo caso d'uso o meno.
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>
Alcune soluzioni in questa pagina non sono affidabili (randomizzano solo parzialmente l'array). Altre soluzioni sono significativamente meno efficienti. Con testShuffleArrayFun
(vedi sotto) possiamo testare le funzioni di shuffle di array per affidabilità e prestazioni. Le seguenti soluzioni sono: affidabili, efficienti e brevi (usando la sintassi ES6)
[I test di confronto sono stati eseguiti utilizzando testShuffleArrayFun
contro altre soluzioni, in Google Chrome]
Shuffle Array Sul posto
function getShuffledArr (array){
for (var i = array.length - 1; i > 0; i--) {
var rand = Math.floor(Math.random() * (i + 1));
[array[i], array[rand]] = [array[rand], array[i]]
}
}
ES6 puro, iterativo
const getShuffledArr = arr => {
const newArr = arr.slice()
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
};
Come puoi vedere in questa pagina, ci sono state soluzioni errate offerte qui in passato. Ho scritto e usato la seguente funzione per testare qualsiasi funzione di randomizzazione di array pura (senza effetti collaterali).
function testShuffleArrayFun(getShuffledArrayFun){
const arr = [0,1,2,3,4,5,6,7,8,9]
var countArr = arr.map(el=>{
return arr.map(
el=> 0
)
}) // For each possible position in the shuffledArr and for
// each possible value, we'll create a counter.
const t0 = performance.now()
const n = 1000000
for (var i=0 ; i<n ; i++){
// We'll call getShuffledArrayFun n times.
// And for each iteration, we'll increment the counter.
var shuffledArr = getShuffledArrayFun(arr)
shuffledArr.forEach(
(value,key)=>{countArr[key][value]++}
)
}
const t1 = performance.now()
console.log(`Count Values in position`)
console.table(countArr)
const frequencyArr = countArr.map( positionArr => (
positionArr.map(
count => count/n
)
))
console.log("Frequency of value in position")
console.table(frequencyArr)
console.log(`total time: ${t1-t0}`)
}
Altre soluzioni solo per divertimento.
ES6 puro, ricorsivo
const getShuffledArr = arr => {
if (arr.length === 1) {return arr};
const rand = Math.floor(Math.random() * arr.length);
return [arr[rand], ...getShuffledArr(arr.filter((_, i) => i != rand))];
};
ES6 Pure utilizzando array.map
function getShuffledArr (arr){
return [...arr].map( (_, i, arrCopy) => {
var rand = i + ( Math.floor( Math.random() * (arrCopy.length - i) ) );
[arrCopy[rand], arrCopy[i]] = [arrCopy[i], arrCopy[rand]]
return arrCopy[i]
})
}
ES6 Pure utilizzando array.reduce
function getShuffledArr (arr){
return arr.reduce(
(newArr, _, i) => {
var rand = i + ( Math.floor( Math.random() * (newArr.length - i) ) );
[newArr[rand], newArr[i]] = [newArr[i], newArr[rand]]
return newArr
}, [...arr]
)
}
[array[i], array[rand]]=[array[rand], array[i]]
? Forse puoi delineare come funziona. Perché scegli di scorrere verso il basso?
Aggiungendo alla risposta @Laurens Holsts. Questo è compresso al 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
};
var b =
in un ciclo invece di dichiarare b ciclo esterno e assegnarlo con b =
in un ciclo?
Vedi https://stackoverflow.com/a/18650169/28234 . Viene lasciato qui come riferimento perché l'idea non è rara.
//one line solution
shuffle = (array) => array.sort(() => Math.random() - 0.5);
//Demo
let arr = [1, 2, 3];
shuffle(arr);
alert(arr);
https://javascript.info/task/shuffle
Math.random() - 0.5
è un numero casuale che può essere positivo o negativo, quindi la funzione di ordinamento riordina gli elementi in modo casuale.
Con ES2015 puoi usare questo:
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;
}
Uso:
[1, 2, 3, 4, 5, 6, 7].shuffle();
n >>> 0
invece di ~~n
. Gli indici di array possono essere superiori a 2³¹-1.
Ho trovato questa variante appesa nelle risposte "cancellate dall'autore" su un duplicato di questa domanda. A differenza di alcune delle altre risposte che hanno già molti voti, questo è:
shuffled
nome piuttosto che shuffle
)Ecco un jsfiddle che lo mostra in uso .
Array.prototype.shuffled = function() {
return this.map(function(n){ return [Math.random(), n] })
.sort().map(function(n){ return n[1] });
}
[1,2,3,4,5,6].sort(function() { return .5 - Math.random(); });
- non dà un ordinamento casuale, e se lo usi puoi finire in imbarazzo: robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
.sort(function(a,b){ return a[0] - b[0]; })
se si desidera che l'ordinamento confronti i valori numericamente. Il .sort()
comparatore predefinito è lessicografico, il che significa che considererà 10
meno di 2
poiché 1
è minore di 2
.
Math.random()
produce. (vale a dire, l'ordine lessicografico è lo stesso dell'ordine numerico quando si tratta di numeri da 0 (incluso) a 1 (esclusivo))
var shuffle = function(array) {
temp = [];
originalLength = array.length;
for (var i = 0; i < originalLength; i++) {
temp.push(array.splice(Math.floor(Math.random()*array.length),1));
}
return temp;
};
arr1.sort(() => Math.random() - 0.5);
Puoi farlo facilmente con:
// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);
Fare riferimento alle matrici di ordinamento JavaScript
Fisher-Yates in ordine casuale in javascript. Sto pubblicando questo qui perché l'uso di due funzioni di utilità (swap e randInt) chiarisce l'algoritmo rispetto alle altre risposte qui.
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);
}
}
Prima di tutto, dai un'occhiata qui per un ottimo confronto visivo di diversi metodi di ordinamento in JavaScript.
In secondo luogo, se dai una rapida occhiata al link sopra troverai che l' random order
ordinamento sembra funzionare relativamente bene rispetto agli altri metodi, pur essendo estremamente facile e veloce da implementare come mostrato di seguito:
function shuffle(array) {
var random = array.map(Math.random);
array.sort(function(a, b) {
return random[array.indexOf(a)] - random[array.indexOf(b)];
});
}
Modifica : come sottolineato da @gregers, la funzione di confronto viene chiamata con valori anziché indici, motivo per cui è necessario utilizzare indexOf
. Si noti che questa modifica rende il codice meno adatto per array più grandi in quanto indexOf
viene eseguito nel tempo O (n).
Array.prototype.sort
passa due valori come a
e b
non l'indice. Quindi questo codice non funziona.
Aggiornamento : Qui sto suggerendo un algoritmo relativamente semplice (non dal punto di vista della complessità ) e breve che andrà benissimo con array di piccole dimensioni, ma sicuramente costerà molto di più rispetto al classico algoritmo di Durstenfeld quando si affrontano array enormi. Puoi trovare Durstenfeld in una delle migliori risposte a questa domanda.
Risposta originale:
Se non si desidera che la funzione shuffle muti l' array di origine , è possibile copiarla in una variabile locale, quindi fare il resto con una semplice logica di shuffle .
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;
}
Logica casuale: raccogliere un indice casuale, quindi aggiungere l'elemento corrispondente all'array dei risultati ed eliminarlo dalla copia dell'array di origine . Ripetere questa azione fino a quando l'array di origine si svuota .
E se lo vuoi davvero breve, ecco quanto potrei arrivare:
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;
}
splice
essere un modo orribilmente inefficiente per fare ciò che chiamavano "colpire". Se non vuoi mutare l'array originale, copialo e poi mescola quella copia in posizione usando la variante di Durstenfeld molto più efficiente.
splice
metodo per creare una copia in questo modo: source = array.slice();
.
Ecco il più FACILE ,
function shuffle(array) {
return array.sort(() => Math.random() - 0.5);
}
per ulteriori esempi, puoi verificarlo qui
ancora un'altra implementazione di Fisher-Yates, usando la modalità rigorosa:
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;
}
Tutte le altre risposte sono basate su Math.random () che è veloce ma non adatto alla randomizzazione a livello crittografico.
Il codice seguente utilizza l' Fisher-Yates
algoritmo ben noto durante l'utilizzo Web Cryptography API
per il livello crittografico di randomizzazione .
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));
Una semplice modifica della risposta di CoolAJ86 che non modifica l'array originale:
/**
* Returns a new array whose contents are a shuffled copy of the original array.
* @param {Array} The 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 remains elements to shuffle...
while (currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// Swap it with the current element.
temporaryValue = newArray[currentIndex];
newArray[currentIndex] = newArray[randomIndex];
newArray[randomIndex] = temporaryValue;
}
return newArray;
};
Sebbene ci siano già un certo numero di implementazioni già consigliate, ma ritengo che possiamo renderlo più breve e più facile usando forEach loop, quindi non dobbiamo preoccuparci di calcolare la lunghezza dell'array e possiamo anche evitare di usare una variabile temporanea.
var myArr = ["a", "b", "c", "d"];
myArr.forEach((val, key) => {
randomIndex = Math.ceil(Math.random()*(key + 1));
myArr[key] = myArr[randomIndex];
myArr[randomIndex] = val;
});
// see the values
console.log('Shuffled Array: ', myArr)
Solo per avere un dito nella torta. Qui presento un'implementazione ricorsiva di Fisher Yates shuffle (credo). Dà casualità uniforme.
Nota: l' ~~
operatore (doppia tilde) si comporta in effetti come Math.floor()
per i numeri reali positivi. È solo una scorciatoia.
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])));
Modifica: il codice sopra è O (n ^ 2) a causa dell'impiego di .splice()
ma possiamo eliminare la giunzione e la riproduzione casuale in O (n) con il trucco di scambio.
var shuffle = (a, l = a.length, r = ~~(Math.random()*l)) => l ? ([a[r],a[l-1]] = [a[l-1],a[r]], shuffle(a, l-1))
: a;
var arr = Array.from({length:3000}, (_,i) => i);
console.time("shuffle");
shuffle(arr);
console.timeEnd("shuffle");
Il problema è che JS non può cooperare con grandi ricorsioni. In questo caso particolare, la dimensione dell'array è limitata, ad esempio 3000 ~ 7000, a seconda del motore del browser e di alcuni fatti sconosciuti.
Randomizza l'array
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;
-1
per n, come si è utilizzato <
non<=
la arrayShuffle
funzione più breve
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;
}
Da un punto di vista teorico, il modo più elegante di farlo, secondo la mia modesta opinione, è ottenere un singolo numero casuale compreso tra 0 e n! -1 e calcolare un mapping uno a uno da {0, 1, …, n!-1}
tutte le permutazioni di(0, 1, 2, …, n-1)
. Finché puoi usare un generatore (pseudo-) casuale abbastanza affidabile per ottenere un tale numero senza alcun pregiudizio significativo, hai abbastanza informazioni in esso per raggiungere quello che vuoi senza bisogno di molti altri numeri casuali.
Quando si esegue il calcolo con numeri mobili IEEE754 a precisione doppia, è possibile aspettarsi che il generatore casuale fornisca circa 15 decimali. Dato che hai 15! = 1.307.674.368.000 (con 13 cifre), puoi usare le seguenti funzioni con array che contengono fino a 15 elementi e supporre che non ci saranno errori di bias significativi con array che contengono fino a 14 elementi. Se lavori su un problema di dimensioni fisse che richiede di calcolare molte volte questa operazione shuffle, potresti provare il seguente codice che potrebbe essere più veloce di altri codici poiché utilizzaMath.random
solo una volta (tuttavia comporta diverse operazioni di copia).
La seguente funzione non verrà utilizzata, ma la do comunque; restituisce l'indice di una data permutazione (0, 1, 2, …, n-1)
secondo la mappatura one to one utilizzata in questo messaggio (la più naturale quando si enumerano le permuazioni); è progettato per funzionare con un massimo di 16 elementi:
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);
}
Il reciproco della funzione precedente (richiesto per la propria domanda) è inferiore; è progettato per funzionare con un massimo di 16 elementi; restituisce la permutazione dell'ordine n di (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;
}
Ora, ciò che vuoi semplicemente è:
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]; });
}
Dovrebbe funzionare per un massimo di 16 elementi con un po 'di pregiudizio teorico (sebbene impercettibile dal punto di vista pratico); può essere visto come completamente utilizzabile per 15 elementi; con array che contengono meno di 14 elementi, puoi tranquillamente considerare che non ci sarà alcun bias.