【问题标题】:Creating a random number generator from a coin toss通过抛硬币创建随机数生成器
【发布时间】:2012-10-23 22:25:34
【问题描述】:

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

给定一个具有完美 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

有人知道解决办法吗?

【问题讨论】:

标签: algorithm random


【解决方案1】:

您可以按照您的描述为大于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 并添加硬币翻转以尽可能减少拒绝。

【讨论】:

  • 这将如何产生范围 0、1、2、...、n - 2、n - 1?
  • 一种有用的方法,但请注意,理论上它可能需要无限长的时间才能运行。至少需要 m+1 个“掷骰子”(k 次掷硬币)的概率为 (2^k - n)^m/2^k,随着 m 的增加迅速下降,但永远不会达到零。跨度>
  • @j_random_hacker 证明任何给出统一结果的方法必须丢弃一些结果并不难。如果您生成z 随机位(对于任何z),您总共有2^z 可能的结果。如果n 不是2^z 的因子,则没有统一的方法将这些2^z 结果划分为n 不同的结果。您可以期望的最好的结果是分配几乎所有它们,并且最多保留n-1 未分配;这些将不得不被拒绝。
  • @j_random_hacker 恐怕这不可能……假设它以R 为界。这意味着有一种算法最多需要R 随机位,并产生从0n-1 的统一的东西。但与前面的评论一样,函数有2^R 可能的输入(不管它使用什么和忽略什么),并且它们是均匀分布的。除非n2^R 的一个因子,否则您不能将它们统一划分为n 结果。
【解决方案2】:

另一个有趣的解决方案可以通过马尔可夫链蒙特卡罗技术,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] 上模拟均匀随机数作为子过程。

【讨论】:

    【解决方案3】:
    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
    

    【讨论】:

      【解决方案4】:
      My #RandomNumberGenerator #RNG
      
      /w any f(x) that gives rand ints from 1 to x,  we can get rand ints from 1 to k, for any k:
      
      get ints p & q, so p^q is smallest possible, while p is a factor of x, & p^q >= k;
      
      Lbl A
      i=0 & s=1; while i < q {
      s+= ((f(x) mod p) - 1) * p^i;
      i++;
      }
      if s > k,  goto A, else return s
      
      
      //** about notation/terms:
      rand = random
      int = integer
      mod is (from) modulo arithmetic 
      Lbl is a “Label”, from the Basic language, & serves as a coordinates for executing code.  After the while loop, if s > k, then “goto A” means return to the point of code where it says “Lbl A”, & resume.  If you return to Lbl A & process the code again, it resets the values of i to 0 & s to 1.  
      i is an iterator for powers of p, & s is a sum.  
      "s+= foo" means "let s now equal what it used to be + foo".
      "i++" means "let i now equal what it used to be + 1".
      f(x) returns random integers from 1 to x. **//
      
      
      I figured out/invented/solved it on my own, around 2008.  The method is discussed as common knowledge here.  Does anyone know since when the random number generator rejection method has been common knowledge?  RSVP.
      

      【讨论】:

      • Lbl 是来自 Basic 语言的“标签”,& 用作执行代码的坐标。在 while 循环之后,如果 s > k,则“goto A”表示返回到显示“Lbl A”的代码点,然后继续。如果您返回 Lbl A 并再次处理代码,它将 i 的值重置为 0 和 s 为 1。i 是 p 的幂的迭代器,& s 是和。 s+= foo 意味着让 s 现在等于它以前的值 + foo。 f(x) 返回 1 和 x 之间的随机整数。这有帮助吗?干杯。
      • 为您的答案添加解释,而不是在 cmets 中。
      • 这是可以升级的基础RNG版本。我很快就会开始处理升级后的代码。这个基本的 RNG 只对 x 进行 1 个因式分解 p 起作用,但是可以使用多个因子来缩短计算时间。例如使用 dice 或 f(6) 生成 rand ints 从 1 到 12: 1. 使用 f(6) 生成从 1 到 6 的结果。 2. 使用 {(f(6) mod 2)-1} 到生成从 0 到 1 的结果。 3. 返回 {(步骤 1 的结果)+(6 * 步骤 2 的结果)}。相比之下,基本的 RNG 可能会使用 p=2 & q=4
      • 例如使用 dice 或 f(6) 生成从 1 到 17 的整数整数(即“使用 f(6) 生成从 1 到 18 的整数整数,只接受从 1 到 17 的返回”) 1. 使用 f(6)得到一个从 1 到 6 的 RNG int。 2. 使用 {(f(6) mod 3) - 1} 来 RNG 一个从 0 到 2 的 int。 3. if {( 步骤 1 的结果) + (6 * the第 2 步的结果)} > 17,重新开始 /w 第 1 步,否则返回该值。
      • 其他 cmets,包括使用硬币翻转生成(12 面骰子,'d12')从 1 到 12 的随机整数的算法,超过了 stackoverflow 注释字符数限制,因此链接plus.google.com/u/0/113366929351507812798/posts/h2dJSEYQeuU
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-30
      • 2018-07-27
      • 2015-10-16
      • 1970-01-01
      • 2022-09-23
      • 2020-07-09
      • 2017-02-25
      相关资源
      最近更新 更多