Hier ist eine optimierte Version des Code von Python von @Derek portierte, mit der zusätzlichen destruktiven (in-place) Option , die es den schnellste Algorithmus ermöglicht , wenn Sie mit ihm gehen. Andernfalls wird entweder eine vollständige Kopie erstellt oder für eine kleine Anzahl von Elementen, die von einem großen Array angefordert werden, auf einen auswahlbasierten Algorithmus umgeschaltet.
// Chooses k unique random elements from pool.
function sample(pool, k, destructive) {
var n = pool.length;
if (k < 0 || k > n)
throw new RangeError("Sample larger than population or is negative");
if (destructive || n <= (k <= 5 ? 21 : 21 + Math.pow(4, Math.ceil(Math.log(k*3, 4))))) {
if (!destructive)
pool = Array.prototype.slice.call(pool);
for (var i = 0; i < k; i++) { // invariant: non-selected at [i,n)
var j = i + Math.random() * (n - i) | 0;
var x = pool[i];
pool[i] = pool[j];
pool[j] = x;
}
pool.length = k; // truncate
return pool;
} else {
var selected = new Set();
while (selected.add(Math.random() * n | 0).size < k) {}
return Array.prototype.map.call(selected, i => population[i]);
}
}
Im Vergleich zu Dereks Implementierung ist der erste Algorithmus in Firefox viel schneller und in Chrome etwas langsamer, obwohl er jetzt die destruktive Option hat - die leistungsstärkste. Der zweite Algorithmus ist einfach 5-15% schneller. Ich versuche, keine konkreten Zahlen anzugeben, da diese je nach k und n variieren und mit den neuen Browserversionen in Zukunft wahrscheinlich nichts mehr bedeuten werden.
Die Heuristik, die die Wahl zwischen Algorithmen trifft, stammt aus Python-Code. Ich habe es so belassen, wie es ist, obwohl es manchmal das langsamere auswählt. Es sollte für JS optimiert werden, ist jedoch eine komplexe Aufgabe, da die Leistung von Eckfällen browser- und versionabhängig ist. Wenn Sie beispielsweise versuchen, 20 von 1000 oder 1050 auszuwählen, wird entsprechend zum ersten oder zweiten Algorithmus gewechselt. In diesem Fall läuft der erste 2x schneller als der zweite in Chrome 80, aber 3x langsamer in Firefox 74.