Ich habe ein Array wie dieses:
var arr1 = ["a", "b", "c", "d"];
Wie kann ich es randomisieren / mischen?
Ich habe ein Array wie dieses:
var arr1 = ["a", "b", "c", "d"];
Wie kann ich es randomisieren / mischen?
Antworten:
Der de facto unvoreingenommene Shuffle-Algorithmus ist der Fisher-Yates-Shuffle (auch bekannt als Knuth).
Siehe https://github.com/coolaj86/knuth-shuffle
Sie können hier eine großartige Visualisierung sehen (und den damit verknüpften Originalbeitrag )
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);
Weitere Informationen zum verwendeten Algorithmus .
i--
nicht sein --i
. Außerdem ist der Test if (i==0)...
überflüssig, da i == 0
die while- Schleife niemals betreten wird. Der Anruf an Math.floor
kann mit schneller erfolgen ...| 0
. Entweder Tempi oder Tempj können entfernt und der Wert je nach Bedarf direkt myArray [i] oder j zugewiesen werden.
0 !== currentIndex
).
Hier ist eine JavaScript-Implementierung des Durstenfeld-Shuffle , einer optimierten Version von 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;
}
}
Es wählt ein zufälliges Element für jedes ursprüngliche Array-Element aus und schließt es von der nächsten Ziehung aus, wie es zufällig aus einem Kartenspiel ausgewählt wird.
Dieser clevere Ausschluss tauscht das ausgewählte Element gegen das aktuelle aus, wählt dann das nächste zufällige Element aus dem Rest aus, schleift für eine optimale Effizienz rückwärts, stellt sicher, dass die zufällige Auswahl vereinfacht wird (sie kann immer bei 0 beginnen), und überspringt dabei das endgültige Element.
Algorithmus Laufzeit ist O(n)
. Beachten Sie, dass das Mischen direkt durchgeführt wird. Wenn Sie also das ursprüngliche Array nicht ändern möchten, erstellen Sie zuerst eine Kopie davon mit .slice(0)
.
Mit dem neuen ES6 können zwei Variablen gleichzeitig zugewiesen werden. Dies ist besonders praktisch, wenn wir die Werte zweier Variablen austauschen möchten, da dies in einer Codezeile möglich ist. Hier ist eine kürzere Form derselben Funktion, die diese Funktion verwendet.
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
da JavaScript Arrays als Referenz übergibt, wenn es als Funktionsargumente verwendet wird. Ich gehe davon aus, dass dies Platz spart, aber es ist eine interessante kleine Funktion. Durch das Mischen auf dem Array wird das ursprüngliche Array gemischt.
Math.random() should not be multiplied with the loop counter + 1, but with
array.lengt () `. Siehe Generieren von zufälligen ganzen Zahlen in JavaScript in einem bestimmten Bereich? für eine sehr umfassende Erklärung.
Warnung!
Die Verwendung dieses Algorithmus wird nicht empfohlen , da er ineffizient und stark voreingenommen ist . Zeige Kommentare. Es wird hier zum späteren Nachschlagen belassen, weil die Idee nicht so selten ist.
[1,2,3,4,5,6].sort(function() {
return .5 - Math.random();
});
Man könnte (oder sollte) es als Prototyp von Array verwenden:
Von 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
Schleifen, um über Arrays zu iterieren.
Sie können es einfach mit Karte und Sortierung tun:
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)
Sie können polymorphe Arrays mischen, und die Sortierung ist so zufällig wie bei Math.random, was für die meisten Zwecke gut genug ist.
Da die Elemente nach konsistenten Schlüsseln sortiert sind, die nicht bei jeder Iteration neu generiert werden, und jeder Vergleich aus derselben Verteilung stammt, wird jede Nicht-Zufälligkeit in der Verteilung von Math.random aufgehoben.
Geschwindigkeit
Die zeitliche Komplexität ist O (N log N), genau wie beim schnellen Sortieren. Die Raumkomplexität ist O (N). Dies ist nicht so effizient wie ein Fischer Yates-Shuffle, aber meiner Meinung nach ist der Code deutlich kürzer und funktionaler. Wenn Sie ein großes Array haben, sollten Sie auf jeden Fall Fischer Yates verwenden. Wenn Sie ein kleines Array mit einigen hundert Elementen haben, können Sie dies tun.
Verwenden Sie die Bibliothek underscore.js. Die Methode _.shuffle()
ist für diesen Fall gut. Hier ist ein Beispiel mit der Methode:
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
Funktion zu erhalten.
NEU!
Kürzerer und wahrscheinlich * schnellerer Fisher-Yates-Shuffle-Algorithmus
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
}
Skriptgröße (mit fy als Funktionsname): 90 Byte
DEMO http://jsfiddle.net/vvpoma8w/
* schneller wahrscheinlich auf allen Browsern außer Chrome.
Wenn Sie Fragen haben, fragen Sie einfach.
BEARBEITEN
ja es ist schneller
LEISTUNG: http://jsperf.com/fyshuffle
Verwenden der am besten bewerteten Funktionen.
BEARBEITEN Es gab eine Berechnung im Übermaß (brauche nicht --c + 1) und niemand bemerkte es
kürzer (4 Bytes) und schneller (testen!).
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
}
Das Zwischenspeichern an einem anderen Ort var rnd=Math.random
und die anschließende Verwendung rnd()
würden die Leistung auf großen Arrays ebenfalls geringfügig erhöhen.
http://jsfiddle.net/vvpoma8w/2/
Lesbare Version (verwenden Sie die Originalversion. Dies ist langsamer, vars sind nutzlos, wie die Verschlüsse & ";", der Code selbst ist auch kürzer ... vielleicht lesen Sie dies Wie man Javascript-Code 'minimiert' , übrigens können Sie nicht Komprimieren Sie den folgenden Code in einem Javascript-Minifier wie dem obigen.)
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
und tausche shuffle prototype
, bin ich fy
in Chrome 37 unter OS X 10.9.5 (81% langsamer ~ 20.000 Operationen im Vergleich zu ~ 100.000) und Safari 7.1 bis zu ~ 8% langsamer. YMMV, aber es ist nicht immer schneller. jsperf.com/fyshuffle/3
Bearbeiten: Diese Antwort ist falsch
Siehe Kommentare und https://stackoverflow.com/a/18650169/28234 . Es wird hier als Referenz gelassen, weil die Idee nicht selten ist.
Ein sehr einfacher Weg für kleine Arrays ist einfach folgender:
const someArray = [1, 2, 3, 4, 5];
someArray.sort(() => Math.random() - 0.5);
Es ist wahrscheinlich nicht sehr effizient, aber für kleine Arrays funktioniert dies einwandfrei. Hier ist ein Beispiel, damit Sie sehen können, wie zufällig (oder nicht) es ist und ob es zu Ihrem Anwendungsfall passt oder nicht.
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>
Einige Lösungen auf dieser Seite sind nicht zuverlässig (sie ordnen das Array nur teilweise zufällig an). Andere Lösungen sind deutlich weniger effizient. Mit testShuffleArrayFun
(siehe unten) können wir Array-Shuffling-Funktionen auf Zuverlässigkeit und Leistung testen. Die folgenden Lösungen sind: zuverlässig, effizient und kurz (unter Verwendung der ES6-Syntax)
[Vergleichstests wurden mit testShuffleArrayFun
anderen Lösungen in Google Chrome durchgeführt]
Shuffle Array vorhanden
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 Rein, iterativ
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
};
Wie Sie auf dieser Seite sehen können, wurden hier in der Vergangenheit falsche Lösungen angeboten. Ich habe die folgende Funktion geschrieben und verwendet, um alle reinen (keine Nebenwirkungen) Array-Randomisierungsfunktionen zu testen.
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}`)
}
Andere Lösungen nur zum Spaß.
ES6 Rein, rekursiv
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 using 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 using 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]]
? Vielleicht können Sie skizzieren, wie das funktioniert. Warum iterieren Sie nach unten?
Hinzufügen zu @Laurens Holsts Antwort. Dies ist zu 50% komprimiert.
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 einer Schleife zu arbeiten, anstatt b außerhalb der Schleife zu deklarieren und sie b =
in einer Schleife zuzuweisen?
Siehe https://stackoverflow.com/a/18650169/28234 . Es wird hier als Referenz gelassen, weil die Idee nicht selten ist.
//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
ist eine Zufallszahl, die positiv oder negativ sein kann, sodass die Sortierfunktion Elemente zufällig neu anordnet.
Mit ES2015 können Sie Folgendes verwenden:
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;
}
Verwendungszweck:
[1, 2, 3, 4, 5, 6, 7].shuffle();
n >>> 0
anstelle von verwenden ~~n
. Array-Indizes können höher als 2³¹-1 sein.
Ich fand diese Variante in den Antworten "vom Autor gelöscht" auf einem Duplikat dieser Frage. Im Gegensatz zu einigen anderen Antworten, die bereits viele positive Stimmen haben, lautet dies:
shuffled
eher der Name als shuffle
)Hier ist eine jsfiddle, die zeigt, wie sie verwendet wird .
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(); });
- Es gibt keine zufällige Sortierung, und wenn Sie es verwenden, können Sie sich schämen
.sort(function(a,b){ return a[0] - b[0]; })
wenn die Sortierung Werte numerisch vergleichen soll. Der Standardkomparator .sort()
ist lexikografisch, was bedeutet 10
, dass er kleiner als ist, 2
da er 1
kleiner als ist 2
.
Math.random()
erzeugt wird, keine Rolle . (Das heißt, die lexikografische Reihenfolge entspricht der numerischen Reihenfolge beim Umgang mit Zahlen von 0 (einschließlich) bis 1 (exklusiv))
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);
Sie können es leicht machen mit:
// array
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// random
fruits.sort(function(a, b){return 0.5 - Math.random()});
// out
console.log(fruits);
Weitere Informationen finden Sie unter JavaScript Sorting Arrays
Eine rekursive Lösung:
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));
};
Fisher-Yates mischen in Javascript. Ich poste dies hier, weil die Verwendung von zwei Dienstprogrammfunktionen (swap und randInt) den Algorithmus im Vergleich zu den anderen Antworten hier verdeutlicht.
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);
}
}
Schauen Sie sich hier zunächst einen großartigen visuellen Vergleich verschiedener Sortiermethoden in Javascript an.
Zweitens, wenn Sie sich den obigen Link genauer ansehen, werden Sie feststellen, dass die random order
Sortierung im Vergleich zu den anderen Methoden relativ gut zu funktionieren scheint, während sie extrem einfach und schnell zu implementieren ist, wie unten gezeigt:
function shuffle(array) {
var random = array.map(Math.random);
array.sort(function(a, b) {
return random[array.indexOf(a)] - random[array.indexOf(b)];
});
}
Bearbeiten : Wie von @gregers hervorgehoben, wird die Vergleichsfunktion eher mit Werten als mit Indizes aufgerufen, weshalb Sie sie verwenden müssen indexOf
. Beachten Sie, dass diese Änderung den Code für größere Arrays weniger geeignet macht, da er indexOf
in O (n) -Zeit ausgeführt wird.
Array.prototype.sort
Übergibt zwei Werte als a
und b
, nicht den Index. Dieser Code funktioniert also nicht.
Update : Hier schlage ich einen relativ einfachen (nicht aus Komplexitätssicht ) und kurzen Algorithmus vor, der mit kleinen Arrays gut funktioniert, aber definitiv viel mehr kostet als der klassische Durstenfeld- Algorithmus, wenn Sie mit großen Arrays arbeiten. Sie finden das Durstenfeld in einer der Top-Antworten auf diese Frage.
Ursprüngliche Antwort:
Wenn Sie nicht möchten, dass Ihre Shuffle-Funktion das Quell-Array mutiert , können Sie es in eine lokale Variable kopieren und den Rest mit einer einfachen Shuffling-Logik erledigen .
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;
}
Mischlogik : Nehmen Sie einen zufälligen Index auf, fügen Sie das entsprechende Element zum Ergebnisarray hinzu und löschen Sie es aus der Kopie des Quellarrays . Wiederholen Sie diese Aktion, bis das Quellarray leer wird .
Und wenn Sie es wirklich kurz haben wollen, hier ist, wie weit ich kommen könnte:
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
eine schrecklich ineffiziente Methode sind, um das zu tun, was sie als "Streik" bezeichneten. Wenn Sie das ursprüngliche Array nicht mutieren möchten, kopieren Sie es einfach und mischen Sie diese Kopie mit der viel effizienteren Durstenfeld-Variante.
splice
Methode auch verwenden , um eine Kopie wie folgt zu erstellen : source = array.slice();
.
Hier ist die EINFACHSTE ,
function shuffle(array) {
array.sort(() => Math.random() - 0.5);
}
Zum Beispiel können Sie es hier überprüfen
Noch eine weitere Implementierung von Fisher-Yates im strengen Modus:
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;
}
Alle anderen Antworten basieren auf Math.random (), das schnell ist, aber nicht für die Randomisierung auf kryptografischer Ebene geeignet ist.
Der folgende Code verwendet den bekannten Fisher-Yates
Algorithmus, während er Web Cryptography API
für die kryptografische Ebene der Randomisierung verwendet wird .
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));
Eine einfache Änderung der Antwort von CoolAJ86, bei der das ursprüngliche Array nicht geändert wird :
/**
* 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;
};
Es gibt zwar bereits eine Reihe von Implementierungen, aber ich bin der Meinung, dass wir die Verwendung der forEach-Schleife kürzer und einfacher gestalten können, sodass wir uns nicht um die Berechnung der Array-Länge kümmern müssen und die Verwendung einer temporären Variablen sicher vermeiden können.
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)
Nur um einen Finger in der Torte zu haben. Hier präsentiere ich eine rekursive Implementierung von Fisher Yates Shuffle (glaube ich). Es gibt eine einheitliche Zufälligkeit.
Hinweis: Der ~~
(Doppel-Tilde-Operator) verhält sich tatsächlich wie Math.floor()
bei positiven reellen Zahlen. Nur eine Abkürzung ist es.
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])));
Bearbeiten: Der obige Code ist O (n ^ 2) aufgrund der Verwendung von, .splice()
aber wir können Spleißen und Mischen in O (n) durch den Swap-Trick eliminieren.
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");
Das Problem ist, dass JS nicht mit großen Rekursionen weitermachen kann. In diesem speziellen Fall ist Ihre Array-Größe abhängig von Ihrer Browser-Engine und einigen unbekannten Fakten auf etwa 3000 bis 7000 begrenzt.
Array zufällig sortieren
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
for n geben, wie Sie es <
nicht <=
die kürzeste arrayShuffle
Funktion
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;
}
Aus theoretischer Sicht besteht meiner bescheidenen Meinung nach die eleganteste Methode darin, eine einzelne Zufallszahl zwischen 0 und n! -1 zu erhalten und eine Eins-zu-Eins-Zuordnung von {0, 1, …, n!-1}
zu allen Permutationen von zu berechnen(0, 1, 2, …, n-1)
. Solange Sie einen (Pseudo-) Zufallsgenerator verwenden können, der zuverlässig genug ist, um eine solche Zahl ohne signifikante Verzerrung zu erhalten, verfügen Sie über genügend Informationen, um das zu erreichen, was Sie möchten, ohne mehrere andere Zufallszahlen zu benötigen.
Wenn Sie mit IEEE754-Gleitkommazahlen mit doppelter Genauigkeit rechnen, können Sie davon ausgehen, dass Ihr Zufallsgenerator etwa 15 Dezimalstellen liefert. Da Sie 15! = 1.307.674.368.000 (mit 13 Ziffern) haben, können Sie die folgenden Funktionen für Arrays mit bis zu 15 Elementen verwenden und davon ausgehen, dass bei Arrays mit bis zu 14 Elementen keine signifikante Verzerrung vorliegt. Wenn Sie an einem Problem mit fester Größe arbeiten, bei dem diese Zufallsoperation mehrmals berechnet werden muss, sollten Sie den folgenden Code ausprobieren, der möglicherweise schneller als andere Codes ist, da er verwendet wirdMath.random
nur einmal verwendet wird (es sind jedoch mehrere Kopiervorgänge erforderlich).
Die folgende Funktion wird nicht verwendet, aber ich gebe sie trotzdem; es gibt den Index einer gegebenen Permutation (0, 1, 2, …, n-1)
gemäß der in dieser Nachricht verwendeten Eins-zu-Eins-Zuordnung zurück (der natürlichste bei der Aufzählung von Permuationen); Es soll mit bis zu 16 Elementen arbeiten:
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);
}
Der Kehrwert der vorherigen Funktion (erforderlich für Ihre eigene Frage) ist unten; Es ist beabsichtigt, mit bis zu 16 Elementen zu arbeiten. es gibt die Permutation der Ordnung n von zurück (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;
}
Was Sie jetzt nur wollen, ist:
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]; });
}
Es sollte für bis zu 16 Elemente mit einer kleinen theoretischen Verzerrung funktionieren (obwohl dies aus praktischer Sicht nicht wahrnehmbar ist). es kann als voll verwendbar für 15 Elemente angesehen werden; Bei Arrays mit weniger als 14 Elementen können Sie davon ausgehen, dass absolut keine Verzerrung vorliegt.