1、产生随机数[0,5),也就是说得到数字0,1,2,3,4的概率都是1/5,现在考虑,已有方法Random_7
根据这个方法,构造方法Random_5_By_Random_7,怎么办?
2、这是一个大范围的随机数,像小范围映射,解决办法很简单,就是对5取模。但是仔细想想,就发现不对,
Random_7产生[0,7)每个数字的概率都是1/7,如果直接对5取模,导致0,1的概率是2/7,而2,3,4的概率
是1/7,这显然不满足随机数的要求。
3、怎么解决上面的问题?
原因是:大范围向小范围映射,由于不能整除,导致部分数字的概率增加,解决办法就是,去掉超出的部分,
比如Random_5_By_Random_7,如果Random_7产生的随机数>=5,重新再产生,一直循环下去,直到产生的数字
满足要求。
4、那这样产生[0,5)每个数字的概率是多少呢?是1/7吗?
错,还是1/5, 为什么?从数学角度来看,以0为例,概率为:
第1次产生0:1/7
第2次产生0:2/7*(1/7) //前面1次产生的是5,6,再试第2次
第3次产生0:(2/7)^2*(1/7) //前面2次产生的是5,6,再试第3次
....
总概率 1/7+2/7*(1/7)+(2/7)^2*(1/7)... = 1/7*((2/7)^0+(2/7)^1+(2/7)^2+...)
等比数列求和,设y=1+n+n^2+n^3+....
则ny=n+n^2+n^3+n^4.... 相减,得到
(1-n)y=1-n^m,其中n小于1,m为无穷大,则y = 1/(1-n),n为2/7,则y为7/5
产生0的概率为 1/7*(7/5) = 1/5
5、那接着思考,Random_7_By_Random_5,该怎么实现呢?
先考虑Random_10_By_Random_5,当然不能直接映射。如果直接映射,[0,5)都是1/5,[5,10)都是0
容易想到的办法是 2*Random_5,这个也不对,这样的话,1的概率是0,2的概率是1/5
那么Random_5+Random_5,不对,0的概率是1/5*(1/5)=1/25
而我们期望的是[1,10)每个数字的概率都是1/10
6、怎么解决上面的问题?
必须是从大范围向小范围映射值,同时裁剪掉不满足条件的数字,继续下去。怎么扩大Random_5呢?
连续产生两次,范围就是5*5=25,对7取余,去掉21,22,23,24
int Random_5()
{
static bool isFirstCall = true;
if(isFirstCall)
{
srand(time(NULL));
isFirstCall = false;
}
return rand()%5;
}
int Random_7()
{
static bool isFirstCall = true;
if(isFirstCall)
{
srand(time(NULL));
isFirstCall = false;
}
return rand()%7;
}
int Random_5_By_Random_7()
{
int a = Random_7();
while(a>=5)
{
a=Random_7();
}
return a;
}
// 想象一个二维数组,第一次产生行数,第二次产生行的第几个元素。
int Random_7_By_Random_5()
{
int row = Random_5();
int col = Random_5();
while(row*5+col >= 21)
{
row = Random_5();
col = Random_5();
}
return (row*5+col)%7;
}