为啥分叉后 rand() 不那么随机?

Posted

技术标签:

【中文标题】为啥分叉后 rand() 不那么随机?【英文标题】:Why is rand() not so random after fork?为什么分叉后 rand() 不那么随机? 【发布时间】:2012-01-27 05:32:56 【问题描述】:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() 
    int i =10;
    /* initialize random seed:  */
    srand(time(NULL));
    while(i--)
        if(fork()==0)
            /* initialize random seed here does not make a difference:
            srand(time(NULL));
             */
            printf("%d : %d\n",i,rand());
            return;
        
    
    return (EXIT_SUCCESS);

打印相同(每次运行不同)数字 10 次 - 预期? 我有一段更复杂的代码,每个分叉的进程依次运行 - 没有区别

【问题讨论】:

非常感谢您的回答 - 现在清楚了 :) 【参考方案1】:

这样就解决了问题:

srand48((long int)time(NULL));
i= (lrand48()/rand()+1) % 123

我没有用 fork 测试过,但是在 for 调用 100 次里面它可以工作。

带有 pid 号的种子。解决起来有点困难。

这是在某个页面中:“这有效 srand(time(0)+getpid()); 但我必须在案例 0 中调用它,即子进程”。

【讨论】:

我听不懂你的英文【参考方案2】:

输出必须相同。如果两个进程每个都用相同的种子播种随机数并且每个调用一次rand,它们必须得到相同的结果。这就是拥有种子的全部意义所在。您的所有进程都使用相同的种子调用srand(因为您只调用一次srand)并且它们都调用一次rand,因此它们必须得到相同的结果。

取消注释 srand 不会产生影响,因为除非秒数发生变化,否则它们仍会给出相同的种子。你可以这样做:

srand(time(NULL) ^ (getpid()<<16));

【讨论】:

您介意简要解释一下 ^ (getpid()&lt;&lt;16) 的幕后情况吗? time(NULL) 确保您每秒获得不同的值。 (getpid()&lt;&lt;16) 增加了您为每个进程获得不同值的几率,因为进程 ID 通常不会那么快地重用。 我得到了那部分先生,我的意思是为什么使用 XOR 运算而不是 & 或 |?我在那个(数学)部分。 @snr XOR 确保任何一方的任何更改都会更改结果。 8&1 与 8&2 相同,所以 & 被淘汰。 9|1 与 9|9 相同,所以 |出去了。【参考方案3】:

即使在循环中添加srand(time(NULL));(您已评论的if 块内的行)也没有什么不同,因为现代计算机可以非常快速地执行整个块,并且time以秒计。从手册页:

time() 以自 Epoch 以来的秒数返回时间...

如果您在while 循环中的if 语句之后添加sleep(1); 并取消注释srand 调用,结果将会不同,因为time 现在将返回不同的值,因为第二个有已过。

然而,使用不同的种子值比等待更合适。像i 这样的东西会是个好主意,因为它对于循环的每次迭代都是唯一的。

【讨论】:

【参考方案4】:

当你创建一个子进程时,你并没有重新播种。随机数发生器的状态完全一样。

即使您再次在孩子身上播种,您也会以 +/- 1 秒的粒度播种时间。当你分叉时,这一切都会在不到一秒钟的时间内发生。

尝试用不同且更随机的东西来播种它。

【讨论】:

【参考方案5】:

如果您的代码运行得足够快,srand() 可能会在每个分叉的时间完全相同。 time() 每秒都在变化。

【讨论】:

【参考方案6】:

这是因为所有程序都使用相同的值播种(在该 while 循环之外)。一旦你分叉了新程序,你应该再次播种,否则两者都会产生相同的序列。

【讨论】:

【参考方案7】:

rand() 函数是一个伪随机数字生成器。这意味着生成的数字序列是确定性的,仅取决于提供的种子。

因为您将同一个进程分叉 10 次,所以随机数生成器的状态对于每个子进程都是相同的。下次您调用rand() 时,您将获得相同的值。

通过在子进程中调用srand(time(NULL)),您可能提供帮助,但time() 的粒度仅为1 秒,因此您的所有子进程可能在同一秒内开始。使用相同的值播种会生成相同的伪随机序列。

您可以尝试使用取决于子编号的值进行播种:

srand(time(NULL) - i*2);

(如果 time() 在 fork 循环期间提前 1 秒,我使用了 i*2。)

【讨论】:

总体来说是个好主意,但明智的做法是以更重要的方式合并子编号。 time(NULL) + 100 * i 或类似的东西,以确保一两秒的差异不会影响种子。 好点,我喜欢 David Schwartz 使用子 pid 的想法。 是的,我真的很喜欢他在 high 中混合它的方式,但是对于使用 clone(2) 而不是 fork(2) 的应用程序,它们都可以pid 具有相同的值,但 i 的值不同。可能对任何人都无关紧要的细微差别...... pid 的好处是即使运行速度非常快,结果也不会相同

以上是关于为啥分叉后 rand() 不那么随机?的主要内容,如果未能解决你的问题,请参考以下文章

为啥新的随机库比 std::rand() 更好?

为啥 rand() 不是真正随机的?

为啥我总是用 rand() 得到相同的随机数序列?

为啥我总是用 rand() 得到相同的随机数序列?

关于PHP中array_rand函数为啥不能只设置一个随机?

rand产生随机数怎样控制在1~52内而且不能重复。1~52必须出现一次。谢谢