从投掷硬币创建随机数生成器

时间:2012-11-03 12:25:26

标签: algorithm random

昨天我有这个面试问题,我无法完全回答:

给定一个具有完美1:1分布的函数f() = 0 or 1,创建一个函数f(n) = 0, 1, 2, ..., n-1,每个函数的概率为1 / n

如果n是2的自然幂,我可以想出一个解决方案,即使用f()生成二进制数k=ln_2 n的位。但这显然不适用于n = 5,因为这会产生我们不想要的f(5) = 5,6,7

有人知道解决方案吗?

4 个答案:

答案 0 :(得分:20)

如您所述,您可以为大于n的最小2的幂建立一个rng。然后,每当此算法生成大于n-1的数字时,抛弃该数字并再次尝试。这被称为拒绝方法。

<强>加成

算法是

Let m = 2^k >= n where k is is as small as possible.
do
   Let r = random number in 0 .. m-1 generated by k coin flips
while r >= n
return r

此循环在最多i次迭代时停止的概率受1 - (1/2)^i的限制。这非常迅速地发展到1:循环在30次迭代后仍然运行,概率小于十亿分之一。

您可以使用略微修改的算法减少预期的迭代次数:

Choose p >= 1
Let m = 2^k >= p n where k is is as small as possible.
do
   Let r = random number in 0 .. m-1 generated by k coin flips
while r >= p n
return floor(r / p)

例如,如果我们尝试使用更简单的算法生成0 ... 4(n = 5),我们将拒绝5,6和7,这是结果的3/8。使用p = 3(例如),pn = 15,我们的m = 16,并且只会拒绝15或1/16的结果。价格需要四个硬币翻转,而不是3和一个分区操作。您可以继续增加p并添加硬币翻转,以便根据需要减少拒绝。

答案 1 :(得分:1)

另一个有趣的解决方案可以通过马尔可夫链蒙特卡罗技术,Metropolis-Hastings算法得出。如果需要大量样本,这将显着提高效率,但只能达到限制内的均匀分布。

 initialize: x[0] arbitrarily    
 for i=1,2,...,N
  if (f() == 1) x[i] = (x[i-1]++) % n
  else x[i] = (x[i-1]-- + n) % n

对于大N,向量x将包含0到n之间的均匀分布的数字。此外,通过添加接受/拒绝步骤,我们可以从任意分布模拟,但您需要在[0,1]上模拟均匀随机数作为子过程。

答案 2 :(得分:0)

def gen(a, b):
  min_possible = a
  max_possible = b

  while True:
    floor_min_possible = floor(min_possible)
    floor_max_possible = floor(max_possible)
    if max_possible.is_integer():
      floor_max_possible -= 1

    if floor_max_possible == floor_min_possible:
      return floor_max_possible

    mid = (min_possible + max_possible)/2
    if coin_flip():
      min_possible = mid
    else:
      max_possible = mid

答案 3 :(得分:-3)

{{1}}