build议初始化srand的方法?

我需要一个“好”的方法来初始化C ++中的伪随机数生成器。 我find一篇文章说:

为了生成随机数字,srand通常被初始化为一些与众不同的值,如与执行时间相关的值。 例如,函数time(在头ctime中声明)返回的值每秒都不相同,这对于大多数随机需求来说足够独特。

Unixtime对于我的应用程序来说不够独特。 有什么更好的方法来初始化这个? 奖励点,如果它是可移植的,但代码将主要在Linux主机上运行。

我正在考虑做一些pid / unixtimemath来获得int,或者可能从/dev/urandom读取数据。

谢谢!

编辑

是的,我实际上是每秒多次启动我的应用程序,而且碰到了碰撞。

最好的答案是使用Boost随机数的东西。 或者,如果您有权访问C ++ 11,请使用<random>标题。

但是如果我们谈论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. } 

这是我用于可以频繁运行的小命令行程序(每秒多次):

 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=ab; a=ac; a=a^(c >> 13); b=bc; b=ba; b=b^(a << 8); c=ca; c=cb; c=c^(b >> 13); a=ab; a=ac; a=a^(c >> 12); b=bc; b=ba; b=b^(a << 16); c=ca; c=cb; c=c^(b >> 5); a=ab; a=ac; a=a^(c >> 3); b=bc; b=ba; b=b^(a << 10); c=ca; c=cb; c=c^(b >> 15); return c; } 

如果你需要一个更好的随机数发生器,不要使用libc rand。 而是直接使用像/dev/random/dev/urandom这样的东西(直接从它里面读入int )。

libc rand的唯一真正好处是给定一个种子,这是可预测的,有助于debugging。

最好的方法是使用另一个伪随机数生成器。 Mersenne Twister(和Wichmann-Hill)是我的推荐。

http://en.wikipedia.org/wiki/Mersenne_twister

在Windows上:

 srand(GetTickCount()); 

提供比time()更好的种子,因为它在几毫秒内。

我build议你在mozilla代码中看到unix_random.c文件。 (猜测它是mozilla / security / freebl / …)它应该在freebl库中。

它使用系统调用信息(如pwd,netstat ….)为随机数生成噪声;它被写入支持大多数平台(可以获得我的奖励点数:D)。

你必须问自己的真正问题是你需要的随机性质量。

libc随机是一个LCG

无论您提供什么样的input,随机性的质量都会很低。

如果你只是需要确保不同的实例将有不同的初始化,你可以混合进程ID(getpid),线程ID和计时器。 将结果与xor混合。 对于大多数应用来说,熵应该是足够的。

例如:

 struct timeb tp; ftime(&tp); srand(static_cast<unsigned int>(getpid()) ^ static_cast<unsigned int>(pthread_self()) ^ static_cast<unsigned int >(tp.millitm)); 

为了获得更好的随机质量,请使用/ dev / urandom。 你可以使用boost :: thread和boost :: date_time来使上面的代码变得可移植。

C ++ 11 random_device

如果你需要合理的质量,那么你不应该在第一时间使用rand()。 你应该使用<random>库。 它提供了许多伟大的function,如不同的质量/尺寸/性能权衡,重入性和预定义的分布的各种引擎,所以你不会最终导致他们错误。 它甚至可以提供对非确定性随机数据(例如,/ dev / random)的简单访问,具体取决于您的实现。

 #include <random> #include <iostream> 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对其进行种子处理,在任何体面的实现中,它都是一个非确定性的RNG,而seed_seq是将32位以上的随机数据组合在一起的。 例如在libc ++中,random_device默认访问/ dev / urandom(尽pipe你可以给它另外一个文件来访问)。

接下来,我们创build一个分布,给定一个随机性来源,重复调用分布将产生一个从1到100的均匀分布。然后我们继续反复使用分布并打印结果。

乔纳森·赖特(Jonathan Wright)顶级投票的c++11版本:

 #include <ctime> #include <random> #include <thread> ... const auto time_seed = static_cast<size_t>(std::time(0)); const auto clock_seed = static_cast<size_t>(std::clock()); const size_t pid_seed = std::hash<std::thread::id>()(std::this_thread::get_id()); std::seed_seq seed_value { time_seed, clock_seed, pid_seed }; ... // Eg seeding an engine with the above seed. std::mt19937 gen; gen.seed(seed_value); 
 #include <stdio.h> #include <sys/time.h> main() { struct timeval tv; gettimeofday(&tv,NULL); printf("%d\n", tv.tv_usec); return 0; } 

tv.tv_usec以微秒为单位。 这应该是可以接受的种子。

假设你有一个像这样的签名的函数:

 int foo(char *p); 

随机种子的一个很好的熵源是以下的哈希值:

  • clock_gettime (秒和纳秒)的完整结果不会丢掉低位 – 它们是最有价值的。
  • p的值, uintptr_tuintptr_t
  • p的地址, uintptr_tuintptr_t

至less第三个,也可能是第二个从系统的ASLR派生熵(如果可用的话)(初始堆栈地址,因此当前堆栈地址有点随机)。

我也会避免完全使用rand / srand ,这是为了不触及全局状态,所以您可以更好地控制所使用的PRNG。 但是,不pipe你使用什么PRNG,上面的过程都是一个很好的(也是相当便携的)方法,可以在没有太多工作的情况下获得体面的熵。

对于那些使用Visual Studio的人来说,还有另一种方式:

 #include "stdafx.h" #include <time.h> #include <windows.h> const __int64 DELTA_EPOCH_IN_MICROSECS= 11644473600000000; struct timezone2 { __int32 tz_minuteswest; /* minutes W of Greenwich */ bool tz_dsttime; /* type of dst correction */ }; struct timeval2 { __int32 tv_sec; /* seconds */ __int32 tv_usec; /* microseconds */ }; int gettimeofday(struct timeval2 *tv/*in*/, struct timezone2 *tz/*in*/) { FILETIME ft; __int64 tmpres = 0; TIME_ZONE_INFORMATION tz_winapi; int rez = 0; ZeroMemory(&ft, sizeof(ft)); ZeroMemory(&tz_winapi, sizeof(tz_winapi)); GetSystemTimeAsFileTime(&ft); tmpres = ft.dwHighDateTime; tmpres <<= 32; tmpres |= ft.dwLowDateTime; /*converting file time to unix epoch*/ tmpres /= 10; /*convert into microseconds*/ tmpres -= DELTA_EPOCH_IN_MICROSECS; tv->tv_sec = (__int32)(tmpres * 0.000001); tv->tv_usec = (tmpres % 1000000); //_tzset(),don't work properly, so we use GetTimeZoneInformation rez = GetTimeZoneInformation(&tz_winapi); tz->tz_dsttime = (rez == 2) ? true : false; tz->tz_minuteswest = tz_winapi.Bias + ((rez == 2) ? tz_winapi.DaylightBias : 0); return 0; } int main(int argc, char** argv) { struct timeval2 tv; struct timezone2 tz; ZeroMemory(&tv, sizeof(tv)); ZeroMemory(&tz, sizeof(tz)); gettimeofday(&tv, &tz); unsigned long seed = tv.tv_sec ^ (tv.tv_usec << 12); srand(seed); } 

也许有点矫枉过正,但在快速间隔时效果很好。 gettimeofday函数在这里find。

编辑:进一步调查rand_s可能是一个很好的替代Visual Studio,它不只是一个安全的兰特(),它是完全不同的,不使用srand的种子。 我推测这与兰特“更安全”几乎完全相同。

要使用rand_s,不要忘记在包含stdlib.h之前#define _CRT_RAND_S。

将标题包含在程序的顶部,并写下:

 srand(time(NULL)); 

在你的程序中,在你声明你的随机数之前。 下面是打印1到10之间的随机数的程序示例:

 #include <iostream> #include <iomanip> using namespace std; int main() { //Initialize srand srand(time(NULL)); //Create random number int n = rand() % 10 + 1; //Print the number cout << n << endl; //End the line //The main function is an int, so it must return a value return 0; }