考虑到性能,从阵列中获取随机子集的最佳方法是什么?
假设我们得到一个包含90000个项目的数组,我想从中获得10000个随机项目。
我想到的一种方法是获取从0到array.length
的随机索引,然后使用Array.prototype.splice
从原始数组中删除所选的索引。然后从其余部分获取下一个随机项目。
但是splice
方法将重新排列我们刚刚选择的项目之后的所有项目的索引,并在步骤中向前移动它们。它不会影响性能吗?
项目可能重复,但我们选择的不应该。假设我们选择了索引0,那么我们应该只查看其余的1~89999。
答案 0 :(得分:3)
如果你想要一个洗牌数组的子集,你不需要洗牌整个数组。当你抽出10000件物品时,你可以停止经典的渔民洗牌,而其他80000指数则保持不变。
答案 1 :(得分:1)
我首先将整个数组随机化然后拼接10000个项目。
How to randomize (shuffle) a JavaScript array? 解释了在javascript中随机化数组的好方法
答案 2 :(得分:1)
油藏采样算法可以做到这一点。
这是尝试从TAOCP第2卷第3.4.2节实施Knuth的“算法S”:
function sample(source, size) {
var chosen = 0,
srcLen = source.length,
result = new Array(size);
for (var seen = 0; chosen < size; seen++) {
var remainingInput = srcLen - seen,
remainingOutput = size - chosen;
if (remainingInput*Math.random() < remainingOutput) {
result[chosen++] = source[seen];
}
}
return result;
}
基本上,它会对输入数组进行一次传递,根据随机数的函数,输入中剩余的项目数以及输出中剩余的项目数来选择或跳过项目。
这段代码有三个潜在的问题:1。我可能已经搞砸了,2。Knuth要求一个“在零和一之间”的随机数,我不确定这是否意味着[0,1)间隔JavaScript提供或完全关闭或完全打开的间隔,3。它容易受到PRNG偏见。
性能特征应该非常好。这是O(srcLen)。大部分时间我们在完成整个输入之前完成。按顺序访问输入,如果您在具有缓存的计算机上运行代码,这是一件好事。我们甚至不会浪费任何时间阅读或编写最终不会在输出中结束的元素。
此版本不会修改输入数组。可以编写一个就地版本,这可能会节省一些内存,但它可能不会快得多。