(Non-)Reproducible Random Number Sequences Reproducible Random Numbers Random

    • distribution objects produce random numbers
    • uniform random bit engine objects act as sources of randomness
    #include <random>
    random_engine_type engine {seed};
    distribution_type distribution {parameters,…};
    auto random_value = distribution(engine);

    Obtain Reproducible Sequences

    Given the same initial state, two pseudo random bit engine objects of the same type will produce exactly the same sequence of random bits.

    Two distribution objects of the same type will produce the same sequence of random numbers if

    • their initial state is the same (e.g., both default constructed or identical copies),
    • the engines feeding them random bits are of the same type
    • the engines were initialized the same, e.g., seeded identically
    • the engines have performed the same number of operations

    Unfortunately, there is no guarantee that a standard distribution type produces the same random number sequence on different platforms, i.e., using different standard library versions or implementations.

    Seed With A Constant

    std::mt19937_64 eng (123);

    The integer seed type engine_type::result_type is usually an unsigned integer type.

    using seed_type = std::mt19937_64::result_type;
    seed_type const seed = 123;
    std::mt19937_64 eng {seed};

    Seed With A Seed Sequence Seed Seq.

    std::seed_seq s {1,5,3,7,0,9};
    std::mt19937_64 eng {s};
    std::seed_seq s {1,5,3,7,0,9};

    Obtain Unpredictable Sequences

    Non-Deterministic ⇒ std::random_device std::random_device random_device

    std::random_device represents a non-deterministic random number generator that e.g., uses a hardware entropy source.

    std::random_device rd;
    std::uniform_real_distribution<double> distr {-1.0, 1.0};
    auto rndNum = distr(rd);

    Standard library implementations are allowed to use a pseudo-random number engine as random_device if there is no non-deterministic entropy source available.

    std::random_device rd;
    bool non_deterministic = rd.entropy() >  0;
    bool deterministic     = rd.entropy() == 0;

    Some (older) standard library implementations return 0, despite beeing non-deterministic.

    Deterministic ⇒ Seed With Clock Seed With Clock Clock

    #include <chrono>  // clocks
    auto const ticks = std::chrono::system_clock::now().time_since_epoch().count();
    std::mt19937_64 eng (ticks);
    #include <chrono>  // clocks
    auto const ticks = std::chrono::system_clock::now().time_since_epoch().count();

    This is still deterministic: given the same number of clock ticks as initial seed, the sequence of random numbers obtained via a pseudo-random engine of a particular type will always be the same.

    The type used for clock ticks is usually a signed integer while the engine seed type is usually an unsigned integer.

    Example: Rolling N-Sided Dice

    • custom function class that models n-sided dice
    • takes integer seed as constructor argument
    • here: seeded with system clock
    #include <random>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <chrono>
    template<int SIDES,   typename Engine = std::mt19937_64> 
    class Dice {
      using engine_type = Engine;
      std::uniform_int_distribution<int> distr_;
      engine_type urbg_;
      using seed_type = typename engine_type::result_type;
      Dice() = default;
      Dice(seed_type seed):     distr_{1,SIDES}, urbg_{seed} {}
      int operator () () { return distr_(urbg_); }
    int main () {
      // make D20 and seed with clock:
      auto const ticks = std::chrono::system_clock::now().  time_since_epoch().count();
      Dice<20> d20 (ticks);
      // generate sequence of dice rolls:
      std::vector<int> rolls (10);
      std::generate(begin(rolls), end(rolls), d20);
      // print results:
      for (int x : rolls) std:: cout << x <<' ';
      std::cout << '\n';