Time and Random Numbers

Random Distributions

A random number engine generates raw unsigned integers over its full domain. A distribution then takes these raw values and generates numbers of a particular shape, or within an interval. The distribution is the component you use when you want to ask the random number generator for an integer between 1 and 6, or a floating point number between 0.0 and 1.0.

The most common integer distribution is std::uniform_int_distribution. This generates values in a closed range, each with equal probability.

#include <iostream>
#include <random>

int main()
{
    std::mt19937 eng{ std::random_device{}() };
    // Map engine output uniformly into this integer range
    std::uniform_int_distribution<int> dist{ 1, 6 };
    std::cout << dist(eng) << '\n';
}

The engine eng is seeded using a value provided by random_device. The distribution dist is created with bounds 1 and 6. Calling dist(eng) then tells the distribution to generate one number using the engine for randomness.

The generated value will be in the inclusive range [1, 6], and it will be printed.

Another call to dist(eng) then produces the next independent roll.

#include <iostream>
#include <random>

int main()
{
    std::mt19937 eng{ std::random_device{}() };
    // Map engine output uniformly into this floating-point range
    std::uniform_real_distribution<double> dist{ 0.0, 1.0 };
    std::cout << dist(eng) << '\n';
}

The distribution dist is constructed to generate numbers in the half open interval [0.0, 1.0). Calling dist(eng) produces a double in this range. The engine provides the raw random bits, and the distribution maps these bits onto the requested range.

#include <iostream>
#include <random>

int main()
{
    // Use a fixed seed to make the generated sequence repeatable
    std::mt19937 eng{ 42 };
    std::uniform_int_distribution<int> die{ 1, 6 };
    std::uniform_int_distribution<int> coin{ 0, 1 };
    std::cout << die(eng) << ' ' << coin(eng) << '\n';
}

The engine eng is constructed with a fixed seed, so the sequence will be reproducible. Two distributions are constructed: die to generate numbers in the range 1 to 6, and coin to generate numbers in the range 0 to 1. Each distribution is then called with the same engine.

The first call consumes some random state and generates a die roll; the second call continues using the new internal state of the engine and generates a coin flip.

The two values are printed on the same line.

The parameters for a distribution are stored internally, but they contain no random state of their own. All of the state for random generation is contained entirely within the engine passed in on a call to operator(). This means that the same distribution object can be used many times with a large number of different engines, and the same engine can be called with a large number of different distributions.

It is possible to create a distribution with minimum greater than maximum, but calling it with the engine results in undefined behavior.

So, an integer distribution sits between the pure randomness of the engine and the values required by the program. You must create the engine, create the distribution giving it the bounds you need, then call the distribution with the engine whenever you need a new random number.

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.