【问题标题】:Select random item from stream in O(1) space从 O(1) 空间中的流中选择随机项
【发布时间】:2019-04-11 21:59:59
【问题描述】:

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

流提供以下操作:

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) 空间从流中随机选择一个项目。

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

【问题讨论】:

  • 您对“O(1) 空间”的定义排除了具有足够状态空间的 PRNG 来统一选择一个元素,因此这几乎是不可能的......但这并不是 O(1) 的实际定义空间。
  • @MattTimmermans 如果我使用真正的 RNG 设备,它会逐位返回 rv?
  • @SeverinPappadeux 当然,或模拟计算机,或许多其他东西,但没有迹象表明任何一种都是允许的。
  • 为什么计算元素个数会消耗 O(log n) 空间?
  • 嗯?!?你在哪里找到这样的限制?在 /dev/random 之上编写这样的生成器非常简单

标签: algorithm optimization random probability


【解决方案1】:

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

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

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

【讨论】:

  • 要实现真正的均匀分布,可能的随机值必须与流中的项目(或它的倍数)一样多。那么最大的随机数将至少为 n,其中 n 是流的长度,因此它需要 O(log n) 空间。您不保留最大数字的事实并不重要,因为在某些时候您仍然需要生成它并将其保存在内存/寄存器中。
  • 我在您的其他评论中看到您认为这是不可能的。事实上,这回答了我的问题,所以如果您将您的评论作为答案发布并可能详细说明,我会接受它。
  • 我宁愿提供一种简单有效的方法来从流中统一选择,以防将来的搜索者有实际需要这样做。
  • @БоратСагдиев 不,假设你有真正的 RNG。
  • @БоратСагдиев,Severin 鼓励我提供你想要的答案 :) 与 Myhill Nerode 定理有关。
【解决方案2】:

在常数空间中?当然,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 可能是对的 - 我必须进行计数(log2(k) 存储),到目前为止还没有办法避免它。即使使用 RNG 技巧,我也必须以 1/k 的概率采样 0,而这个 k 会随着流的大小而增长。

【讨论】:

  • 错了。您的 k 的大小为 O(log n)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多