【问题标题】:Select an element from a stream with uniform distributed probability从具有均匀分布概率的流中选择一个元素
【发布时间】:2014-06-14 15:19:50
【问题描述】:

给你:

  1. 一个流(流的结尾是 EOF)
  2. 一个函数next(),用于获取流中的下一个元素并推进流中的指针
  3. 随机生成器均匀地生成 0 到 1(包括)之间的浮点数

输出:

  • 被证明是随机(均匀分布)选择的元素

您可以使用一个或两个变量。

你不能使用数组/列表,你不能告诉trying to get all elements out and store them all and then pick的方式。


这是一道面试题。

我的想法是:

  1. 我使用 var cur 来存储最近保存的元素
  2. 所以,如果我得到一个新元素,我会使用生成器生成一个随机的 0 或 1,如果是 0 则 cur = new element;否则,继续;
  3. 如果我得到 EOF,则返回 cur

我的想法对吗?如何证明?


这是同一个问题

How would you pick a uniform random element in linked list with unknown length?

【问题讨论】:

  • 绘制概率树。
  • @OliCharlesworth 怎么样?你的意思是画一个概率树来证明?
  • 对于前几个阶段,是的,要识别数学关系。从中你应该能够识别出一个证据。
  • 你的算法基本上返回对应于生成器最后一次返回 0 的元素,这对应于末尾连续 1 的数量的几何分布(从 EOF 开始的方向看溪流)。这不是均匀分布。
  • 您说生成器产生的浮点数统一为 0 或 1。您确定不应该是 介于 0 和 1 之间的浮点数吗?

标签: algorithm random


【解决方案1】:

此算法以 1/2 的概率选择流中的最后一个元素,因此除非流的大小 = 2,否则这不是有效的解决方案。

一种有效的方法是将从 [0..1] 之间的均匀分布中抽取的随机浮点值分配给每个元素,并在最后返回具有最大(或最小)值的那个。这可以在 O(1) 辅助空间中完成,您只需要记住最大值和关联元素即可。

【讨论】:

  • 我在想同样的解决方案,但是这句话读起来好像生成器只返回 0 或 1:“随机生成器均匀地生成 0 或 1”。它没有说“在 0 和 1 之间浮动”。但是,为什么会浮动呢?
  • 我在网上找到了这个面试题,可能描述有误。
  • @JacksonTale 想象一下在处理完所有元素后绘制的随机值序列。它们都是不同的。根据值在所有值集中的排名为它们分配整数。结果序列是一个均匀绘制的随机排列(因为所有排列都是可能的,没有一个比任何其他排列更有可能)。随机排列中最大值的位置是均匀分布的。虽然不确定是否有严格的证明
  • 如果没有给出随机生成器,是否可以实现?
  • @Jackson 你的意思是如果你只有一个比特生成器? amit 提出了一个只需要随机整数的解决方案,您可以使用随机位来实现它
【解决方案2】:

设当前元素的索引为i

选择以1/i 的概率“记住”当前元素。当到达 EOF 时,产生你记得的元素。

最后,对于索引为i 的每个元素,都有一个被选中的概率:

可以使用归纳法完成正式证明,遵循这些指南。

【讨论】:

  • 我认为这和我的回答有同样的问题:如果你只有一个只产生 0 或 1 的随机数生成器,如何决定是否取元素(概率为 1/i)?至少不再是 O(1) 了。不过很酷的技巧
  • @NiklasB。您只需要 O(log(i+1)) = O(log(i)) 绘制(平均)来产生概率 1/i,并且您不需要多个变量(基本上是迭代器)。
  • 有道理,这看起来很不错。这也没有对浮点精度做任何假设:)
  • @JacksonTale 查看编辑后的答案,在撰写评论时来自 android。
  • @Asad 是的,这叫做Reservoir Sampling。这个想法是保存一个大小为k 的数组(其中k 是您选择的元素数),概率为k/i(其中i 是您现在正在迭代的索引)您替换其中一个元素在这个数组中,如果你绘制来替换一个元素,你对每个元素的概率为1/k
【解决方案3】:

此解决方案并不完全适合问题中的所有参数。但是,此解决方案是基于现实世界的需求和代码。

private static final SecureRandom s_random = new SecureRandom();  // Use SecureRandom for truly random selection without a pattern

public static <V> V randomValue(Iterator<V> values)
{
   V result, item;
   int count;

   result = null;

   for (count = 1, values.hasNext(); count++)
   {
      item = values.next();

      if (count == 1)  // Always select the first element
         result = item;
      else if (s_random.nextnextInt(count) == 0)  // Replace the previous random item with a new item with 1/count probability
         result = item;
   }

   if (result == null)
      throw new IllegalArgumentException("No value found");

   return(result);
}

以上算法取自here

【讨论】:

    【解决方案4】:

    这是我解决这个问题的python代码(也是available with tests on my github):

    import random as rnd
    
    def sample(iterator):
        """ Uniform sampling of an element from an stream.
        The i-th element (indexed at 1) is sampled with probability 1/i.
        This can be shown by recurrence to lead to uniform sampling. 
        Indeed, at step n we impose p(val==n)=1/n, and all elements i before n 
        were sampled with equal probability, which is also equal to 1/n since
        p(val<n) = (n-1)*p(val==i) = 1-1/n = (n-1)/n => p(val==i) = 1/n """
        i = 1
        out = None
        for elm in iterator:
            if rnd.uniform(0,1) <= 1/i:
                out = elm
            i += 1
        return out
    

    【讨论】:

      猜你喜欢
      • 2012-01-23
      • 2021-12-29
      • 1970-01-01
      • 2017-07-22
      • 1970-01-01
      • 2011-08-22
      • 2012-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多