std :: mt19937需要预热吗?

Bre*_*ent 46 c++ random standard-library mersenne-twister c++11

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

#include <random>
std::random_device rd;
std::mt19937 gen(rd());
Run Code Online (Sandbox Code Playgroud)

bam*_*s53 57

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

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


该标准包括一个符合SeedSequence概念的对象,称为seed_seq.seed_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);
Run Code Online (Sandbox Code Playgroud)

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

虽然在这里我完全从中加载了seed_seq std::random_device,但是seed_seq指定的只是一些不是特别随机的数字应该运行良好.例如:

std::seed_seq seq{1, 2, 3, 4, 5};
std::mt19937 eng(seq);
Run Code Online (Sandbox Code Playgroud)

在下面的评论中,Cubbi表示seed_seq通过为您执行预热序列来工作.

这是播种的"默认":

std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 rng(seed);
Run Code Online (Sandbox Code Playgroud)

  • @Wyzard每个引擎类确切地指定它从`seed_seq`中读取的值.它恰好是`std :: mt19937`类的`state_size`,但是例如`std :: mt19937_64`的'2*std :: mt19937_64 :: state_size`.您必须阅读特定引擎的文档.看起来在线参考文献没有详细介绍,但它已在标准中涵盖. (3认同)
  • 巧合的是,默认的C++ 11`种子_seq`*是Mersenne Twister预热序列(虽然现有的实现,例如libc ++的`mt19937`,在提供单值种子时使用更简单的预热) (2认同)

Mar*_*utz 5

如果你只用一个 32 位值作为种子,你将得到的只是通过状态空间的相同 2^32 轨迹之一。如果您使用带有 KiB 状态的 PRNG,那么您可能应该为所有这些设置种子。如@bames63' 回答的评论中所述,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;

}
Run Code Online (Sandbox Code Playgroud)

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