可以将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);
}
这里是博客文章的clickable link,我将其改编为用于播种Mersenne Twister的类。该类的内容位于我上面包含的默认构造函数中。这个想法就像您说的那样,用种子数据填充整个19937位状态。
完整的实现是here。那里的大多数其他内容都是为了与<random>
中的发行版兼容。有很多针对教室环境的评论。值得一提的是,除非random_device
可以访问真实的熵,否则它将使用确定性的方法来创建种子值。 seed_seq
也是确定性的,但是如果random_device
至少具有熵源,则可以将其减轻到一定程度。 NOTE:Mersenne Twister构造函数上也调用了seed_seq
,因此即使您仅提供32位状态,也会生成完整状态,但它并不理想:link
简短的故事是,仅提供32位初始状态将导致PRNG永远不会生成某些值。人们可能会争辩说,它足以满足游乐场的需求,或者如果您始终通过分布对它进行过滤,那么它的重要性就小了,我认为这很合理。但是同时,永远不会选择某些值的PRNG与rand()
一样糟糕。
[其他人也会说,一旦您开始关心这个问题,C ++标准库(如果您说STL,这里的人就会发疯,尽管它们在技术上是正确的,但我从未进行过真诚的技术讨论。无论如何,<random>
可能对您来说都不足够。它们不是加密安全的PRNG,并且易于误用,例如被允许对状态大小为19937位的PRNG使用32位。
编辑:我的构造函数建立了一个seed_seq
;我只是试图保证特定的构造函数被调用。