Ch12.4: Seeding Reproducible Engines with seed_seq
When You Need Reproducible Randomness
Sometimes you need a reproducible random number generator — for
debugging, testing, or simulations where you want to reproduce the same sequence of random
numbers. In these cases, you can't use ibuf_white_hole_engine directly because
it's designed to be unpredictable.
Instead, use a fast PRNG like ::std::mt19937_64 and seed it properly. The
correct way to seed such engines is with ::std::seed_seq, which takes multiple
random values from a secure source and combines them into a high-quality seed.
Example: Seeding mt19937_64 for Reproducible Randomness
#include <fast_io.h>
#include <fast_io_device.h>
#include <random>
#include <array>
int main() {
using namespace ::fast_io::io;
// Use secure random to generate seed data
::fast_io::native_white_hole rng;
// Generate enough random bytes for a high-quality seed
::fast_io::array<::std::uint32_t, 8> seed_data;
::fast_io::operations::read_all(rng, reinterpret_cast<::std::byte*>(seed_data.data()), reinterpret_cast<::std::byte*>(seed_data.data() + seed_data.size()));
// Create seed_seq from random data
::std::seed_seq seed(seed_data.begin(), seed_data.end());
// Seed the Mersenne Twister engine
::std::mt19937_64 reproducible_eng(seed);
// Use with distributions
::std::uniform_int_distribution<::std::int64_t> dist(1, 100);
// This sequence is now reproducible if you save and reuse the seed_data
for (::std::size_t i{}; i != 5; ++i) {
println("Random number: ", dist(reproducible_eng));
}
// For true reproducibility, save seed_data to a file and reload it
// Then the same sequence can be reproduced later
}
Common Mistakes to Avoid
Never use
::std::mt19937_64 with its default constructor for anything
important! The default seed is fixed (typically 5489u), which means every program
run produces the exact same sequence of "random" numbers. This is predictable and insecure.
Never seed with
std::time(nullptr)! This is also predictable
and provides very low entropy. Always use ::std::seed_seq with values from a
secure source like ibuf_white_hole_engine.
Bad Examples (Don't Do This)
// WRONG: Default seed is predictable
::std::mt19937_64 eng; // Always produces the same sequence!
// WRONG: time(nullptr) is low-entropy and predictable
::std::mt19937_64 eng(::std::time(nullptr)); // Insecure!
// WRONG: Single value seed doesn't use full state space
::std::mt19937_64 eng(42); // Only 32 bits of seed for 19937-bit state!
Good Example (Do This)
// CORRECT: Use seed_seq with multiple secure random values
::fast_io::native_white_hole rng;
::fast_io::array<::std::uint32_t, 8> seed_data;
::fast_io::operations::read_all(rng, reinterpret_cast<::std::byte*>(seed_data.data()), reinterpret_cast<::std::byte*>(seed_data.data() + seed_data.size()));
::std::seed_seq seed(seed_data.begin(), seed_data.end());
::std::mt19937_64 eng(seed); // High-quality, secure seed
When to Use Each Approach
| Use Case | Recommended Approach |
|---|---|
| Cryptographic keys, tokens, passwords | ibuf_white_hole_engine directly |
| Security-sensitive applications | ibuf_white_hole_engine directly |
| Debugging random algorithms | ::std::mt19937_64 with ::std::seed_seq |
| Unit tests with deterministic behavior | ::std::mt19937_64 with ::std::seed_seq |
| Scientific simulations (reproducible) | ::std::mt19937_64 with ::std::seed_seq |
| Games (save/load random state) | ::std::mt19937_64 with ::std::seed_seq |
Key Takeaways
-
For reproducible randomness, use
::std::mt19937_64seeded with::std::seed_seqinitialized fromibuf_white_hole_engine. -
Never use
::std::mt19937_64with default seed — it's predictable. -
Never seed with
std::time(nullptr)— low entropy and predictable. -
Use
ibuf_white_hole_enginedirectly for security-sensitive applications where unpredictability is required. - Save the seed data if you need to reproduce the exact same sequence later.