我需要一种'好'的方法来初始化C++中的伪随机数生成器.我发现一篇文章指出:
为了生成类似随机数的数字,srand通常被初始化为一些独特的值,就像那些与执行时间相关的值.例如,函数时间返回的值(在标题ctime中声明)每秒都不同,这对于大多数随机需求来说足够独特.
Unixtime对我的应用来说并不是很有特色.有什么更好的方法来初始化它?奖励点是否可移植,但代码主要在Linux主机上运行.
我正在考虑做一些pid/unixtime数学来得到一个int,或者可能从中读取数据/dev/urandom
.
谢谢!
编辑
是的,我实际上是每秒多次启动我的应用程序而且我遇到了碰撞.
这就是我用于可以频繁运行的小命令行程序(每秒多次):
unsigned long seed = mix(clock(), time(NULL), getpid());
混合的地方是:
// http://www.concentric.net/~Ttwang/tech/inthash.htm unsigned long mix(unsigned long a, unsigned long b, unsigned long c) { a=a-b; a=a-c; a=a^(c >> 13); b=b-c; b=b-a; b=b^(a << 8); c=c-a; c=c-b; c=c^(b >> 13); a=a-b; a=a-c; a=a^(c >> 12); b=b-c; b=b-a; b=b^(a << 16); c=c-a; c=c-b; c=c^(b >> 5); a=a-b; a=a-c; a=a^(c >> 3); b=b-c; b=b-a; b=b^(a << 10); c=c-a; c=c-b; c=c^(b >> 15); return c; }
最好的答案是使用Boost随机数的东西.或者,如果您有权访问C++ 11,请使用
标头.
但是如果我们谈论的话rand()
,srand()
最好的方法就是使用time()
:
int main() { srand(time(NULL)); ... }
一定要在程序开始时这样做,而不是每次打电话都这样做rand()
!
每次启动时,time()都将返回一个唯一值(除非您每秒多次启动应用程序).在32位系统中,它只会每60年重复一次.
我知道你不认为时间足够独特但我觉得很难相信.但我知道这是错的.
如果您同时启动大量应用程序副本,则可以使用具有更高分辨率的计时器.但是在价值重复之前,你会冒更短时间的风险.
好的,所以如果你真的认为你一秒钟就开始了多个应用程序.
然后在计时器上使用更精细的颗粒.
int main() { struct timeval time; gettimeofday(&time,NULL); // microsecond has 1 000 000 // Assuming you did not need quite that accuracy // Also do not assume the system clock has that accuracy. srand((time.tv_sec * 1000) + (time.tv_usec / 1000)); // The trouble here is that the seed will repeat every // 24 days or so. // If you use 100 (rather than 1000) the seed repeats every 248 days. // Do not make the MISTAKE of using just the tv_usec // This will mean your seed repeats every second. }
如果你需要一个更好的随机数生成器,不要使用libc rand.而只是使用类似/dev/random
或/dev/urandom
直接的东西(int
直接从它或类似的东西读取).
libc rand唯一真正的好处是给定一个种子,它是可预测的,有助于调试.
在Windows上:
srand(GetTickCount());
提供比time()
自毫秒以来更好的种子.
C++ 11 random_device
如果你需要合理的质量,那么你不应该首先使用rand(); 你应该使用
图书馆.它提供了许多强大的功能,例如各种引擎,用于不同的质量/尺寸/性能权衡,重新进入和预定义的分发,因此您不会最终弄错它们.它甚至可以提供对非确定性随机数据的轻松访问(例如,/ dev/random),具体取决于您的实现.
#include#include int main() { std::random_device r; std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()}; std::mt19937 eng(seed); std::uniform_int_distribution<> dist{1,100}; for (int i=0; i<50; ++i) std::cout << dist(eng) << '\n'; }
eng
是随机性的来源,这里是mersenne twister的内置实现.我们使用random_device对其进行播种,在任何体面的实现中,random_device都是非威慑性的RNG,而seed_seq用于组合超过32位的随机数据.例如,在libc ++中,random_device默认访问/ dev/urandom(尽管你可以给它另一个文件来访问).
接下来,我们创建一个分布,这样,给定一个随机源,重复调用分布将产生从1到100的整数均匀分布.然后我们重复使用分布并打印结果.
最好的方法是使用另一个伪随机数生成器.Mersenne twister(和Wichmann-Hill)是我的推荐.
http://en.wikipedia.org/wiki/Mersenne_twister
我建议你在mozilla代码中看到unix_random.c文件.(猜它是mozilla/security/freebl/...)它应该在freebl库中.
它使用系统调用信息(如pwd,netstat ....)来为随机数生成噪声;它被编写为支持大多数平台(可以获得奖励积分:D).
您必须问自己的真正问题是您需要的随机性质量.
libc random是一个LCG
无论您提供什么样的输入,随机性的质量都会很低.
如果您只需要确保不同的实例具有不同的初始化,则可以混合进程ID(getpid),线程ID和计时器.将结果与xor混合.对于大多数应用来说,熵应该足够了.
示例:
struct timeb tp; ftime(&tp); srand(static_cast(getpid()) ^ static_cast (pthread_self()) ^ static_cast (tp.millitm));
为了获得更好的随机质量,请使用/ dev/urandom.您可以使用boost :: thread和boost :: date_time使上述代码可移植.