简体   繁体   中英

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

Is it ok that 624 integers generated by random_device are directly used to seed mt19937? Should I use 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);

The short answer is that you don't need to, all Mersenne Twister constructors call seed_seq on whatever state you give it regardless of its size.


This is code I've written to fill the initial state of a 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);
}

Here is a clickable link to the blog post that I adapted into a class for seeding a Mersenne Twister. The meat of the class is in the default constructor which I included above. The idea is exactly like you said, to fill the entire 19937-bit state with seed data.

The full implementation is here . Most of the other stuff is there to provide compatibility with the distributions in <random> . There are a lot of comments aimed at a classroom setting. It is worth mentioning that unless random_device has access to true entropy, it will use a deterministic method of creating seed values. seed_seq is also deterministic, but it can be alleviated to an extent if random_device at least has an entropic source. NOTE : seed_seq is also called on Mersenne Twister constructors, so even if you provide just 32-bits of state, a full state will be generated, but it's less than ideal: link

The short story is that providing just 32-bits of initial state will cause the PRNG to simply never generate certain values, ever. People may argue that it's sufficient for playground purposes, or that it matters less if you're always filtering it through a distribution, and I think that's a fair point. But at the same time, a PRNG that will never select certain values is just as bad as rand() .

Others will also make the case that once you start caring about this, the C++ Standard Library (people here go nuts if you say STL, and while they're technically correct, I've never had a good-faith technical discussion go off the rails) <random> simply may not be good enough for you anyway. They are not cryptographically secure PRNGs, and are easy to mis-use, like being allowed to use 32-bits for a PRNG that has a 19937-bit state size.

EDIT: My constructor builds a seed_seq ; I'm just attempting to guarantee that a specific constructor gets invoked.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM