修改均匀随机数生成器的范围

时间:2012-07-03 04:59:36

标签: algorithm random

我得到一个函数rand5(),它在均匀分布中生成闭区间[1,5]中的随机整数。我如何使用rand5(),而不是别的,来创建一个函数rand7(),它在[1,7]中生成整数(再次,均匀分布)?


  1. 我搜索了stackoverflow,发现了很多类似的问题,但不完全像这个问题。
  2. 我最初的尝试是rand5()+ 0.5 * rand5()+ 0.5 * rand5()。但是这不会以均匀的概率从1到7生成整数。任何答案或答案链接都是非常受欢迎的。

4 个答案:

答案 0 :(得分:7)

请注意,使用有限数量的draw5() 调用无法实现完美的统一分布,因为对于每个k5^k % 7 != 0 - 所以您将永远有一些“备用”元素。

这是一个解决方案,其中无限数量的draw5() 使用:

画两个数字,x1,x2。这有5 * 5 = 25种可能的结果。

注意25 / 7~ = 3.57。选择3 * 7 = 21种组合,这样每个组合将被映射到[1,7]中的一个数字,对于所有其他4个数字 - 重绘。

例如:

(1,1),(1,2),(2,1) : 1
(3,1),(1,3),(3,2): 2
(3,3),(1,4),(4,1): 3
(2,4),(4,2)(3,4): 4
(4,3), (4,4), (1,5): 5
(5,1), (2,5), (5,2) : 6
(5,3), (3,5), (4,5) : 7
(5,4),(5,5),(2,3), (2,2) : redraw

答案 1 :(得分:4)

这是一个简单的方法:

  1. 使用rand5()从集合{1,2,4,5}生成三个随机整数的序列(即丢弃生成的任何3个)。
  2. 如果所有三个数字都在集{1,2}中,则丢弃序列并返回步骤1.
  3. 对于序列中的每个数字,将{1,2}映射到0并将{4,5}映射到1.将这些值用作3位数的三位值。因为位不能都是0,所以数字将在[1,7]范围内。因为每个位的概率为0或1,所以[1,7]的分布应该是均匀的。

答案 2 :(得分:2)

好吧,我不得不考虑一段时间,但实际上并没有那么难。想象一下,而不是rand5你有rand2输出0或1.你可以通过简单地做rand2我们的rand5

rand2() {
    if(rand5() > 2.5) return 1
    else return 0
}

现在多次使用rand2做一棵树来获得rand7。例如,如果你开始rand7可以在[1,2,3,4,5,6,7]抛出rand2之后给出0你现在是[1,2,3,4]的子集并且在另一次抛出或之后rand2是1,你是[3,4]的子集,最后一次抛出给rand7的输出为4.一般来说,这个树技巧可以用来获取rand2并映射到randx,其中x是任意整数。

答案 3 :(得分:2)

这里有一个元技巧,它可以解决许多这些问题:当我们以某种方式对待这些术语时会产生偏见,所以如果我们在每一步都对它们进行相同的处理,并且仅在<强>设置,我们将远离麻烦。

我们必须至少调用一次rand5()(显然!),但是如果我们分支就会发生坏事,除非我们很聪明。因此,让我们为7种可能性中的每一种调用一次:

In [126]: import random

In [127]: def r5():
   .....:     return random.randint(1, 5)
   .....: 

In [128]: [r5() for i in range(7)]
Out[128]: [3, 1, 3, 4, 1, 1, 2]

显然,这些术语中的每一个都同样可能是这些数字中的任何一个......但是其中只有一个碰巧是2,所以如果我们的规则是“选择任何一个术语rand5()返回2”那么它会有工作。或4,或者其他什么,如果我们只是循环足够长的话就会发生。所以有很多方法可以找到有用的东西。这里(伪代码 - 这是可怕的Python)是一种方式:

import random, collections

def r5():
    return random.randint(1, 5)

def r7():
    left = range(1, 8)
    while True:
        if len(left) == 1: 
            return left[0]
        rs = [r5() for n in left]
        m = max(rs)
        how_many_at_max = rs.count(m)
        if how_many_at_max == len(rs):
            # all the same: try again
            continue
        elif how_many_at_max == 1:
            # hooray!
            return left[rs.index(m)]
        # keep only the non-maximals
        left = [l for l,r in zip(left, rs) if r != m]

给出了

In [189]: collections.Counter(r7() for _ in xrange(10**6))
Out[189]: Counter({7: 143570, 5: 143206, 4: 142827, 2: 142673, 6: 142604, 1: 142573, 3: 142547})