190 votes

Comment obtenir un nombre d'éléments aléatoires à partir d'un tableau ?

Je travaille sur "comment accéder à des éléments de manière aléatoire à partir d'un tableau en javascript". J'ai trouvé de nombreux liens à ce sujet. Par exemple : Obtenir un élément aléatoire d'un tableau JavaScript

var item = items[Math.floor(Math.random()*items.length)];

Mais dans ce cas, nous ne pouvons choisir qu'un seul élément du tableau. Si nous voulons plus d'un élément, comment pouvons-nous y parvenir ? Comment obtenir plus d'un élément d'un tableau ?

1voto

Storage Lenovo Points 35

J'avais besoin d'une fonction pour résoudre ce genre de problème, je la partage donc ici.

    const getRandomItem = function(arr) {
        return arr[Math.floor(Math.random() * arr.length)];
    }

    // original array
    let arr = [4, 3, 1, 6, 9, 8, 5];

    // number of random elements to get from arr
    let n = 4;

    let count = 0;
    // new array to push random item in
    let randomItems = []
    do {
        let item = getRandomItem(arr);
        randomItems.push(item);
        // update the original array and remove the recently pushed item
        arr.splice(arr.indexOf(item), 1);
        count++;
    } while(count < n);

    console.log(randomItems);
    console.log(arr);

Remarque : si n = arr.length il s'agit en fait de mélanger le tableau arr y randomItems renvoie ce tableau mélangé.

Demo

1voto

user Points 1438

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.

1voto

Duloren Points 1024

Échantillonnage avec d'éventuels doublons :

const sample_with_duplicates = Array(sample_size).fill().map(() => items[~~(Math.random() * items.length)])

Échantillonnage sans doublons :

const sample_without_duplicates = [...Array(items.length).keys()].sort(() => 0.5 - Math.random()).slice(0, sample_size).map(index => items[index]);

Depuis sans doublons nécessite de trier d'abord l'ensemble du tableau d'index, elle est considérablement plus lente que la méthode avec d'éventuels doublons pour les grands items les tableaux d'entrée.

Il est évident que la taille maximale de sans doublons est <= items.length

Regardez ce violon : https://jsfiddle.net/doleron/5zw2vequ/30/

0voto

Il extrait des éléments aléatoires de srcArray un par un jusqu'à ce qu'il y en ait assez ou qu'il ne reste plus d'éléments à extraire dans srcArray. Rapide et fiable.

function getNRandomValuesFromArray(srcArr, n) {
    // making copy to do not affect original srcArray
    srcArr = srcArr.slice();
    resultArr = [];
    // while srcArray isn't empty AND we didn't enough random elements
    while (srcArr.length && resultArr.length < n) {
        // remove one element from random position and add this element to the result array
        resultArr = resultArr.concat( // merge arrays
            srcArr.splice( // extract one random element
                Math.floor(Math.random() * srcArr.length),
                1
            )
        );
    }

    return resultArr;
}

0voto

evilReiko Points 2048

2019

C'est la même chose que Laurynas Mališauskas Il suffit que les éléments soient uniques (pas de doublons).

var getMeRandomElements = function(sourceArray, neededElements) {
    var result = [];
    for (var i = 0; i < neededElements; i++) {
    var index = Math.floor(Math.random() * sourceArray.length);
        result.push(sourceArray[index]);
        sourceArray.splice(index, 1);
    }
    return result;
}

Pour répondre à la question initiale " Comment obtenir plusieurs éléments aléatoires par jQuery ", voici :

var getMeRandomElements = function(sourceArray, neededElements) {
    var result = [];
    for (var i = 0; i < neededElements; i++) {
    var index = Math.floor(Math.random() * sourceArray.length);
        result.push(sourceArray[index]);
        sourceArray.splice(index, 1);
    }
    return result;
}

var $set = $('.someClass');// <<<<< change this please

var allIndexes = [];
for(var i = 0; i < $set.length; ++i) {
    allIndexes.push(i);
}

var totalRandom = 4;// <<<<< change this please
var randomIndexes = getMeRandomElements(allIndexes, totalRandom);

var $randomElements = null;
for(var i = 0; i < randomIndexes.length; ++i) {
    var randomIndex = randomIndexes[i];
    if($randomElements === null) {
        $randomElements = $set.eq(randomIndex);
    } else {
        $randomElements.add($set.eq(randomIndex));
    }
}

// $randomElements is ready
$randomElements.css('backgroundColor', 'red');

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X