如何播种srand()以避免在大量机器上发生冲突?

时间:2014-01-09 12:34:23

标签: c random prng

通常,srand()的播种是通过以下方式完成的:

srand(time(NULL));

就我而言,我使用随机数在网络上的运行时为我的客户端进程生成一个标识符。该过程有时会重新启动并生成新标识符。随着客户端数量的增加,两个客户端很可能在同一秒内调用srand(time(NULL)),这会创建两个相同的标识符,或者服务器端看到的冲突。 Some people suggested a finer resolution

srand((time.tv_sec * 1000) + (time.tv_usec / 1000));

但这里的问题是种子会每24天左右重复一次,当机器数量足够大时,仍有可能发生碰撞。 There's another solution

srand(time.tv_usec * time.tv_sec);

但这对我来说似乎也有问题,因为该产品的模数(较高位溢出并被放弃)不均匀地分布在unsigned int种子值的范围内。例如,对于每一秒,time.tv_usec == 0会导致相同的种子。

那么有没有办法在我的案例中播种srand()?

编辑:客户端在Linux,Windows,Android和iOS上运行,因此/dev/random/dev/urandom并非始终可用。

P.S。我知道GUID / UUID方法,但我想知道在这种情况下是否可以正确地为srand()播种。

5 个答案:

答案 0 :(得分:2)

如果你有很多进程或线程需要在它们之间独立的伪随机性,那么

srandrand根本不合适。

在POSIX系统上,您可以使用已知状态大小的rand48函数系列,如jrand48。如果依赖于进程,线程和机器独立性,则应使用进程ID,线程ID,IP地址和时间中的有效位来初始化状态。 jrand[48]采用(并修改)三个short的状态,因此使用不同的量种子进行种子化应该相对简单。

你列出的所有系统都是POSIX,所以这应该在那里工作。对于Windows系统来说,什么是适当的后备,我不知道。

答案 1 :(得分:2)

如果两个随机数发生器从不发生碰撞,则它们不是随机的。这就像玩'快照',但永远不会得到匹配。

所以,你想要的是更糟糕的,而不是更好的随机数生成器。使用GUID确实是一种应该完全消除问题的方法。

但是,如果你很高兴只是减少碰撞的机会,而不是完全消除它们,你可以使用机器的IP地址(或处理器序列号,或某些)作为种子的一部分。

答案 2 :(得分:1)

如果您是为linux平台编写的,请使用/dev/random/dev/urandom获取随机数。与srand()不同,/dev/random/dev/urandom使用硬件外设生成的噪声来生成随机数。 srand()使用作为参数提供的种子来生成随机数。

答案 3 :(得分:0)

您有两个域:客户端和进程。因此,每个都需要一个唯一的标识符。显然可以使用process-id完成进程。对于客户端,我建议使用MAC地址,每个网络接口必须是唯一的。我相信您列出的所有平台都支持套接字,因此可能支持SIOCGIFHWADDR ioctl。

唯一的问题是MAC地址是48位,PID通常是32位,因此您必须选择两个值的最高熵位用于您的srand种子。我建议PID的低16位和MAC地址的低16位。

答案 4 :(得分:0)

我想你会对“并行随机数:像1,2,3一样简单”这篇论文感兴趣。

引自摘要:“我们所有的PRNG都通过了严格的统计测试(包括TestU01的BigCrush),并产生至少264个独特的随机数并行流,每个流的周期为2128或更多”