如何从数组中随机获取n个元素

时间:2013-10-09 10:31:36

标签: javascript jquery html arrays

我正在研究'如何在javascript中从数组中随机访问元素'。我找到了很多相关的链接。喜欢: Get random item from JavaScript array

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

问题:但是在这里我们只能从数组中选择一个项目。如果我们想要多个元素,那么我们怎样才能实现这一点。所以请从这个声明中我们怎样才能获得更多比阵列中的一个元素。

23 个答案:

答案 0 :(得分:89)

只有两行:

// Shuffle array
const shuffled = array.sort(() => 0.5 - Math.random());

// Get sub-array of first n elements after shuffled
let selected = shuffled.slice(0, n);

DEMO

答案 1 :(得分:48)

尝试这种非破坏性(和fast)功能:

function getRandom(arr, n) {
    var result = new Array(n),
        len = arr.length,
        taken = new Array(len);
    if (n > len)
        throw new RangeError("getRandom: more elements taken than available");
    while (n--) {
        var x = Math.floor(Math.random() * len);
        result[n] = arr[x in taken ? taken[x] : x];
        taken[x] = --len in taken ? taken[len] : len;
    }
    return result;
}

答案 2 :(得分:10)

创建一个功能:

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

您还应该检查sourceArray是否有足够的元素要返回。如果要返回唯一元素,则应从sourceArray中删除所选元素。

答案 3 :(得分:7)

Getting 5 random items without changing the original array:

const n = 5;
const sample = items
  .map(x => ({ x, r: Math.random() }))
  .sort((a, b) => a.r - b.r)
  .map(a => a.x)
  .slice(0, n);

(Don't use this for big lists)

答案 4 :(得分:5)

lodash(https://lodash.com/_.sample_.sampleSize

从集合到集合大小的唯一键获取一个或n个随机元素。

_.sample([1, 2, 3, 4]);
// => 2

_.sampleSize([1, 2, 3], 2);
// => [3, 1]
 
_.sampleSize([1, 2, 3], 3);
// => [2, 3, 1]

答案 5 :(得分:5)

从Python标准库移植.sample

function sample(population, k){
    /*
        Chooses k unique random elements from a population sequence or set.

        Returns a new list containing elements from the population while
        leaving the original population unchanged.  The resulting list is
        in selection order so that all sub-slices will also be valid random
        samples.  This allows raffle winners (the sample) to be partitioned
        into grand prize and second place winners (the subslices).

        Members of the population need not be hashable or unique.  If the
        population contains repeats, then each occurrence is a possible
        selection in the sample.

        To choose a sample in a range of integers, use range as an argument.
        This is especially fast and space efficient for sampling from a
        large population:   sample(range(10000000), 60)

        Sampling without replacement entails tracking either potential
        selections (the pool) in a list or previous selections in a set.

        When the number of selections is small compared to the
        population, then tracking selections is efficient, requiring
        only a small set and an occasional reselection.  For
        a larger number of selections, the pool tracking method is
        preferred since the list takes less space than the
        set and it doesn't suffer from frequent reselections.
    */

    if(!Array.isArray(population))
        throw new TypeError("Population must be an array.");
    var n = population.length;
    if(k < 0 || k > n)
        throw new RangeError("Sample larger than population or is negative");

    var result = new Array(k);
    var setsize = 21;   // size of a small set minus size of an empty list

    if(k > 5)
        setsize += Math.pow(4, Math.ceil(Math.log(k * 3, 4)))

    if(n <= setsize){
        // An n-length list is smaller than a k-length set
        var pool = population.slice();
        for(var i = 0; i < k; i++){          // invariant:  non-selected at [0,n-i)
            var j = Math.random() * (n - i) | 0;
            result[i] = pool[j];
            pool[j] = pool[n - i - 1];       // move non-selected item into vacancy
        }
    }else{
        var selected = new Set();
        for(var i = 0; i < k; i++){
            var j = Math.random() * (n - i) | 0;
            while(selected.has(j)){
                j = Math.random() * (n - i) | 0;
            }
            selected.add(j);
            result[i] = population[j];
        }
    }

    return result;
}

移植自Lib/random.py

注意:

  • setsize基于Python中的特性进行设置以提高效率。虽然它尚未针对JavaScript进行调整,但算法仍将按预期运行。
  • 由于滥用Array.prototype.sort,根据ECMAScript规范,本页描述的其他一些答案并不安全。但是,该算法可以保证在有限的时间内终止。
  • 对于未实施Set的旧版浏览器,可以将该集替换为Array,将.has(j)替换为.indexOf(j) > -1

针对接受的答案的表现:

答案 6 :(得分:5)

这里有一种单线独特的解决方案

 array.sort(() => Math.random() - Math.random()).slice(0, n)

答案 7 :(得分:3)

ES6语法

const pickRandom = (arr,count) => {
  let _arr = [...arr];
  return[...Array(count)].map( ()=> _arr.splice(Math.floor(Math.random() * _arr.length), 1)[0] ); 
}

答案 8 :(得分:2)

2020
非破坏性的函数式编程风格,在不可变的上下文中工作。

const _randomslice = (ar, size) => {
  let new_ar = [...ar];
  new_ar.splice(Math.floor(Math.random()*ar.length),1);
  return ar.length <= (size+1) ? new_ar : _randomslice(new_ar, size);
}


console.log(_randomslice([1,2,3,4,5],2));

答案 9 :(得分:2)

如果您想在循环中随机获取数组中的项而不重复,则需要使用splice从数组中删除所选项:

var items = [1, 2, 3, 4, 5];
var newItems = [];

for(var i = 0; i < 3; i++) {
    var idx = Math.floor(Math.random() * items.length);
    newItems.push(items[idx]);
    items.splice(idx, 1);
}

Example fiddle

答案 10 :(得分:2)

Array.prototype.getnkill = function() {
    var a = Math.floor(Math.random()*this.length);
    var dead = this[a];
    this.splice(a,1);
    return dead;
}

//.getnkill() removes element in the array 
//so if you like you can keep a copy of the array first:

//var original= items.slice(0); 


var item = items.getnkill();

var anotheritem = items.getnkill();

答案 11 :(得分:1)

我需要一个函数来解决这类问题,所以我在这里分享它。

    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);

注意:如果n = arr.length,则基本上是在对数组arr进行改组,而randomItems将返回经过改组的数组。

Demo

答案 12 :(得分:1)

这是@Derek从Python移植的code的优化版本,并添加了破坏性(就地)选项,使它成为可能的最快算法。否则,它要么进行完整复制,要么针对大数组中请求的少量项目切换到基于选择的算法。

// 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 => population[i]);
    }
}

与Derek的实现相比,第一种算法在Firefox中速度更快,而在Chrome中则稍慢,尽管现在它具有破坏性的选项-性能最高。第二种算法仅提高了5-15%。我尝试不给出任何具体数字,因为它们取决于k和n,并且在新的浏览器版本中将来可能毫无意义。

在算法之间进行选择的启发式方法源自Python代码。我将其保留为原样,尽管有时会选择较慢的一个。应该针对JS对其进行优化,但这是一项复杂的任务,因为特殊情况的性能取决于浏览器及其版本。例如,当您尝试从1000或1050中选择20时,它将相应地切换到第一个或第二个算法。在这种情况下,第一个运行速度比Chrome 80中的第二个运行速度快2倍,但在Firefox 74中运行的速度慢3倍。

答案 13 :(得分:1)

编辑:如果您只想获得少量元素,此解决方案比此处介绍的其他解决方案(拼接源数组)慢。此解决方案的速度仅取决于原始数组中元素的数量,而拼接解决方案的速度取决于输出数组中所需的元素数。

如果你想要非重复的随机元素,你可以随机播放你的数组,然后只获得你想要的数量:

function shuffle(array) {
    var counter = array.length, temp, index;

    // While there are elements in the array
    while (counter--) {
        // Pick a random index
        index = (Math.random() * counter) | 0;

        // And swap the last element with it
        temp = array[counter];
        array[counter] = array[index];
        array[index] = temp;
    }

    return array;
}

var arr = [0,1,2,3,4,5,7,8,9];

var randoms = shuffle(arr.slice(0)); // array is cloned so it won't be destroyed
randoms.length = 4; // get 4 random elements

DEMO:http://jsbin.com/UHUHuqi/1/edit

随机播放功能:https://stackoverflow.com/a/6274398/1669279

答案 14 :(得分:0)

这是一个很好键入的版本。它不会失败。如果样本大小大于原始数组的长度,则返回经过改组的数组。

function sampleArr<T>(arr: T[], size: number): T[] {
  const setOfIndexes = new Set<number>();
  while (setOfIndexes.size < size && setOfIndexes.size < arr.length) {
    setOfIndexes.add(randomIntFromInterval(0, arr.length - 1));
  }
  return Array.from(setOfIndexes.values()).map(i => arr[i]);
}

const randomIntFromInterval = (min: number, max: number): number =>
  Math.floor(Math.random() * (max - min + 1) + min);

答案 15 :(得分:0)

这是我使用的一项功能,可让您轻松地对有或没有替换的阵列进行采样:

  // Returns a random sample (either with or without replacement) from an array
  const randomSample = (arr, k, withReplacement = false) => {
    let sample;
    if (withReplacement === true) {  // sample with replacement
      sample = Array.from({length: k}, () => arr[Math.floor(Math.random() *  arr.length)]);
    } else { // sample without replacement
      if (k > arr.length) {
        throw new RangeError('Sample size must be less than or equal to array length         when sampling without replacement.')
      }
      sample = arr.map(a => [a, Math.random()]).sort((a, b) => {
        return a[1] < b[1] ? -1 : 1;}).slice(0, k).map(a => a[0]); 
      };
    return sample;
  };

使用很简单:

不替换(默认行为)

randomSample([1, 2, 3], 2)可能返回[2, 1]

可替换

randomSample([1, 2, 3, 4, 5, 6], 4)可能返回[2, 3, 3, 2]

答案 16 :(得分:0)

var getRandomElements = function(sourceArray, requiredLength) {
    var result = [];
    while(result.length<requiredLength){
        random = Math.floor(Math.random()*sourceArray.length);
        if(result.indexOf(sourceArray[random])==-1){
            result.push(sourceArray[random]);
        }
    }
    return result;
}

答案 17 :(得分:0)

2019

这与 LaurynasMališauskas答案相同,只是元素是唯一的(没有重复)。

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;
}

现在要回答原始问题“如何通过jQuery获取多个随机元素”,就可以了:

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');

答案 18 :(得分:0)

在这个答案中,我想与您分享测试,我必须知道最好的方法,该方法可以使所有元素具有随机子数组的机会均等。

方法01

array.sort(() => Math.random() - Math.random()).slice(0, n)

使用此方法,某些元素与其他元素相比具有更高的机会。

calculateProbability = function(number=0 ,iterations=10000,arraySize=100) { 
let occ = 0 
for (let index = 0; index < iterations; index++) {
   const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize]
   
  /** Wrong Method */
    const arr = myArray.sort(function() {
     return val= .5 - Math.random();
      });
     
  if(arr[0]===number) {
    occ ++
    }

    
}

console.log("Probability of ",number, " = ",occ*100 /iterations,"%")

}

calculateProbability(0)
calculateProbability(0)
calculateProbability(0)
calculateProbability(50)
calculateProbability(50)
calculateProbability(50)
calculateProbability(25)
calculateProbability(25)
calculateProbability(25)

方法2

使用此方法,元素具有相同的概率:

 const arr = myArray
      .map((a) => ({sort: Math.random(), value: a}))
      .sort((a, b) => a.sort - b.sort)
      .map((a) => a.value)

calculateProbability = function(number=0 ,iterations=10000,arraySize=100) { 
let occ = 0 
for (let index = 0; index < iterations; index++) {
   const myArray= Array.from(Array(arraySize).keys()) //=> [0, 1, 2, 3, 4, ... arraySize]
   

  /** Correct Method */
  const arr = myArray
  .map((a) => ({sort: Math.random(), value: a}))
  .sort((a, b) => a.sort - b.sort)
  .map((a) => a.value)
    
  if(arr[0]===number) {
    occ ++
    }

    
}

console.log("Probability of ",number, " = ",occ*100 /iterations,"%")

}

calculateProbability(0)
calculateProbability(0)
calculateProbability(0)
calculateProbability(50)
calculateProbability(50)
calculateProbability(50)
calculateProbability(25)
calculateProbability(25)
calculateProbability(25)

正确的答案发布在以下链接中:https://stackoverflow.com/a/46545530/3811640

答案 19 :(得分:0)

items.sort(()=>(Math.random()> 0.5?1:-1))。slice(0,count);

答案 20 :(得分:0)

我简直直截了当,简直是干净利落。

const getRnd = (a, n) => new Array(n).fill(null).map(() => a[Math.floor(Math.random() * a.length)]);

答案 21 :(得分:0)

它会在srcArray足够或没有足够的元素要提取的情况下,从srcArray中一一提取随机元素。 快速可靠。

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;
}

答案 22 :(得分:-1)

这是最正确的答案,它会为您提供随机+唯一元素。

function randomize(array, n)
{
    var final = [];
    array = array.filter(function(elem, index, self) {
        return index == self.indexOf(elem);
    }).sort(function() { return 0.5 - Math.random() });

    var len = array.length,
    n = n > len ? len : n;

    for(var i = 0; i < n; i ++)
    {
        final[i] = array[i];
    }

    return final;
}

// randomize([1,2,3,4,5,3,2], 4);
// Result: [1, 2, 3, 5] // Something like this