从O(1)空间中的流中选择随机项

时间:2019-04-11 22:00:00

标签: algorithm optimization random probability

使用恒定空间从流中以均匀的概率随机选择一个项目

该流提供以下操作:

class Stream:

  def __init__(self, data):
    self.data = list(data)

  def read(self):
    if not self.data:
      return None

    head, *self.data = self.data
    return head

  def peek(self):
    return self.data[0] if self.data else None

流中的元素(遍历data的元素)的大小是恒定的,并且它们都不是None,因此None表示流的结尾。流的长度只能通过消耗整个流来了解。请注意,计算元素数量会占用 O(log n)空间。

我相信没有办法统一使用 O(1)空间从流中随机选择一个项目。

任何人都可以证明这一点吗?

2 个答案:

答案 0 :(得分:4)

在恒定的空间?当然,Reservoir Sampling,恒定空间,线性时间

一些经过严格测试的代码

import numpy as np

def stream(size):
    for k in range(size):
        yield k

def resSample(ni, s):
    ret = np.empty(ni, dtype=np.int64)

    k = 0
    for k, v in enumerate(s):
        if k < ni:
            ret[k] = v
        else:
            idx = np.random.randint(0, k+1)
            if (idx < ni):
                ret[idx] = v

    return ret

SIZE = 12

s = stream(SIZE)
q = resSample(1, s)
print(q)

我看到有一个关于RNG的问题。假设我有真正的RNG,即一次返回一位的硬件设备。我们仅在获取索引的代码中使用它。

if (idx < ni):

将为选择一个元素触发唯一条件 是ni=1的时间,因此idx只能是零。

因此具有这种实现的np.random.randint(0,k + 1)类似于

def trng(k):
    for _ in range(k+1):
        if next_true_bit():
            return 1 # as soon as it is not 0, we don't care
    return 0 # all bits are zero, index is zero, proceed with exchange

QED,这种实现是可能的,因此这种采样方法应该有效

更新

@kyrill可能是正确的-我必须要计数(log 2 (k)存储),到目前为止,没有办法避免这种情况。即使使用RNG技巧,我也必须以概率1/k采样0,并且这个k随着流的大小而增长。

答案 1 :(得分:3)

为每个元素生成一个随机数,并记住具有最小数字的元素。

这是我最喜欢的答案,但您可能正在寻找的答案是:

如果流的长度为 N 个项目,则返回 Nth 个项目的概率为 1 / N 。由于每个 N 的概率都不同,因此任何可以完成此任务的机器在读取不同长度的流后都必须进入不同的状态。由于可能的长度数是无限的,因此所需的可能状态数是无限的,并且机器将需要无限量的内存来区分它们。