通常,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()播种。
答案 0 :(得分:2)
srand
和rand
根本不合适。
在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或更多”