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

Бор*_*иев 6 random algorithm optimization 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
Run Code Online (Sandbox Code Playgroud)

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

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

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

Sev*_*eux 5

在恒定空间中?当然,油藏采样,恒定空间,线性时间

一些经过轻微测试的代码

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)
Run Code Online (Sandbox Code Playgroud)

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

if (idx < ni):
Run Code Online (Sandbox Code Playgroud)

要选择一个元素时触发条件的唯一方法是当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
Run Code Online (Sandbox Code Playgroud)

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

更新

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


Mat*_*ans 5

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

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

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