【问题标题】:calling rand() returning non-random results调用 rand() 返回非随机结果
【发布时间】:2012-03-13 14:40:59
【问题描述】:

我正在编写一个简单的 C 程序,它掷硬币 100000 次,并使用 srand 和 rand 计算掷硬币的正面数和反面数。这是我所拥有的:

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

int main(void){

    int i;
    int h=0;
    int t=0;
    int n;

    for(i=0;i<100000;i++){
        srand(time(NULL));
        n=rand()%2;
        if(n==0){
            h++;
        }
        else {   
            t++;
        }
    }

    printf("%d heads\n", h);
    printf("%d tails\n", t);

    return EXIT_SUCCESS;
}

但是,在运行程序时,有时我会得到 100000 个正面和 0 个反面,有时是 0 个正面和 100000 个反面,有时我会得到正面和反面的随机数,但无论如何都不是 50% 正面和 50% 反面。

我错过了什么吗? 谢谢!

【问题讨论】:

    标签: c


    【解决方案1】:

    您每次都在为伪随机数生成器播种相同的种子。只需将调用移至循环外的srand

    srand(time(NULL));
    for(i=0;i<100000;i++){
        n=rand()%2;
        .....
    

    pseudo-random number generator (PRNG) 是确定性的。也就是说,给定相同的初始种子,每次调用它都会返回相同的数字序列。在您的代码中,由于您每次调用 rand 时都会播种,因此每次循环迭代都会获得相同的 1 数字序列。现在,程序的不同执行会有不同的结果,因为time 会发生变化。

    在您没有看到 100% 正面或 100% 反面的情况下,这就是循环需要足够长的时间才能推进的情况。

    【讨论】:

    • 感谢您的回答,但是您能解释一下为什么会发生这种情况吗?我认为将 srand 放在循环之外只会在整个循环执行期间为数字生成器播种一次。
    • @user1249740 你只希望它被播种一次。我在答案中添加了一些解释。
    • 这是正确的 - 我要补充说明,结果中观察到的可变性来自代码执行的速度和 time() 函数的分辨率。代码执行得如此之快,以至于许多迭代从系统时间接收到相同的“tick”值作为 PRNG 的种子
    • @sgorozco 是的,这就是我在最后一段中试图传达的内容。
    • @DavidHeffernan 抱歉,我想我错过了你的最后一段。
    【解决方案2】:

    是的,如果您使用 srand,它会使用时间随机获取一个数字,因此如果您或计算机在不到一秒的时间内随机选择一个值,则该数字每次都会出现相同的数字。

    【讨论】:

    • 明确指出 srand 通常只在程序开始时调用一次
    【解决方案3】:

    我认为问题在于每次迭代都调用srand();你只需要调用一次。

    试试这个版本:

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    int main(void){
    
    
    int i;
    int h=0;
    int t=0;
    int n;
    
    srand(time(NULL));
    for(i=0;i<100000;i++){
        if((rand() & 1) == 1){
            h++;
        }
        else{
            t++;
        }
    }
    
    printf("%d heads\n", h);
    printf("%d tails\n", t);
    
    return EXIT_SUCCESS;
    }
    

    这给出了相当一致的结果:

    $ ./rand
    49980 heads
    50020 tails
    $ ./rand
    50131 heads
    49869 tails
    $ ./rand
    50000 heads
    50000 tails
    $ ./rand
    50000 heads
    50000 tails
    $ ./rand
    50093 heads
    49907 tails
    $ ./rand
    50093 heads
    49907 tails
    

    编辑:重复我之前关于rand() 的评论——它不会返回负值。

    【讨论】:

    • cplusplus.com/reference/clibrary/cstdlib/rand - rand() 不会返回低于 0 的值。
    • @PawelZubrycki 是的,我做了一个快速的man rand,看到它返回了一个(签名的)int,并决定它可以返回负数,将它与可以返回负数的 Java Random.nextInt() 混淆。 &amp; 1 必须比模快。
    【解决方案4】:

    不要在循环中调用srand。如果您要调用它,它应该只在执行开始时调用一次。

    实际上,您在每次使用随机数生成器之前将其重置为相同的种子(除非第二个边界通过)...所以当它得出一致的结果时应该不足为奇!

    【讨论】:

      【解决方案5】:

      您在循环的每次迭代中使用相同的种子值。

      srand(time(NULL)); 移到 for 循环之前。

      【讨论】:

        【解决方案6】:

        是的。都是关于这一行的:

        srand(time(NULL));
        

        将它移到for 循环之前,它将起作用。 srand 设置种子,所以如果你这样称呼它,time(NULL) 不会改变(而且很有可能) rand() 将始终返回相同的值。

        【讨论】:

          【解决方案7】:

          复制到srand(time(NULL)) doesn't change seed value quick enough。 是的,如上所述,移动

          srand(time(NULL)); 
          

          在循环之外(之前;)) :)

          【讨论】:

            【解决方案8】:

            将 srand 放在 for 循环之外。只调用一次 srand(time(null) 。

            【讨论】:

              猜你喜欢
              • 2013-05-31
              • 1970-01-01
              • 1970-01-01
              • 2010-11-23
              • 2015-01-14
              • 1970-01-01
              • 1970-01-01
              • 2021-06-25
              • 2017-12-27
              相关资源
              最近更新 更多