我正在研究“如何从 javascript 中的数组随机访问元素”。我找到了很多关于这个的链接。喜欢: 从 JavaScript 数组中获取随机项
var item = items[Math.floor(Math.random()*items.length)];
但在这里,我们只能从数组中选择一项。如果我们想要多个元素,那么我们如何实现这一目标?我们如何从数组中获取多个元素?
我正在研究“如何从 javascript 中的数组随机访问元素”。我找到了很多关于这个的链接。喜欢: 从 JavaScript 数组中获取随机项
var item = items[Math.floor(Math.random()*items.length)];
但在这里,我们只能从数组中选择一项。如果我们想要多个元素,那么我们如何实现这一目标?我们如何从数组中获取多个元素?
只有两行:
// 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);
试试这个非破坏性(且快速)的功能:
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;
}
这里有一个单线独特的解决方案
array.sort(() => Math.random() - Math.random()).slice(0, n)
.sample
从 Python 标准库移植:
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) / Math.log(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 | 0;
while(selected.has(j)){
j = Math.random() * n | 0;
}
selected.add(j);
result[i] = population[j];
}
}
return result;
}
从Lib/random.py移植的实现。
笔记:
setsize
基于 Python 中的特性进行设置以提高效率。尽管没有针对 JavaScript 进行调整,但算法仍会按预期运行。Array.prototype.sort
. 然而,该算法保证在有限时间内终止。Set
实现的旧浏览器,可以将集合替换为Array
并.has(j)
替换为.indexOf(j) > -1
。针对已接受答案的表现:
lodash _.sample
和_.sampleSize
.
从集合到集合大小的唯一键处获取一个或 n 个随机元素。
_.sample([1, 2, 3, 4]);
// => 2
_.sampleSize([1, 2, 3], 2);
// => [3, 1]
_.sampleSize([1, 2, 3], 3);
// => [2, 3, 1]