我一直在使用random_device rd{}为 Mersenne-Twister 伪随机数生成器生成种子,mt19937 RNG{rd()} 正如此处所建议的那样。然而,文档中写道(文档示例代码中的注释),“random_device一旦熵池耗尽,许多实现的性能都会急剧下降。在实际使用中,random_device通常仅用于播种 PRNG,例如mt19937”。我尝试测试这个“熵池”有多大,对于 10^6 次调用,random_device返回超过 10^2 个重复数字(请参阅下面的示例代码和输出)。换句话说,如果我尝试将random_device其用作 Mersenne-Twister PRNG 的种子,它将生成大量重复种子。
问题:人们是否仍在使用random_deviceC++ 来生成 PRNG 种子,或者是否已经有更好的替代方案?
我的代码:
#include <iostream>
#include <random>
#include <chrono>
using namespace std;
int main(){
auto begin = std::chrono::high_resolution_clock::now();
random_device rd{};
mt19937 RNG{ rd() };
int total_n_of_calls = 1e6;
vector<int> seeds;
for(auto call = 0; call < total_n_of_calls; call++){
int call_rd = rd();
seeds.push_back(call_rd);
}
int count_repeats = 0;
sort(seeds.begin(), seeds.end());
for(int i = 0; i < seeds.size() - 1; i++) {
if (seeds[i] == seeds[i + 1]) {
count_repeats++;
}
}
printf("Number of times random_device have been called: %i\n", total_n_of_calls);
printf("Number of repeats: %i\n", count_repeats);
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin);
printf("Duration: %.3f seconds.\n", elapsed.count() * 1e-9);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
random_device 被调用的次数:1000000
重复次数:111
持续时间:0.594 秒。
TL;DR:不,没有什么比这更好的了。你只需要停止滥用它。
\n要点random_device是,它向您的平台请求实际上是随机的位,而不仅仅是来自某些确定性种子的伪随机位。
如果平台/操作系统认为它所拥有的熵已被消耗,那么它就无法为您提供此服务。但老实说,它使用真正的随机源(从 CPU 中的实际随机性硬件到磁盘访问时间)来修改 PRNG 的内部状态。对于外部人员来说,这就是 \xe2\x80\x93 的全部内容,你得到的位仍然是不可预测的。
\n所以,答案是这样的:
\nrandom_device因为你实际上需要随机种子。没有随机性的算法捷径\xe2\x80\x93“算法”这个词已经表明它是确定性的。一般来说,软件是确定性的,除非它从外部获取随机数据。因此,您所能做的就是询问操作系统,它实际上会处理系统中存在的任何随机源。这已经是事实了random_device。random_device(除非您购买昂贵的专用随机生成器卡并为其编写驱动程序)。事情正是如此:如果您需要在几秒钟之内生成 10\xe2\x81\xb6 个独立的加密安全密钥,那么您就有点特殊了。您是否正在为 CDN 工作,其中单个操作系统每秒可以服务数百万个新的 TLS 连接?如果没有,请将您的使用量减少random_device到实际有用的程度。
如果您想更多地了解真正的随机性在程序中的最终结果,我建议您阅读此答案。简而言之,如果您实际上每秒需要比默认random_device提供更多的随机字节,请尝试使用 ctor 参数来构造它"/dev/urandom"。对于您提出这个问题的上下文中的含义的任何假定定义,它仍然是安全的(这意味着我假设您没有为密钥生成的极高吞吐量编写加密库) )。