【发布时间】:2018-10-08 10:52:29
【问题描述】:
我有一个大小为 n 的输入流,我想生成一个大小为 k 的输出流,其中包含输入流的不同随机元素,而不需要为样本选择的元素提供任何额外的内存。
我打算使用的算法基本如下:
for each element in input stream
if random()<k/n
decrement k
output element
if k = 0
halt
end if
end if
decrement n
end for
函数 random() 在随机分布上从 [0..1) 生成一个数字,我相信该算法的操作原理很简单。
虽然该算法可以在选择最后一个元素时提前终止,但一般情况下该算法仍约为 O(n)。起初它似乎按预期工作(从输入流中输出大致均匀分布但仍然是随机的元素),但我认为当 k 远小于 n 时,可能会有一种不均匀的倾向来选择后面的元素。但是,我不确定这一点......所以我很高兴知道一种或另一种方式。我也想知道是否存在更快的算法。显然,由于必须生成 k 个元素,因此算法不能比 O(k) 快。对于 O(k) 解决方案,可以假设存在一个函数 skip(x),它可以在 O(1) 时间内跳过输入流中的 x 个元素(但不能向后跳过)。不过,我仍然希望保持不需要任何额外内存的要求。
【问题讨论】:
-
从长度为 n 的流中选择 一个 随机元素是
O(n)。 -
您可以使用Monte Carlo analysis 向自己保证算法运行正常。
-
查看this answer 的第二部分,特别是 Jeffrey Scott Visser 的论文链接(显然仍然有效)。
-
实施很重要。确保您生成的随机数具有足够的精度。
r<k/n可以/应该实现为random(n)<k,其中random(n)是通过拒绝的方法获得的。例如,参见 JavaRandom库类中nextInt(n)的实现。
标签: algorithm random sampling reservoir-sampling