“n *(rand()/ RAND_MAX)”是否会产生偏斜的随机数分布?

时间:2012-04-18 23:03:27

标签: c random numbers skew

我想找到一种在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次。

1 个答案:

答案 0 :(得分:16)

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

如果您将数字从0到RAND_MAX,并尝试将它们分成10堆,那么您实际上只有三种可能性:

  1. RAND_MAX是10的倍数,并且堆积均匀。
  2. RAND_MAX不是10的倍数,并且桩不均匀。
  3. 你把它分成不均匀的组开始,但扔掉所有会使它变得不均匀的“额外”。
  4. 你很少能控制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中的每个桶中得到100个,在桶10中得到0。如果我注释掉do / while循环,我会在0中看到100。桶9中的.9和9。

    为了确保,我已经使用各种其他数字重新运行测试,包括产生的范围(主要使用的素数)和桶的数量。到目前为止,我无法让它产生任何范围的偏差结果(当然,只要启用do / while循环)。

    另一个细节:我在这个算法中使用除法而不是余数是有原因的。有了rand()的良好(甚至是不错的)实现它是无关紧要的,当你使用除法将数字钳位到一个范围时,你保留了位输入。使用余数执行此操作时,保留输入的较低位。实际上,对于典型的线性同余伪随机数发生器,较低位往往比高位更不随机。一个合理的实现将抛出一些最不重要的位,使这无关紧要。另一方面,rand周围存在一些非常差的实现,并且使用大多数,通过使用除法而不是余数,最终得到更好的输出质量。

    我还应该指出生成器大致相反 - 低位比高位更随机。至少根据我的经验,这些都是非常罕见的。高位更随机的是相当更常见。