您有一个带偏差的随机数生成器,它以概率p产生1和(1-p)的概率产生0。您不知道p的值。使用它制作一个无偏的随机数生成器,该生成器以概率0.5产生1和概率0.5产生0。
注意: 这个问题是《算法导论》(clrs)中的练习问题。
事件(p)(1-p)和(1-p)(p)具有相同的概率。将它们分别视为0和1,且舍弃另外两对结果,则得到一个无偏随机生成器。
在代码中,这个过程非常简单:
int UnbiasedRandom()
{
int x, y;
do
{
x = BiasedRandom();
y = BiasedRandom();
} while (x == y);
return x;
}
从一个有偏的硬币制造一个无偏的硬币的过程最初被归因于冯·诺伊曼(一个在数学和许多相关领域做出巨大贡献的人)。该过程非常简单:
这个算法有效的原因是因为得到HT的概率是p(1-p)
,与得到TH的概率(1-p)p
相同。因此,两个事件是等可能的。
我也在阅读这本书,它问了期望的运行时间。两次投掷结果不相等的概率是z = 2*p*(1-p)
,因此期望运行时间是1/z
。
p=0.99
的硬币,则需要投掷约50次,这并不算多)。因此,您可能认为这是一种最佳算法。可惜它不是。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
,但我们得到了大约相同数量的1
和0
1/p(1-p)
,如果p
特别小或大,则可以变得非常大,因此值得问一下该方法是否可以改进,特别是因为显然它会丢失很多信息(所有00和11情况)。这还没有提及从有偏差的随机数(不仅仅是有偏差的位)生成无偏差随机位的过程;例如,参见Camion(1974)。
我在关于随机性提取的注释中更详细地讨论了随机性提取器。
参考文献:
这里有一种方法,可能不是最有效的。通过一堆随机数,直到你得到一个形如[0...,1,0...,1](其中0...是一个或多个0)的序列。计算0的数量。如果第一个序列更长,则生成0,如果第二个序列更长,则生成1。(如果它们相同,请重试。)
这就像HotBits从放射性粒子衰变中生成随机数的方式:
由于任何给定衰变的时间都是随机的,因此两个连续衰变之间的时间间隔也是随机的。那么我们所做的就是测量一对这些间隔,并根据两个间隔的相对长度发出零或一位。如果我们为两个衰变测量相同的间隔,则丢弃该测量并重试。
我只是用一些运行的证明来解释已经提出的解决方案。这个解决方案将是公正的,无论我们改变概率的次数有多少次。在抛硬币的头和尾上,连续的"头和尾"或"尾和头"的排他性始终是公正的。
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)