首页 > 解决方案 > 我应该使用 std::seed_seq 来播种 std::mt19937 吗?

问题描述

直接用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);

标签: c++random

解决方案


简短的回答是你不需要,所有 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,这里的人会发疯,虽然他们在技术上是正确的,但我从未有过真诚的技术讨论rails)<random>对你来说可能还不够好。它们不是加密安全的 PRNG,而且很容易被误用,例如允许将 32 位用于具有 19937 位状态大小的 PRNG。

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


推荐阅读