【问题标题】:Unbiased random number generator using a biased one使用有偏随机数生成器
【发布时间】:2009-12-31 19:45:24
【问题描述】:

您有一个有偏随机数生成器,它以概率 p 生成 1,以概率 (1-p) 生成 0。你不知道 p 的值。使用它可以生成一个无偏随机数生成器,它以 0.5 的概率生成 1,以 0.5 的概率生成 0。

注意:这个问题是 Cormen, Leiserson, Rivest, Stein 的 Introduction to Algorithms 中的一个练习题。(clrs)

【问题讨论】:

  • 我猜答案与使用偏置生成器一次标准方式和一次作为反函数有关,因此您的 p 概率为 0 一次和 (1-p) 概率为a 0 第二次迭代并混合两个结果以平衡分布。我不知道它背后的确切数学原理。
  • Eric- 是的,如果你做了 (rand() + (1-rand()))/2,你可以合理地期望得到一个公正的结果。请注意,在上面你应该调用 rand() 两次——否则你总是得到 .5
  • @JohnE:基本上我就是这么想的,但这不会让你得到一个直接的 0 或 1,这是要求的。我认为 pau 的回答一针见血。
  • @Eric。 Hrm-好点。你还不能做类似 (rand() xor !rand()) 的事情吗? (假设我们将 1 和 0 视为布尔值)
  • 这看起来与我最近提出的问题完全相反:stackoverflow.com/questions/2075912/…

标签: algorithm random probability clrs


【解决方案1】:

事件 (p)(1-p) 和 (1-p)(p) 是等概率的。将它们分别作为 0 和 1 并丢弃其他两对结果,您将得到一个无偏随机生成器。

在代码中,这很简单:

int UnbiasedRandom()
{
    int x, y;

    do
    {
        x = BiasedRandom();
        y = BiasedRandom();
    } while (x == y);

    return x;
}

【讨论】:

【解决方案2】:

produce an unbiased coin from a biased one 的过程首先归功于Von Neumann(一个在数学和许多相关领域做了大量工作的人)。过程超级简单:

  • 掷硬币两次。
  • 如果结果匹配,重新开始,忘记两个结果。
  • 如果结果不同,使用第一个结果,忘记第二个。

这个算法之所以有效,是因为得到HT的概率是p(1-p),和得到TH的概率是(1-p)p。因此,两个事件的可能性相同。

我也在读这本书,它询问了预期的运行时间。两次抛掷不相等的概率为z = 2*p*(1-p),因此预期运行时间为1/z


前面的例子看起来很鼓舞人心(毕竟,如果你有一个偏差为p=0.99 的硬币,你需要扔硬币大约 50 次,这并不多)。所以你可能认为这是一个最优算法。可惜不是。

这是它与Shannon's theoretical bound 的比较(图片来自answer)。这表明该算法很好,但远非最优。

如果你考虑到 HHTT 会被这个算法丢弃,你可以提出改进,但实际上它与 TTHH 具有相同的概率。所以你也可以在这里停下来并返回H。HHHHTTTT等也是如此。使用这些案例可以提高预期的运行时间,但在理论上并未达到最优。


最后——python代码:

import random

def biased(p):
    # create a biased coin
    return 1 if random.random() < p else 0

def unbiased_from_biased(p):
    n1, n2 = biased(p), biased(p)
    while n1 == n2:
        n1, n2 = biased(p), biased(p)

    return n1

p = random.random()
print p

tosses = [unbiased_from_biased(p) for i in xrange(1000)]
n_1 = sum(tosses)
n_2 = len(tosses) - n_1
print n_1, n_2

这是不言自明的,这里是一个示例结果:

0.0973181652114
505 495

如您所见,尽管我们有 0.097 的偏差,但我们得到的 10 的数量大致相同

【讨论】:

  • 如果偏差本身随着时间的推移而改变,这仍然有效吗?
  • @2501 不,它没有
  • 感谢您的回复。很明显,概率不再相同了。我想知道现实生活中的生成器是如何处理这个问题的。
【解决方案3】:

von Neumann 的诀窍是一次获取两个位,01 对应于 0,10 对应于 1,并且重复 00 或 11 已经出现。使用这种方法得到单个位需要提取的位的期望值是1/p(1-p),如果p特别小或特别大,它会变得相当大,所以值得问问该方法是否可以改进,特别是因为很明显它会丢弃大量信息(所有 00 和 11 案例)。

谷歌搜索“von neumann trick biased”产生了this paper,为该问题开发了更好的解决方案。这个想法是您仍然一次取两个位,但如果前两次尝试只产生 00 和 11,您将一对 0 视为单个 0,将一对 1 视为单个 1,并应用 von Neumann 的技巧到这些对。如果这也不起作用,请继续在此级别的对中进行类似的组合,依此类推。

进一步,本文将其发展为从有偏源生成多个无偏位,本质上是使用两种不同的方式从位对生成位,并给出一个草图,即这是最佳的,因为它可以准确地产生原始序列中包含熵的位数。

【讨论】:

    【解决方案4】:

    您需要从 RNG 中绘制 个值,直到获得一系列不同的值,即零后接一或一后接零。然后,您获取该序列的第一个值(或最后一个,无关紧要)。 (即只要绘制的对是两个零或两个一,就重复)

    这背后的数学原理很简单:0 后 1 序列与 1 后 0 序列具有相同的概率。通过始终将此序列的第一个(或最后一个)元素作为新 RNG 的输出,我们有机会获得 0 或 1。

    【讨论】:

      【解决方案5】:

      这是一种方法,可能不是最有效的。仔细检查一堆随机数,直到得到 [0..., 1, 0..., 1] 形式的序列(其中 0... 是一个或多个 0)。计算 0 的个数。如果第一个序列较长,则生成 0,如果第二个序列较长,则生成 1。(如果它们相同,请重试。)

      这就像 HotBits 从放射性粒子衰变中生成随机数一样:

      由于任何给定衰减的时间是随机的,因此两次连续衰减之间的间隔也是随机的。然后,我们要做的是测量一对这些间隔,并根据两个间隔的相对长度发出一个零或一个位。如果我们测量两次衰减的相同间隔,我们会丢弃测量值并重试

      HotBits: How It Works

      【讨论】:

        【解决方案6】:

        除了其他答案中给出的冯诺依曼程序之外,还有一整套技术,称为 randomness extract(也称为 debiasingdeskewing em> 或 whitening),用于从未知偏差的随机数中产生无偏差的随机位。它们包括 Peres (1992) 的迭代 von Neumann 程序,以及 Zhou 和 Bruck (2012) 的“提取树”。这两种方法(以及其他几种方法)都是渐近最优的,也就是说,随着输入数量的增加,它们的效率(就每个输入的输出位数而言)接近最优极限(Pae 2018)。 p>

        例如,Peres 提取器将比特列表(零和具有相同偏差的一)作为输入,描述如下:

        1. 创建两个名为 U 和 V 的空列表。然后,当输入中保留两个或更多位时:
          • 如果接下来的两位是 0/0,则将 0 附加到 U 并将 0 附加到 V。
          • 否则,如果这些位为 0/1,则将 1 附加到 U,然后写入 0。
          • 否则,如果这些位为 1/0,则将 1 附加到 U,然后写入 1。
          • 否则,如果这些位是 1/1,则将 0 附加到 U 并将 1 附加到 V。
        2. 递归运行此算法,从 U 中的位读取。
        3. 递归运行此算法,从 V 中的位读取。

        更不用说从有偏的骰子或其他有偏的随机数(不仅仅是有偏的位)产生无偏随机位的程序了;例如,参见 Camion (1974)。

        我在note on randomness extraction 中讨论了更多关于随机提取器的信息。

        参考:

        【讨论】:

          【解决方案7】:

          我只是用一些运行证明来解释已经提出的解决方案。无论我们改变概率多少次,这个解决方案都是无偏的。在头尾折腾中,连续的head n tailtail n head 的排他性始终是无偏的。

          import random
          
          def biased_toss(probability):
              if random.random() > probability:
                  return 1
              else:
                  return 0
              
          def unbiased_toss(probability):
              x = biased_toss(probability)
              y = biased_toss(probability)
              while x == y:
                  x = biased_toss(probability)
                  y = biased_toss(probability)
              else:
                  return x
          
          # results with contain counts of heads '0' and tails '1'
          results = {'0':0, '1':0}
          for i in range(1000):
              # on every call we are changing the probability
              p = random.random()
              results[str(unbiased_toss(p))] += 1
          
          # it still return unbiased result
          print(results)
              
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-02-16
            • 2018-12-31
            • 2013-07-29
            • 2019-02-28
            • 2017-08-04
            • 2015-03-14
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多