给定一个函数,它产生的是1到5之间的随机整数,写一个函数,它产生的是1到7之间的随机整数。


当前回答

int rand7()
{
    return ( rand5() + (rand5()%3) );
}

rand5() -返回1-5之间的值 rand5()%3 -返回0-2之间的值 所以,当加起来时,总价值将在1-7之间

其他回答

(我剽窃了亚当·罗森菲尔德的答案,使其运行速度提高了7%左右。)

假设rand5()返回分布相等的{0,1,2,3,4}中的一个,目标是返回分布相等的{0,1,2,3,4,5,6}。

int rand7() {
  i = 5 * rand5() + rand5();
  max = 25;
  //i is uniform among {0 ... max-1}
  while(i < max%7) {
    //i is uniform among {0 ... (max%7 - 1)}
    i *= 5;
    i += rand5(); //i is uniform {0 ... (((max%7)*5) - 1)}
    max %= 7;
    max *= 5; //once again, i is uniform among {0 ... max-1}
  }
  return(i%7);
}

我们在跟踪这个循环在变量max中所能产生的最大值。如果到目前为止的结果在max%7和max-1之间,那么结果将均匀分布在该范围内。如果不是,则使用余数,余数是0到max%7-1之间的随机数,然后再次调用rand()来生成一个新的数字和一个新的max。然后我们重新开始。

编辑:在这个方程中,期望调用rand5()的次数是x:

x =  2     * 21/25
   + 3     *  4/25 * 14/20
   + 4     *  4/25 *  6/20 * 28/30
   + 5     *  4/25 *  6/20 *  2/30 * 7/10
   + 6     *  4/25 *  6/20 *  2/30 * 3/10 * 14/15
   + (6+x) *  4/25 *  6/20 *  2/30 * 3/10 *  1/15
x = about 2.21 calls to rand5()

只要没有剩下7种可能性,就再画一个随机数,将可能性数乘以5。在Perl中:

$num = 0;
$possibilities = 1;

sub rand7
{
  while( $possibilities < 7 )
  {
    $num = $num * 5 + int(rand(5));
    $possibilities *= 5;
  }
  my $result = $num % 7;
  $num = int( $num / 7 );
  $possibilities /= 7;
  return $result;
}
function Rand7
   put 200 into x
   repeat while x > 118
      put ((random(5)-1) * 25) + ((random(5)-1) * 5) + (random(5)-1) into x
   end repeat
   return (x mod 7) + 1
end Rand7

3次调用Rand5,平均125次中只重复6次。

把它想象成一个5x5x5的3D数组,一遍又一遍地填满1到7,还有6个空格。重新滚动空白。rand5调用在该数组中创建一个以5为基数的三位索引。

4D或更高的n维数组的重复次数会更少,但这意味着对rand5函数的更多调用将成为标准。你会在更高维度上得到递减的效率回报。在我看来,三个似乎是一个很好的折衷方案,但我还没有对它们进行测试。它是特定于rand5实现的。

这个问题的主要概念是关于正态分布的,这里提供了一个简单的递归解决这个问题的方法

假设我们已经在我们的作用域中有rand5():

def rand7():
    # twoway = 0 or 1 in the same probability
    twoway = None
    while not twoway in (1, 2):
        twoway = rand5()
    twoway -= 1

    ans = rand5() + twoway * 5

    return ans if ans in range(1,8) else rand7()

解释

我们可以把这个程序分成两个部分:

循环rand5()直到我们找到1或2,这意味着我们有1/2的概率在变量中有1或2 复合ans by rand5() + twoway * 5,这正是rand10()的结果,如果这不符合我们的需要(1~7),然后我们再次运行rand7。

附注:我们不能在第二部分直接运行while循环,因为双向的每个概率都需要是单独的。

但是有一个权衡,因为第一部分中的while循环和return语句中的递归,这个函数不能保证执行时间,它实际上是无效的。

结果

我做了一个简单的测试来观察我的答案的分布。

result = [ rand7() for x in xrange(777777) ]

ans = {
    1: 0,
    2: 0,
    3: 0,
    4: 0,
    5: 0,
    6: 0,
    7: 0,
}

for i in result:
    ans[i] += 1

print ans

它给了

{1: 111170, 2: 110693, 3: 110651, 4: 111260, 5: 111197, 6: 111502, 7: 111304}

因此,我们可以知道这个答案是正态分布。

简单的答案

如果你不关心这个函数的执行时间,下面是一个基于我上面给出的答案的简化答案:

def rand7():
    ans = rand5() + (rand5()-1) * 5
    return ans if ans < 8 else rand7()

这增加了大于8的值的概率,但这可能是这个问题的最短答案。

这个解决方案不浪费任何熵,并给出了范围内第一个可用的真正随机数。随着每一次迭代,得不到答案的概率可证明地降低了。在N次迭代中得到答案的概率是0到max (5^N)之间的随机数小于该范围内7的最大倍数(max-max%7)的概率。必须迭代至少两次。但这对所有解都是成立的。

int random7() {
  range = 1;
  remainder = 0;

  while (1) {
    remainder = remainder * 5 + random5() - 1;
    range = range * 5;

    limit = range - (range % 7);
    if (remainder < limit) return (remainder % 7) + 1;

    remainder = remainder % 7;
    range = range % 7;
  }
}

数值上等价于:

r5=5;
num=random5()-1;
while (1) {
   num=num*5+random5()-1;
   r5=r5*5;
   r7=r5-r5%7;
   if (num<r7) return num%7+1;
}

第一个代码以模形式计算。第二个代码只是简单的数学。或者我在某个地方犯了错误。: -)