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