昨天我有这个面试问题,我无法完全回答:
给定一个具有完美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
。
有人知道解决方案吗?
答案 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}}