Mersenne twister热身与再现性

Ili*_*llo 15 c++ random prng mersenne-twister c++11

在我目前的C++ 11项目中,我需要执行M模拟.对于每个模拟m = 1, ..., M,我使用一个std::mt19937对象随机生成一个数据集,构造如下:

std::mt19937 generator(m);
DatasetFactory dsf(generator);
Run Code Online (Sandbox Code Playgroud)

根据/sf/answers/1085695971//sf/answers/1044704531/,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};
}
Run Code Online (Sandbox Code Playgroud)

在我的情况下的问题是我需要结果的可重复性,即在不同的执行中,对于每个模拟,数据集必须是相同的.这就是为什么在我目前的解决方案中,我使用当前的模拟种子Mersenne Twister PRNG.在我看来,使用std::random_device防止数据是相同的(AFAIK,这是确切的目的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};
    }
Run Code Online (Sandbox Code Playgroud)

可能的解决方案#2

这是基于@SteveJassop和@AndréNeve联合贡献的暂定实施.该sha256功能改编自/sf/answers/744290781/

#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};
}
Run Code Online (Sandbox Code Playgroud)

编译: -I/opt/ssl/include/ -L/opt/ssl/lib/ -lcrypto

Ste*_*sop 5

两种选择:

  1. 按照您的建议,但不要使用std::random_device r;生成MT的种子序列,而是使用另外的种子PRNG m.当与小种子数据一起使用时,选择一个不像MT一样需要预热的一个:我怀疑LCG可能会这样做.对于大规模的过度杀伤,您甚至可以使用基于安全哈希的PRNG.如果您已经听说过,这很像密码学中的"关键拉伸".实际上,您可以使用标准键拉伸算法,但是您使用它来生成长种子序列而不是大键材料.

  2. 继续使用m播种MT,但discard在开始模拟之前需要大量恒定的数据.也就是说,忽略建议使用强大的种子,而是运行MT足够长的时间,以达到一个体面的内部状态.我不知道您需要丢弃多少数据,但我希望互联网能够丢弃.