【问题标题】:Does "n * (rand() / RAND_MAX)" make a skewed random number distribution?“n * (rand() / RAND_MAX)” 是否会产生偏斜的随机数分布?
【发布时间】:2012-04-18 23:03:27
【问题描述】:

我想找到一种在 C 中获取随机数的不偏不倚的方法(尽管我最多将它用于 0-20 的值,更可能只用于 0-8)。我看过这个公式,但是在运行了一些测试之后,我不确定它是否有偏差。有什么帮助吗?

这是使用的完整功能:

int randNum() 
{ 
    return 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));
}

我使用以下方法播种:

unsigned int iseed = (unsigned int)time(NULL);
srand (iseed);

我尝试过下面建议的那个拒绝为我工作

int greek; 
for (j=0; j<50000; j++) 
{ 
greek =rand_lim(5); 
printf("%d, " greek); 
greek =(int) (NUM * (rand() / (RAND_MAX + 1.0))); 
int togo=number[greek]; 
number[greek]=togo+1; 
}

当我注释掉 printf 时,它停止工作并给我同样的数字 50000 次。

【问题讨论】:

  • 不应该RAND_MAX + 1.0 只是RAND_MAX
  • @EdHeal,如果您希望间隔为 [0.0,1.0)(即不包括 1.0),则必须除以 RAND_MAX 以上,尽管我认为小于 1.0 的值会更好。
  • @MarkRansom - 我知道。该问题要求 0-20 范围内的值。所以公式只需要除以 RAND_MAX,因为它没有提到范围不排除 20。
  • @EdHeal,如果您乘以 20.999999999999 而不是 20,您的方法将起作用。否则输出 20 的几率是 1/RAND_MAX,这与您可以得到的偏差差不多。
  • 当然它总是给你相同的值,因为你一直调用未显示的 rand_lim(5),而不是 rand()。

标签: c random numbers skew


【解决方案1】:

是的,它是倾斜的,除非您的 RAND_MAX 恰好是 10 的倍数。

如果你取从 0 到 RAND_MAX 的数字,并尝试将它们分成 10 堆,你真的只有三种可能:

  1. RAND_MAX 是 10 的倍数,堆出来的都是偶数。
  2. RAND_MAX 不是 10 的倍数,堆出来的东西不均匀。
  3. 一开始您将其分成不均匀的组,但丢弃所有可能使其不均匀的“额外”。

你很少能控制 RAND_MAX,而且它通常是一个素数。这真的只剩下 2 和 3 的可能性。

第三个选项大致如下: [编辑:经过一番思考,我修改了这个以产生范围 0...(limit-1) 的数字,以适应 C 和 C++ 中大多数事情的工作方式。这也简化了代码(一点点)。

int rand_lim(int limit) {
/* return a random number in the range [0..limit)
 */

    int divisor = RAND_MAX/limit;
    int retval;

    do { 
        retval = rand() / divisor;
    } while (retval == limit);

    return retval;
}

对于任何质疑这种方法是否会产生偏差的人,我还编写了一个完全不同的版本,纯粹用于测试。这个使用了一个范围非常有限的绝对非随机生成器,因此我们可以简单地遍历范围内的每个数字。它看起来像这样:

#include <stdlib.h>
#include <stdio.h>

#define MAX 1009

int next_val() {
    // just return consecutive numbers
    static int v=0;

    return v++;
}

int lim(int limit) {
    int divisor = MAX/limit;
    int retval;

    do {
        retval = next_val() / divisor;
    } while (retval == limit);

    return retval;
}

#define LIMIT 10

int main() {

    // we'll allocate extra space at the end of the array:
    int buckets[LIMIT+2] = {0};
    int i;

    for (i=0; i<MAX; i++)
        ++buckets[lim(LIMIT)];

    // and print one beyond what *should* be generated
    for (i=0; i<LIMIT+1; i++)
        printf("%2d: %d\n", i, buckets[i]);
}

所以,我们从 0 到 1009 的数字开始(1009 是质数,所以它不会是我们选择的任何范围的精确倍数)。因此,我们从 1009 个数字开始,并将其分成 10 个桶。这应该在每个桶中提供 100 个,并且 9 个剩菜(可以这么说)被 do/while 循环“吃掉”。正如它现在所写的那样,它分配并打印出一个额外的桶。当我运行它时,我在每个存储桶 0..9 中得到正好 100,在存储桶 10 中得到 0。如果我注释掉 do/while 循环,我会在 0..9 中的每个存储桶中看到 100,并且9 在 10 号桶中。

为了确定起见,我已经针对生成的范围(主要使用素数)和存储桶的数量使用各种其他数字重新运行了测试。到目前为止,我还不能让它为任何范围产生偏斜的结果(当然,只要启用了do/while 循环)。

另一个细节:我在这个算法中使用除法而不是余数是有原因的。 rand() 的良好(甚至体面)实现是无关紧要的,但是当您使用除法将数字限制在一个范围内时,您会保留输入的 高位。当您使用余数执行此操作时,您会保留输入的 lower 位。碰巧的是,对于典型的线性同余伪随机数生成器,低位的随机性往往低于高位。一个合理的实现会丢弃一些最低有效位,从而使这无关紧要。另一方面,rand 的一些实现非常糟糕,其中大多数,通过使用除法而不是余数,你最终会得到更好的输出质量。

我还应该指出,个生成器的作用大致相反——低位比高位更随机。至少在我的经验中,这些是相当少见的。高位更随机的那个相当更常见。

【讨论】:

  • 我之前试过那个该死的功能,但它拒绝工作。它只是一遍又一遍地给我同样的号码。如果我有 int 希腊语;对于 (j=0; j
  • 哦,是的,我把它变成了一个 while 循环,而不是我认为是一样的 do while 循环。
  • @RobertZ:我在答案中添加了一点演示代码。
  • 如果从0RAND_MAX 的数字有RAND_MAX + 1 数字,那么RAND_MAX + 1 必须是10 的倍数。
  • 一个想法出现了,我不能让它做{retval =rand() } while (retval
猜你喜欢
  • 2015-02-26
  • 1970-01-01
  • 1970-01-01
  • 2014-02-28
  • 2016-07-12
  • 1970-01-01
  • 2023-04-04
  • 2011-06-24
  • 2015-08-29
相关资源
最近更新 更多