算法题
用返回0-6随机数的函数构造返回0-9随机数的函数
问题:给定一个返回06的随机自然数的函数,记为rand6(),则如何用rand6()函数构造返回09随机自然数的函数rand9()?
对于此问题,首先最容易想到的rand6() + 3、rand6() * 1.5、rand6()*2 - 4 等很明显都是错误的(rand6() + 3将返回的是3~9之间的随机数;rand6() * 1.5对于在返回int型的随机数时某些随机数不能产生;rand6()*2 - 4会产生负数)。
如果我们考虑相加,即rand6()+rand6(),这样产生的随机数的范围是012,所以我们在产生1012之间的随机数时不保留而重新产生一个新的随机数。这样做似乎是正确的,然而我们却忽略了一点:每个随机数产生的概率相等,即等概率。产生0只有一种可能(两次产生的随机数都为0),产生1却有两种可能(两次产生的随机数分别为0、1或1、0),……
再考虑相乘,即rand6()*rand6(),这样产生的随机数的范围是036,在产生3036之间的随机数时不保留而重新产生一个新的随机数,最后除以3返回。这样做同样不满足等概率,两次产生的随机数只要有一个为0返回的结果即为0,而只有两次产生的随机数都为1时返回的结果才为1,……
因此简单的直接相加相乘都是错的。
我们可以想,第一个rand6()产生一个06范围的随机数a,第二个rand6()产生一个06范围的随机数b,如果(a, b)这一对数可以确定一个唯一的数,从而可以产生的49个数(a和b都有7种可能,则(a, b)确定的数就有7*7=49种可能)都是等概率的。这就相当于有一个二维坐标系,X轴和Y轴上都可以取0~6的数,对于取x=a,y=b可以确定一个唯一的点(a, b)。
想到这个就是我们的关键,接下来就是想办法构造函数使得产生的每个数都是唯一的。我们不难想到把第一个rand6()产生的随机数a放在个位上,而把第二个rand6()产生的随机数b放在十位上,所有可能为:0006,1016,2026,3036,4046,5056,6066。很容易看出这些就是7进制数,所以我们用rand6()*7+rand6()就能生成049范围的随机数,而且每个数产生的概率相等,都是1/49。产生4049之间的随机数时不保留而重新产生一个新的随机数,这样产生039之间的数也是等概率的,仍然都是1/49,对于产生的数我们记为r(0<= r <=39),则个位数上的数就是想要的数。
具体的函数构造的C++代码如下:
int rand9() // 用rand6()编写随机产生0~9之间的数
{
int a, b, r;
do{
a = rand6(); // 随机产生0~6之间的数
b = rand6();
r = a * 7 + b;
} while (r >= 40);
return r / 10 +1;
}
如果进行测试,可写出完整代码如下:
#include <iostream>
#include <ctime>
using namespace std;
// 随机产生0~6之间的数
int rand6()
{