【问题标题】:constant memory reservoir sampling, O(k) possible?恒定内存库采样,O(k) 可能吗?
【发布时间】: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&lt;k/n 可以/应该实现为random(n)&lt;k,其中random(n) 是通过拒绝的方法获得的。例如,参见 Java Random 库类中 nextInt(n) 的实现。

标签: algorithm random sampling reservoir-sampling


【解决方案1】:

如果是真实流,则需要O(n)时间来扫描。

您现有的算法很好。 (我之前弄错了。)你可以通过归纳证明你没有在i尝试中选择第一个元素的概率是1 - i/n = (n-i)/n。首先,通过检查,i=0 是正确的。现在如果你在ith 次尝试中没有选择它,那么下一个选择它的几率是1/(n-i)。然后在i+1'th 尝试中选择它的几率是((n-i)/n) * (1/(n-i)) = 1/n。这意味着在第一个i+1 次中没有选择它的几率是1 - i/n - 1/n = 1 - (i+i)/n。这样就完成了归纳。所以在第一次k 尝试中选择第一个元素的几率是没有选择它的几率,或者1 - (n - k/n) = k/n

但是如果你有O(1) 访问任何元素呢?请注意,选择k 与选择n-k 离开是一样的。所以不失一般性,我们可以假设k &lt;= n/2。这意味着我们可以使用这样的随机算法:

chosen = set()
count_chosen = 0
while count_chosen < k:
    choice = random_element(stream)
    if choice not in chosen:
        chosen.add(choice)
        count_chosen = count_chosen + 1

集合将是O(k) 空间,并且由于每个随机选择对您来说是新的概率至少为0.5,因此预期运行时间不会比2k 选择差。

【讨论】:

  • 我实际上是在寻求一个占用 O(1) 空间的解决方案。尽管您提出了一个关于需要 O(n) 来扫​​描输入流的好观点。但是,如果存在第二个函数以在 O(1) 中跳过流中的元素怎么办?就我的目的而言,这种类似搜索的函数不能向后扫描,但是......只能向前扫描......并且可以假设流中的每个元素占用相同的空间,因此我们可以任意搜索超出当前的。我已经调整了问题以适应这一点。
  • @markt1964 您可以简单地旋转并生成随机数,直到获得成功,然后搜索流以跳过您必须生成的数字计数。
  • 另外,您使用的概率计算似乎是错误的。如果一个人有 7 个元素,需要选择其中的 6 个,根据你的计算,选择第一个元素的概率是 1/7+1/6+1/5+1/4+1/3+1/2,这是 1.59,作为概率分数没有意义。
  • @MarkRansom 如果说“直到你得到一击”,你的意思是直到我得到一个我以前没有得到的,这需要知道我以前有哪些,这会占用空间。跨度>
  • @markt1964 不,“获得成功”是指if random()&lt;k/n 的积极结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-31
  • 1970-01-01
  • 2019-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多