一般返回一个[0,n)的随机数,都会使用rand()%n。rand()函数返回区间为[0,RAND_MAX]中的一个数。RAND_MAX在<cstdlib>中定义的。实际上rand()是一个伪随机函数。并且如果n的值很大,那么RAND_MAX就不会均匀地被n除尽,一些余数出现的概率将会比其他的更大。假定RAND_MAX是32767,且n等于20000。则rand()将会有两个不同的值能令rand()%n等于10000(即10000和30000),但rand()仅有一个值能让rand()%n等于15000(即15000)。所以rand()%20000产生10000的概率是产生15000概率的两倍。
为了避免这些缺陷,我们将使用一种不同的策略。把这些可利用的随机数分成长度精确相等的存储桶。我们能计算一个随机数并返回相应的存储桶的编号。因为存储桶的长度相同,所以某些随机数根本没可能进入到任何的存储桶中。这样的话,我们会反复请求随机数,直到获得一个合适的为止。
为了避免这些缺陷,我们将使用一种不同的策略。把这些可利用的随机数分成长度精确相等的存储桶。我们能计算一个随机数并返回相应的存储桶的编号。因为存储桶的长度相同,所以某些随机数根本没可能进入到任何的存储桶中。这样的话,我们会反复请求随机数,直到获得一个合适的为止。
代码如下:
// return a random integer in the range `[0,' `n)'
int nrand(int n)
{
if (n <= 0 || n > RAND_MAX)
throw domain_error("Argument to nrand is out of range");
const int bucket_size = RAND_MAX / n;
int r;
do r = rand() / bucket_size;
while (r >= n);
return r;
}