Algorithm 使用有偏随机数生成器的无偏随机数生成器

Algorithm 使用有偏随机数生成器的无偏随机数生成器,algorithm,random,probability,clrs,Algorithm,Random,Probability,Clrs,有一个有偏随机数生成器,它生成概率为p的1和概率为(1-p)的0。你不知道p的值。使用此函数生成一个无偏随机数生成器,它生成概率为0.5的1和概率为0.5的0 注:该问题是由Cormen,Leiserson,Rivest,Stein.(clrs)算法介绍中的一个练习问题。事件(p)(1-p)和(1-p)(p)是等概率的。将它们分别取为0和1,然后丢弃另外两对结果,得到一个无偏随机生成器 在代码中,此操作非常简单: int UnbiasedRandom() { int x, y;

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

:该问题是由Cormen,Leiserson,Rivest,Stein.(clrs)算法介绍中的一个练习问题。

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

在代码中,此操作非常简单:

int UnbiasedRandom()
{
    int x, y;

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

    return x;
}
您需要从RNG中绘制成对的值,直到获得不同值的序列,即0后跟1或1后跟0。然后取该序列的第一个值(或最后一个值,无所谓)。(即,只要绘制的一对为两个0或两个1,则重复此操作)


这背后的数学很简单:0-1序列的概率与1-0序列的概率非常相同。通过始终将此序列的第一个(或最后一个)元素作为新RNG的输出,我们有机会获得零或一。这里有一种方法,可能不是最有效的。仔细研究一组随机数,直到得到[0…,1,0…,1](其中0…是一个或多个0)形式的序列。计算0的数量。如果第一个序列较长,则生成0;如果第二个序列较长,则生成1。(如果它们相同,请重试。)

这就像热比特通过放射性粒子衰变产生随机数:

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


冯·诺依曼一次得到两个比特的技巧已经出现,01对应于0和10:1,并重复00或11。使用此方法获取单个位所需提取的位的预期值为
1/p(1-p)
,如果
p
特别小或特别大,则该值可能会非常大,因此值得询问该方法是否可以改进,特别是因为它显然会丢弃大量信息(所有00和11种情况)

谷歌搜索“冯·诺依曼把戏偏颇”产生了一个更好的解决方案的问题。这个想法是,你仍然一次取两位,但是如果前两次尝试只产生00和11,你将一对0视为一个0,将一对1视为一个1,并对这些对应用冯·诺依曼的技巧。如果这也不起作用,继续以类似的方式在这一级别的成对组合,以此类推

进一步,本文将其发展为从偏置源生成多个无偏置位,基本上使用两种不同的方式从位对生成位,并给出一个草图,说明这是最佳的,因为它产生的比特数正好是原始序列中包含熵的比特数。

这个过程首先归功于(一个在数学和许多相关领域做了大量工作的人)。程序非常简单:

  • 掷硬币两次
  • 如果结果一致,重新开始,忘记两个结果
  • 如果结果不同,则使用第一个结果,而忽略第二个结果
该算法之所以有效,是因为获得HT的概率为
p(1-p)
,这与获得TH
(1-p)p
的概率相同。因此,两个事件的可能性相等

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


上一个例子看起来很鼓舞人心(毕竟,如果你有一枚偏向
p=0.99
的硬币,你需要投掷大约50次,这并不多)。所以你可能认为这是一个最优算法。遗憾的是,事实并非如此

下面是它与(图片取自此)的比较。结果表明,该算法是好的,但远不是最优的

你可以提出一个改进,如果你认为HHTT会被这个算法丢弃,但事实上它和TTHH的概率是一样的。所以你也可以停在这里,返回H,HHTTTT也是如此,等等。使用这些情况可以提高预期的运行时间,但在理论上并不是最优的


最后—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.097
,但我们得到的
1
0
的数量大致相同。我只是用一些运行证明来解释已经提出的解决方案。无论我们改变概率多少次,这个解都是无偏的。在头尾抛投中,连续的
头尾
尾头尾
的排他性始终是无偏的

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)
    

除了在其他答案中给出的冯·诺依曼程序外,还有一整套技术,称为随机性提取(也称为去像差、去像差或增白),用于从未知偏差的随机数中生成无偏差随机位。其中包括佩雷斯(1992年)迭代的冯·诺依曼程序,以及周和布鲁克(2012年)的“提取器树”。这两种方法(以及其他几种方法)都是渐近最优的,也就是说,随着输入数量的增加,它们的效率(就每个输入的输出位而言)接近最优极限(Pae 2018)

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

  • 创建两个名为U和的空列表
    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)