C++ Random Numbers with mt19937 and uniform_int_distribution

Do not use rand(). It is old, of low quality and has a bad range. Modern C++ uses a generator (an engine) and a distribution from <random>.

A dice and a decimal number

#include <iostream>
#include <random>

int main() {
    std::random_device device;                          // a true random seed, if the system has it
    std::mt19937 gen(device());                         // the engine (Mersenne Twister)

    std::uniform_int_distribution<int> dice(1, 6);
    std::uniform_real_distribution<double> unit(0.0, 1.0);

    bool all_ok = true;
    for (int i = 0; i < 1000; i++) {
        int d = dice(gen);
        double u = unit(gen);
        all_ok = all_ok && d >= 1 && d <= 6 && u >= 0.0 && u < 1.0;
    }
    std::cout << "1000 numbers, all in range: " << all_ok << '\n';
}
Output

1000 numbers, all in range: 1

Every run gives different numbers, so we only check the range. When you run it, print dice(gen) to see them.

A fixed seed: the same numbers every time

For tests and games with a “seed”, give the engine a number. The mt19937 engine is exactly defined by the standard, so its raw numbers are the same everywhere:

#include <iostream>
#include <random>

int main() {
    std::mt19937 a(42);
    std::cout << a() << ' ' << a() << ' ' << a() << '\n';

    std::mt19937 c(42), d(42);                         // two engines with the same seed
    std::cout << std::boolalpha << (c() == d()) << ' ' << (c() == d()) << '\n';
}
Output

1608637542 3421126067 4083286876
true true

The distributions (like uniform_int_distribution) can give different numbers on different compilers, even with the same seed. Only the engine is fully fixed.

Shuffle and pick

#include <algorithm>
#include <iostream>
#include <random>
#include <vector>

int main() {
    std::vector<int> cards = {1, 2, 3, 4, 5, 6, 7, 8};
    std::mt19937 gen(std::random_device{}());

    std::shuffle(cards.begin(), cards.end(), gen);
    std::cout << cards.size() << " cards, sorted again: ";
    std::sort(cards.begin(), cards.end());
    for (int c : cards) std::cout << c << ' ';
    std::cout << '\n';

    std::uniform_int_distribution<std::size_t> pick(0, cards.size() - 1);
    std::cout << "random index in range: " << (pick(gen) < cards.size()) << '\n';
}
Output

8 cards, sorted again: 1 2 3 4 5 6 7 8
random index in range: 1

Good to know

  • Create the engine once and reuse it. Do not make a new one for every number.
  • mt19937 is not safe for passwords or keys. For security use the random functions of the operating system or a crypto library.
  • std::random_device can be weak on some old systems.

Related: C++ Algorithms and C++ Vectors.


Learn more about the similar topics:
Tutorials
No Content Found.
Exercises & Assignments
No Content Found.
Interview Questions & Answers
No Content Found.