我目前的C++11项目需要执行M次模拟。对于每个模拟 m = 1, ..., M
,我使用一个如下构造的 std::mt19937
对象随机生成数据集:
std::mt19937 generator(m);
DatasetFactory dsf(generator);
根据https://dev59.com/I2Up5IYBdhLWcg3wHUvi#15509942和https://dev59.com/HWUp5IYBdhLWcg3w8bJB#14924350,梅森旋转算法(Mersenne Twister PRNG)受益于热身阶段,而我的代码目前缺少这个阶段。为了方便起见,我报告了建议的代码片段。
#include <random>
std::mt19937 get_prng() {
std::uint_least32_t seed_data[std::mt19937::state_size];
std::random_device r;
std::generate_n(seed_data, std::mt19937::state_size, std::ref(r));
std::seed_seq q(std::begin(seed_data), std::end(seed_data));
return std::mt19937{q};
}
在我的情况下,问题是我需要结果的可重复性,也就是说,在不同的执行中,对于每个模拟,数据集必须相同。这就是为什么在我的当前解决方案中,我使用当前模拟来生成Mersenne Twister PRNG的种子。在我看来,使用
std::random_device
会防止数据相同(据我所知,这正是std::random_device
的确切目的)。编辑:通过“不同的执行”,我指重新启动可执行文件。
如何在不影响可重复性的情况下引入前述的预热阶段?谢谢。
可能的解决方案#1
这里是基于 @SteveJessop 的第二个建议的试验性实现。
#include <random>
std::mt19937 get_generator(unsigned int seed) {
std::minstd_rand0 lc_generator(seed);
std::uint_least32_t seed_data[std::mt19937::state_size];
std::generate_n(seed_data, std::mt19937::state_size, std::ref(lc_generator));
std::seed_seq q(std::begin(seed_data), std::end(seed_data));
return std::mt19937{q};
}
可能的解决方案 #2
以下是基于@SteveJassop和@AndréNeve的共同贡献的初步实现。 sha256
函数改编自 https://dev59.com/zHE95IYBdhLWcg3wkegf#10632725
#include <openssl/sha.h>
#include <sstream>
#include <iomanip>
#include <random>
std::string sha256(const std::string str) {
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, str.c_str(), str.size());
SHA256_Final(hash, &sha256);
std::stringstream ss;
for(int i = 0; i < SHA256_DIGEST_LENGTH; i++)
ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
return ss.str();
}
std::mt19937 get_generator(unsigned int seed) {
std::string seed_str = sha256(std::to_string(seed));
std::seed_seq q(seed_str.begin(), seed_str.end());
return std::mt19937{q};
}
使用以下参数进行编译:
-I/opt/ssl/include/ -L/opt/ssl/lib/ -lcrypto
std::mt19937::state_size
,同时保持可重复性。 - Ilio Catallodiscard(n)
,用于向前推进内部状态,就好像调用了operator()
n
次。 - Xeostd::hash<unsigned int>
不够好。你试图克服的 MT 的问题是它需要很多非零位的种子数据,否则它的内部状态大部分为 0,并输出糟糕的数据。std::hash
并不是解决这个问题的正确哈希类型。最多它仍然只提供 64 位的种子数据,而且它比这更糟,因为它很可能是恒等操作。如果你使用例如m
的 SHA256 哈希值,那么你可能会成功。 - Steve Jessop