std :: mt19937需要预热吗?

时间:2013-03-19 19:59:21

标签: c++ random c++11 standard-library mersenne-twister

我已经读过许多伪随机数生成器需要许多有序的样本才能“预热”。这是使用std :: random_device种子std :: mt19937的情况,还是我们可以期待它在构建后准备就绪?有问题的代码:

#include <random>
std::random_device rd;
std::mt19937 gen(rd());

3 个答案:

答案 0 :(得分:57)

Mersenne Twister是一个基于移位寄存器的pRNG(伪随机数发生器),因此受到长期0或1的坏种子的影响,导致相对可预测的结果,直到内部状态混合得足够。

然而,采用单个值的构造函数在该种子值上使用复杂的函数,该函数旨在最小化产生这种“坏”状态的可能性。还有第二种方法可以通过符合SeedSequence概念的对象初始化mt19937,直接设置内部状态。这是第二种初始化方法,您可能需要关注选择“良好”状态或进行预热。


该标准包括一个符合SeedSequence概念的对象,称为seed_seqseed_seq接受任意数量的输入种子值,然后对这些值执行某些操作,以生成适合直接设置pRNG内部状态的不同值序列。

以下是加载具有足够随机数据的种子序列以填充整个std::mt19937状态的示例:

std::array<int, 624> seed_data;
std::random_device r;
std::generate_n(seed_data.data(), seed_data.size(), std::ref(r));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));

std::mt19937 eng(seq);

这确保整个状态是随机的。此外,每个引擎都指定从seed_sequence读取的数据量,因此您可能需要阅读文档以查找您使用的任何引擎的信息。

虽然在这里我完全从std::random_device加载了seed_seq,但是seed_seq被指定为只有少数不是特别随机的数字才能正常工作。例如:

std::seed_seq seq{1, 2, 3, 4, 5};
std::mt19937 eng(seq);

在下面的评论中,Cubbi表示seed_seq通过为您执行预热序列而起作用。

以下是播种的“默认”:

std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 rng(seed);

答案 1 :(得分:4)

如果只使用一个32位值进行播种,那么您将获得的只是通过状态空间的2 ^ 32个轨迹之一。如果你使用具有状态KiBs的PRNG,那么你应该将所有这些都归结为种子。正如对@ bames63'回答的评论中所描述的那样,如果你想用随机数初始化整个状态,使用#containerQuotes { bottom: 5px; text-align: center; left: 0px; margin-left: 600px; margin-right: 600px; position: absolute; } 可能不是一个好主意。遗憾的是,std::seed_seq不符合std::random_device概念,但您可以编写一个包装器:

SeedSequence

这至少在您启用概念之前有效。根据您的编译器是否知道#include <random> #include <iostream> #include <algorithm> #include <functional> class random_device_wrapper { std::random_device *m_dev; public: using result_type = std::random_device::result_type; explicit random_device_wrapper(std::random_device &dev) : m_dev(&dev) {} template <typename RandomAccessIterator> void generate(RandomAccessIterator first, RandomAccessIterator last) { std::generate(first, last, std::ref(*m_dev)); } }; int main() { auto rd = std::random_device{}; auto seedseq = random_device_wrapper{rd}; auto mt = std::mt19937{seedseq}; for (auto i = 100; i; --i) std::cout << mt() << std::endl; } 作为C ++ 20 SeedSequence,它可能无法工作,因为我们只提供缺少的concept方法,没有别的。在duck-typed模板编程中,该代码就足够了,因为PRNG不存储种子序列对象。

答案 2 :(得分:2)

我认为有些情况下MT可以播种“差”,导致非最佳序列。如果我没记错的话,用全零播种就是这种情况。如果这是一个严重的问题,我建议您尝试使用WELL生成器。我相信它们更灵活 - 种子的质量并不重要。 (也许更直接地回答你的问题:专注于种子播种可能更有效,而不是播种不良,然后尝试生成一堆样本以使发生器达到最佳状态。)