均匀分布的随机数生成

时间:2013-02-04 15:28:31

标签: c++ math

为什么此代码会生成均匀分布的数字?我理解它有些困难。有人能解释一下吗感谢。

int RandomUniform(int n) {  
  int top = ((((RAND_MAX - n) + 1) / n) * n - 1) + n;  
  int r;  
  do {  
    r = rand();  
  } while (r > top);  
  return (r % n);  
}

更新:我明白为什么rand()%n不会给你一个均匀分布的序列。我的问题是为什么

top = ((((RAND_MAX - n) + 1) / n) * n - 1) + n;

这里有什么问题?我认为一个简单的顶部= RAND_MAX / n * n就可以了。

3 个答案:

答案 0 :(得分:10)

该函数假定rand()均匀分布;这是否是一个有效的假设取决于rand()的实现。

给定统一rand(),我们可以通过计算[0,n)得到rand()%n范围内的随机数。但是,一般来说,这不会很均匀。例如,假设n为3而RAND_MAX为7:

rand()      0 1 2 3 4 5 6 7
rand() % n  0 1 2 0 1 2 0 1

我们可以看到0和1的概率为3/8,而2只出现2/8的概率:分布不均匀。

您的代码会丢弃rand()的任何值,该值大于或等于它可以生成的n的最大倍数。现在每个值的概率相等:

rand()      0 1 2 3 4 5 6 7
rand() % n  0 1 2 0 1 2 X X

所以0,1和2都有1/3的概率,只要我们不那么不幸,循环永远不会终止。

关于您的更新:

  

我认为一个简单的顶部= RAND_MAX / n * n就可以了。

如果RAND_MAX是一个独占界限(比实际最大值多一个),那么这是正确的。由于它是一个包容性的界限,我们需要添加一个来获得独占界限;并且由于以下逻辑与包含边界的>进行比较,因此在计算后再次减去一个:

int top = ((RAND_MAX + 1) / n) * n - 1;

但是,如果RAND_MAX等于INT_MAX,则计算会溢出;为避免这种情况,请在计算开始时减去n,并在结尾处再次添加:

int top = (((RAND_MAX - n) + 1) / n) * n - 1 + n;

答案 1 :(得分:7)

基本问题是:假设您有一个随机数生成器my_rand(),它生成0到6(含)的值,并且您希望生成0到5之间的值,包括0和5;如果您运行生成器并返回my_rand() % 6,则无法获得统一分布。当my_rand()返回0时,您得到0;当它返回1时,你得到1,等等,直到my_rand()返回6;在这种情况下my_rand() % 6为0.总的来说,my_rand() % 6将返回0的频率是任何其他值的两倍。解决此问题的方法是不使用大于5的值,即代替my_rand() % 5编写循环并丢弃my_rand()中过大的值。这基本上就是问题中的代码所做的事情。我没有追踪它,但通常的实现是计算n的最大倍数小于或等于RAND_MAX,并且只要rand()返回的值大于{{1}}多重,返回并获得新值。

答案 2 :(得分:2)

我没有追踪计算top的代码,但RAND_MAXrand()可以返回的最大值; (RAND_MAX + 1) / n * n会有更好的上限,但如果RAND_MAXINT_MAX,则结果将无法预测。所以也许所有代码都试图避免溢出。