让我们把这个问题泛化一下,从特殊到一般。现在我给你两个生成随机数的函数Randa, Randb。Randa和Randb分别产生0到a的随机数和0到b的随机数,a,b不相等 (相等就没必要做转换了)。现在让你用Randa实现Randb。
首先当 a>b , 时,最简单的就是通过:
int x=a;
while(x>b)
{
x=randA();
}
return x;
即只输出在0-b范围内的情况,但是对于a,b相差很大的情况,会导致循环随机多次,时间消耗太多,做了 太多次不必要的随机。
那么改进的方法就是:将范围设置为最接近a的一个能被b整除的数n,譬如a=120,b=7时,n取b*(a/b) 119
这样只需要return x%b取余的结果即可 等概率输出0到b-1
通过上文分析,我们可以得到步骤如下:
-
如果a > b,进入步骤2;否则构造Randa2 = a * (Randa – 1) + Randa, 表示生成1到a2(a平方) 随机数的函数。
-
如果a2 仍小于b,继教构造 Randa3 = a * (Randa2 – 1) + Randa…直到ak > b a的k次方大于b,这时我们得到Randak , 我们记为RandA。
-
步骤1中我们得到了RandA(可能是Randa或Randak ),其中A > b, 我们用下述代码构造Randb:
// A > b
int Randb(){
int x = ~(1<<31); // max int
while(x > b*(A/b)) // b*(A/b)表示最接近A且小于A的b的倍数
x = RandA();
return x%b ;
}
从上面一系列的分析可以发现,如果给你两个生成随机数的函数Randa和Randb, 你可以通过以下方式轻松构造Randab,生成1到a*b的随机数。
-
Randab = b * (Randa - 1) + Randb
-
Randab = a * (Randb - 1) + Randa
-
这是因为组合成1-a*b结果是等概率的
另一个问题:给定一个randA函数可以随机生成0-A之间的数,去实现一个randBC(随机生成B-C的数):
问题可以转换为 生成一个rand(c-b)的函数,因为rand(c-b)+b 即可等概率表示b到c之间的数
如果再一般化一下,我们还可以把问题变成:给你一个随机生成a到b的函数, 用它去实现一个随机生成c到d的函数。
问题可以转换成已知一个rand(b-a)的函数,生成一个rand(d-c)的函数
因为randab()=rand(b-a)+a ; randcd()=rand(d-c)+c