我应该使用std::seed_seq来种子std::mt19937吗

Should I use std::seed_seq to seed std::mt19937?

本文关键字:std 种子 seq mt19937 seed 我应该      更新时间:2023-10-16

random_device生成的624个整数直接用于种子mt19937可以吗?我应该使用seed_seq吗?

class RDSeq {
public:
template <typename It>
void generate (It first, It last) const {
std::random_device rd {};
std::generate(first, last, std::ref(rd));
}
};
std::mt19937 random {};
RDSeq seq {};
random.seed(seq);

简单的答案是,不需要,所有Mersenne Twister构造函数都会在任何状态下调用seed_seq,而不管它的大小。


这是我为填充Mersenne Twister的初始状态而编写的代码。

template <typename T = std::uint32_t, typename Enable = void>
class Mersenne;
template <typename T>
using AllowForUnsigned = std::enable_if_t<std::is_unsigned_v<T>>;
template <typename T>
class Mersenne<T, AllowForUnsigned<T>>
{
public:
Mersenne();
T operator()();
using result_type = T;
static constexpr result_type min();
static constexpr result_type max();
private:
using Twister = std::conditional_t<sizeof(T) <= 4, std::mt19937, std::mt19937_64>;
Twister engine_;
};
// Mersenne class implementation
template <typename T>
Mersenne<T, AllowForUnsigned<T>>::Mersenne()
{
// Proper seeding of mt19937 taken from:
// https://kristerw.blogspot.com/2017/05/seeding-stdmt19937-random-number-engine.html
// Body walkthrough at end of file
std::random_device rd;
std::array<T, Twister::state_size> seed_data;
std::generate_n(std::begin(seed_data), seed_data.size(), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
engine_ = Twister(seq);
}

这是一个可点击的链接,我把它改编成了一个播种Mersenne Twister的课程。该类的核心在我上面包含的默认构造函数中。这个想法就像你说的,用种子数据填充整个19937位状态。

全面实施就在这里。其他大部分内容都是为了提供与<random>中的发行版的兼容性。有很多针对课堂环境的评论。值得一提的是,除非random_device能够访问真熵,否则它将使用创建种子值的确定性方法。seed_seq也是确定性的,但如果random_device至少有熵源,它可以在一定程度上得到缓解注意seed_seq也在Mersenne Twister构造函数上调用,因此即使您只提供32位状态,也会生成完整的状态,但这并不理想:link

简而言之,只提供32位的初始状态将导致PRNG永远不会生成某些值。人们可能会争辩说,这对于游乐场来说已经足够了,或者如果你总是通过分发来过滤它,那就不那么重要了,我认为这是一个公平的观点。但与此同时,永远不会选择某些值的PRNG和rand()一样糟糕。

其他人也会说,一旦你开始关心这个问题,C++标准库(如果你说STL,这里的人会发疯,虽然他们在技术上是正确的,但我从未进行过真诚的技术讨论(<random>对你来说可能根本不够好。它们不是加密安全的PRNG,并且很容易被错误使用,比如被允许对具有19937位状态大小的PRNG使用32位。

编辑:我的构造函数构建了一个seed_seq;我只是试图保证调用一个特定的构造函数。