Voici une version optimisée de la code porté depuis Python par @Derek, avec l'option destructive (in-place) qui en fait l'algorithme le plus rapide possible si vous pouvez l'accepter. Sinon, il effectue une copie complète ou, pour un petit nombre d'éléments demandés dans un grand tableau, passe à un algorithme basé sur la sélection.
// 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) / Math.log(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 => pool[i]);
}
}
Par rapport à l'implémentation de Derek, le premier algorithme est beaucoup plus rapide dans Firefox et un peu plus lent dans Chrome, bien qu'il dispose maintenant de l'option destructive - la plus performante. Le second algorithme est simplement 5-15% plus rapide. J'essaie de ne pas donner de chiffres concrets car ils varient en fonction de k et n et ne signifieront probablement rien à l'avenir avec les nouvelles versions des navigateurs.
L'heuristique qui permet de choisir entre les algorithmes provient du code Python. Je l'ai laissé tel quel, bien qu'il sélectionne parfois l'algorithme le plus lent. Il devrait être optimisé pour JS, mais c'est une tâche complexe car la performance des cas particuliers dépend du navigateur et de sa version. Par exemple, lorsque vous essayez de sélectionner 20 parmi 1000 ou 1050, le système passe au premier ou au second algorithme en conséquence. Dans ce cas, le premier algorithme est deux fois plus rapide que le second dans Chrome 80, mais trois fois plus lent dans Firefox 74.