patterncppMajor
Seed std::mt19937 from std::random_device
Viewed 0 times
stdfromseedrandom_devicemt19937
Problem
Many people seed their Mersenne Twister engines like this:
However, this only provides a single
I am trying to build a function to properly seed a
This does look like a safe solution to me; however, I have learned that problems with RNG are often times quite subtle.
Provided
std::mt19937 rng(std::random_device{}());However, this only provides a single
unsigned int, i.e. 32 bits on most systems, of seed randomness, which seems quite tiny when compared to the 19937 bit state space we want to seed. Indeed, if I find out the first number generated, my PC (Intel i7-4790K) only needs about 10 minutes to search through all 32 bit numbers and find the used seed. (I know that MT is not a cryptographic RNG, but I just did that to get a feel for how small 32 bit really is in these days.)I am trying to build a function to properly seed a
mt19937 and came up with this:#include
#include
#include
auto RandomlySeededMersenneTwister () {
// Magic number 624: The number of unsigned ints the MT uses as state
std::vector random_data(624);
std::random_device source;
std::generate(begin(random_data), end(random_data), [&](){return source();});
std::seed_seq seeds(begin(random_data), end(random_data));
std::mt19937 seededEngine (seeds);
return seededEngine;
}
int main() {
auto rng = RandomlySeededMersenneTwister();
for (int i = 0; i < 10; ++i)
std::cout << rng() << "\n";
}This does look like a safe solution to me; however, I have learned that problems with RNG are often times quite subtle.
Provided
std::random_device produces good, random data on my system, does the code give me a correctly seeded std::mt19937?Solution
-
Well, first off, why do you use a
-
Next, avoid needless magic numbers. Use
-
Why do you use a lambda? A simple
The seeding itself looks perfectly fine and there's no actual error anywhere in your code.
You could avoid the need for
This is not simpler, but maybe more efficient:
On coliru
If you can upgrade to C++20, use ranges and views (godbolt):
Well, first off, why do you use a
std::vector for a comparatively small sequence of known length? A raw array or std::array suffice and avoids any dynamic allocation.-
Next, avoid needless magic numbers. Use
std::mt19937::state_size instead of manually specifying 624.-
Why do you use a lambda? A simple
std::ref(source) suffices.The seeding itself looks perfectly fine and there's no actual error anywhere in your code.
template
auto ProperlySeededRandomEngine () -> typename std::enable_if::type {
std::random_device source;
std::random_device::result_type random_data[(N - 1) / sizeof(source()) + 1];
std::generate(std::begin(random_data), std::end(random_data), std::ref(source));
std::seed_seq seeds(std::begin(random_data), std::end(random_data));
return T(seeds);
}You could avoid the need for
random_data by using counting and transforming iterators as detailed in "Sequence iterator? Isn't there one in boost?".This is not simpler, but maybe more efficient:
template
T ProperlySeededRandomEngine () {
std::random_device source;
auto make_iter = [&](std::size_t n) {
return boost::make_transform_iterator(
boost::counting_iterator(n), [&](size_t){return source();});
};
std::seed_seq seeds(make_iter(0), make_iter((N - 1) / sizeof(source()) + 1));
return T(seeds);
}On coliru
If you can upgrade to C++20, use ranges and views (godbolt):
template
T ProperlySeededRandomEngine () {
std::random_device source;
auto random_data = std::views::iota(std::size_t(), (N - 1) / sizeof(source()) + 1)
| std::views::transform([&](auto){ return source(); });
std::seed_seq seeds(std::begin(random_data), std::end(random_data));
return T(seeds);
}Code Snippets
template<class T = std::mt19937, std::size_t N = T::state_size * sizeof(typename T::result_type)>
auto ProperlySeededRandomEngine () -> typename std::enable_if<N, T>::type {
std::random_device source;
std::random_device::result_type random_data[(N - 1) / sizeof(source()) + 1];
std::generate(std::begin(random_data), std::end(random_data), std::ref(source));
std::seed_seq seeds(std::begin(random_data), std::end(random_data));
return T(seeds);
}template<class T = std::mt19937, std::size_t N = T::state_size * sizeof(typename T::result_type)>
T ProperlySeededRandomEngine () {
std::random_device source;
auto make_iter = [&](std::size_t n) {
return boost::make_transform_iterator(
boost::counting_iterator<std::size_t>(n), [&](size_t){return source();});
};
std::seed_seq seeds(make_iter(0), make_iter((N - 1) / sizeof(source()) + 1));
return T(seeds);
}template<class T = std::mt19937, std::size_t N = T::state_size * sizeof(typename T::result_type)>
T ProperlySeededRandomEngine () {
std::random_device source;
auto random_data = std::views::iota(std::size_t(), (N - 1) / sizeof(source()) + 1)
| std::views::transform([&](auto){ return source(); });
std::seed_seq seeds(std::begin(random_data), std::end(random_data));
return T(seeds);
}Context
StackExchange Code Review Q#109260, answer score: 42
Revisions (0)
No revisions yet.