In a blog post entitled "C++ Seeding Surprises," Melissa E. O'Neill reports that, "When std::seed_seq
tries to “fix” high-quality seed data, it actually makes it worse." According O'Neill, a truly random seeding makes all states possible, but if you push such a seeding through std::seed_seq
, it becomes less random, and certain states become unreachable through seeding.
So, if you have a good source of entropy, why not bypass seed_seq
entirely?
That's what function seed_randomly()
does below. It's taken from my rand_replacement repository on GitHub. It uses operator>>
to overwrite all 624 state variables in mt19937
.
template <typename ResultType>
class rand_replacement
{
public:
using urbg_type = std::mt19937;
using seed_type = typename std::mt19937::result_type;
private:
urbg_type eng_{ seed_type{1u} }; // By default, rand() uses seed 1u.
// ...
void seed_randomly()
{
std::random_device rd;
std::stringstream ss;
for (auto i{ std::mt19937::state_size }; i--;)
ss << rd() << ' ';
ss >> eng_;
}
};
Is this a novel and interesting idea, or is it really foolish?
Regarding std::stringstream
: I understand that it is relatively slow, but that's okay. Seeding should be an infrequent operation.
Regarding std::random_device
: I understand that random_device
may be deterministic on some systems, may block on other systems, and also that it has a checkered history with minGW, but for now, at least, I am satisfied with it. My question is not about random_device
; it is strictly focused on the idea of bypassing seed_seq
using operator>>
, a technique that could be used with any entropy source.
Are there any downsides?
By the way, the alternative, which uses seed_seq
, is a tad bit more complex, and looks something like the following. Is it a better choice than what I coded above?
void seed_randomly()
{
std::random_device rd;
std::array<seed_type, std::mt19937::state_size> seeds;
for (auto& s : seeds)
s = rd();
std::seed_seq const sseq{ std::cbegin(seeds), std::cend(seeds) };
eng_.seed(sseq);
}
The conclusion reached here is that it is wise to bypass std::seed_seq
when you have a truly random source of seeding data.
On many systems, but perhaps not all, std::random_device
qualifies as such a source. Its potential pitfalls are well known. This answer assumes that std::random_device
is a reliable source for random seeds.
A further conclusion is that the solution using operator>>
given in the question is suboptimal. That solution works fine as it stands, but the overhead of std::stringstream
slows things down unnecessarily. A better solution is to create a custom seed_seq
that generates seeds directly, without the need to serialize them, and push them through std::stringstream
.
Based on the ideas of @Sam Mason and @Severin Pappadeux, I came up with class tbx::seed_seq_rd
, which implements the complete interface of std::seed_seq
. It performs only basic checks of its template arguments. Other than that, it complies with all requirements of a seed sequence as defined in the C++ standard.
The reason for implementing the complete interface is so that seed_seq_rd
will satisfy whatever concepts or SFINAE may be blocking entrance to function seed
in a standard-conforming random number engine.
Using it is simple.
// Example: Seed mt19937 with random seeds from std::random_device.
std::mt19937 mt;
tbx::seed_seq_rd s;
mt.seed( s );
// Example: Seed pcg32, one of the PCG engines by Melissa O'Neill.
pcg32 e;
e.seed( s ); // seed_seq_rd object can be reused.
Function seed_randomly
, from my original question, is now templated, and works with any random number engine in the C++ Standard Library. It also works with PCG, by Melissa O'Neill, and any other random number engine that can be seeded with a seed sequence.
template< typename RandomEngine >
void seed_randomly( RandomEngine& e ) {
tbx::seed_seq_rd s;
e.seed( s );
}
// Example: Seed mt19937 with random seeds from std::random_device.
std::mt19937 mt;
tbx::seed_randomly( mt );
// Example: Seed pcg32, one of the PCG engines by Melissa O'Neill.
pcg32 e;
tbx::seed_randomly( e );
I tested with MSVC, and was able to seed all of the engines from
the standard library, as well as pcg32
, a PCG engine by Melissa O'Neill.
I put some polish on seed_seq_rd
, so that it is suitable as a library routine, and uploaded the source code to GitHub.
Souce code for a short demo program is also on GitHub. The demo is a complete program, so you should be able to download and compile without much need to fiddle. I had my compiler set to C++14.