使用偏置的无偏随机数发生器

时间:2009-12-31 19:45:25

标签: algorithm random probability clrs

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

注意:这个问题是Cormen,Leiserson,Rivest,Stein的算法导论中的一个练习问题。(clrs)

7 个答案:

答案 0 :(得分:21)

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

在代码中,这很简单:

int UnbiasedRandom()
{
    int x, y;

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

    return x;
}

答案 1 :(得分:4)

von Neumann一次获得两位的技巧,01表示0和10比1,重复00或11已经出现。使用此方法获取单个位所需的位的预期值为1/p(1-p),如果p特别小或大,则可能会变得非常大,因此值得询问方法是否合适可以改进,特别是因为很明显它会抛弃很多信息(所有00和11个案例)。

谷歌搜索“von neumann技巧偏见”产生了this paper,为问题提供了更好的解决方案。我的想法是你仍然一次取两位,但如果前两次尝试只生成00和11,你将一对0视为单个0和一对1作为单个1,并应用冯诺依曼的技巧对这些对。如果这也不起作用,那么在这个级别的对上保持类似的组合,依此类推。

此外,本文将其发展为从偏置源生成多个无偏位,主要使用两种不同的方式从位对生成位,并给出草图,这是最佳的,因为它产生了精确的位原始序列在其中具有熵的位数。

答案 2 :(得分:4)

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)。它表明该算法很好,但远非最优。

enter image description here

如果您认为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

大致相同

答案 3 :(得分:2)

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

这背后的数学很简单:0然后1序列具有与1然后零序列非常相同的概率。通过始终将此序列的第一个(或最后一个)元素作为新RNG的输出,我们甚至可以得到零或一个。

答案 4 :(得分:1)

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

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

  

由于任何给定衰减的时间是随机的,因此两个连续衰减之间的间隔也是随机的。那么,我们所做的是测量一对这些间隔,并根据两个间隔的相对长度发出零或一位。如果我们测量两个衰变的相同间隔,我们放弃测量并再试一次

HotBits: How It Works

答案 5 :(得分:0)

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

例如,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中的位读取。

更不用说从偏斜的 dice 或其他偏斜的随机数(不只是偏斜的位)产生无偏随机比特的过程了;参见例如Camion(1974)。

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

参考:

答案 6 :(得分:-1)

我只是用一些运行证明来解释已经提出的解决方案。无论我们改变概率多少次,该解决方案都将是公正的。在头尾折腾中,连续的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)