1。关于time()
time(或更好的 C++ 中的std::time)是一个函数,它返回以某种方式表示当前时间的整数或浮点数。
未指定它实际返回的算术类型以及它如何表示当前时间,但是,最常见的情况是,您会得到一些整数类型,其中包含自 Unix 纪元开始以来的秒数。
2。关于srand()
srand 是一个函数,它使用其参数(类型为unsigned int),即所谓的seed,来设置伪数生成器rand 的内部状态。当我在这个答案的其余部分写 random 时,请阅读 pseudo random。
使用不同的种子通常会导致后续调用 rand 时产生不同的随机数序列,而再次使用相同的种子会产生完全相同的随机数序列。
3。使用time() 播种rand()
如果我们不想每次运行程序都得到相同的随机数,我们需要一些每次运行都不同的种子。当前时间是此类种子的广泛使用的来源,因为它不断变化。
这个表示当前时间的整数(或返回的任何其他 time)现在转换为带有 static_cast 的 unsigned int。实际上并不需要这种显式转换,因为所有算术类型都会隐式转换为 unsigned int,但这种转换可能会使一些警告静音。随着时间的推移,我们可以预期得到的unsigned int 以及rand 产生的随机数序列会发生变化。
4。陷阱
如果time 通常返回自 Unix 纪元开始以来的秒数,则需要注意三个重要事项:
- 只有在两次调用之间至少经过一秒钟时,您生成的序列才会有所不同。
- 根据实际实现,如果用于播种
rand 的时间点彼此接近(与纪元以来的时间相比),则生成的序列可能会开始有点相似。 Afaik,MSVC 的实现就是这种情况。 如果这有问题,只需丢弃序列的前几百或几千个值。(据我所知,这对于通常用于rand 的不良 RNG 并没有太大帮助. 因此,如果这有问题,请使用<random>,如下所述。)
- 你的号码最终不是很随机:如果有人知道你何时拨打
srand,他们可以从中推导出整个随机数序列。这实际上导致了一种用于勒索软件的解密工具,该工具使用srand(time(0)) 生成其“随机”加密密钥。
此外,即使种子很好,rand 生成的序列也往往具有较差的统计特性。对于像您这样的玩具程序,这可能没问题,但是,对于现实世界的使用,人们应该意识到这一点。
5。新的<random>
C++11 引入了新的随机数功能,在很多方面都优于基于 rand 的旧东西。它们在标准标题<random> 中提供。它包括std::random_device,它提供了一种获取实际随机种子的方法、强大的伪随机数生成器(如std::mt19937)以及将生成的随机序列映射到整数或浮点范围而不引入不必要的偏差的工具。
这是一个如何在 C++11 中随机掷骰子的示例:
#include <random>
#include <iostream>
int main()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(1, 6);
for (int n=0; n<10; ++n)
std::cout << dis(gen) << ' ';
std::cout << '\n';
}
(来自cppr的代码)注意: std::random_device不能与MinGW正常工作,至少在我测试的版本(Nuwen MinGW5.3)中!
还应该注意的是,mt19937 的状态空间比我们(通常)通过一次调用 random_device 得到的 32 位空间大得多。同样,这对于玩具程序和家庭作业很可能无关紧要,但仅供参考:Here 是我尝试正确播种整个状态空间,并在答案中加上一些有用的建议。
如果您对rand 与<random> 的更多细节感兴趣,this 是一款有趣的手表。