首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >std::mt19937是否需要预热?

std::mt19937是否需要预热?
EN

Stack Overflow用户
提问于 2013-03-20 03:59:21
回答 3查看 14.4K关注 0票数 47

我读到过许多伪随机数生成器需要按顺序排列许多样本才能“预热”。当使用std::random_device来播种std::mt19937时是这样的吗,或者我们能期望它在构造之后就准备好了吗?有问题的代码:

代码语言:javascript
复制
#include <random>
std::random_device rd;
std::mt19937 gen(rd());
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-03-20 04:37:21

Mersenne Twister是一种基于移位寄存器的pRNG (伪随机数生成器),因此容易受到0或1的长时间运行的不良种子的影响,导致相对可预测的结果,直到内部状态充分混淆。

然而,接受单个值的构造函数对该种子值使用了一个复杂的函数,该函数旨在最大限度地减少产生这种“坏”状态的可能性。还有另一种初始化mt19937的方法,您可以通过符合SeedSequence概念的对象直接设置内部状态。这是第二种初始化方法,您可能需要考虑选择一个“良好”状态或进行热身。

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

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

代码语言:javascript
复制
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被指定为只有几个不是特别随机的数字就可以很好地工作。例如:

代码语言:javascript
复制
std::seed_seq seq{1, 2, 3, 4, 5};
std::mt19937 eng(seq);

在下面的评论中,Cubbi指出seed_seq是通过为您执行预热序列来工作的。

下面是你设置种子时的“默认”设置:

代码语言:javascript
复制
std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 rng(seed);
票数 61
EN

Stack Overflow用户

发布于 2018-05-06 17:41:54

如果您只使用一个32位的值作为种子,那么在整个状态空间中,您将得到的是相同的2^32轨迹之一。如果您使用带有状态KiBs的PRNG,那么您可能应该将其全部播种。正如@bames63答案的注释中所描述的,如果你想用随机数初始化整个状态,使用std::seed_seq可能不是一个好主意。遗憾的是,std::random_device不符合SeedSequence的概念,但是您可以编写一个包装器来实现:

代码语言:javascript
复制
#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;

}

这至少在您启用概念之前是有效的。取决于您的编译器是否知道SeedSequence是一个C++20 concept,它可能无法工作,因为我们只提供了缺少的generate()方法,没有提供其他方法。但是,在鸭型模板编程中,这些代码就足够了,因为PRNG不存储种子序列对象。

票数 5
EN

Stack Overflow用户

发布于 2013-03-20 04:04:51

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15509270

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档