In the midst of the creation of the STL and the tumultuous war over the C++ language standard, a number of programmers developed their own cross-platform class library that provides developers with tools for everyday tasks such as data processing, algorithms, file manipulation, etc. This library is called boost. The project is so successful that the features of Boost are borrowed and fit into the language standard starting with C++11. One of these additions is improved work with random numbers.

The functions rand() and srand() are school level and are suitable for writing simple programs. The disadvantage of these functions is the generation of an insufficiently good sequence of pseudo-random numbers (picture above). Also, the possibilities of simple functions are not enough when developing complex projects.

Random number generators (hereinafter referred to as RNG) were invented to solve the problem. From theirthe advent of significantly improved work on the generation of many types of data, both pseudo- and truly random. An example of generating truly random numbers is the noise in the picture below.

## Pseudo-random number generator

The traditional MF generation algorithm combined both the algorithm for creating unpredictable bits and turning them into a sequence of numbers. The random C++ library, which is part of Boost, separates the two mechanisms. Now the generation of random numbers and the creation of a distribution (sequence) from them occurs separately. The use of distribution is perfectly logical. Because a random number without a specific context makes no sense and is hard to use. Let's write a simple function that rolls a die:

include int roll_a_dice() { std::default_random_engine e{}; //create a random generator std::uniform_int_distribution d{1, 6} //create a distribution with min and max values return d(e); }

A common mistake students of random make is to ignore the creation of a distribution and go straight to generating random numbers the way they are used to. For example, consider the above function.

return 1 + e() % 6;

Some consider this use acceptable. After all, C++ allows it to work that way. However, the creators of the Boost library and the C++11 standards strongly recommend against doing this. At best, it will turn out to be just badcode that looks like code, and at worst it will be working code that makes mistakes that are very difficult to catch. The use of distributions ensures that the programmer gets what he expects.

## Generator and seed initialization

The stage of declaring, defining and creating entities is often viewed as something that is not worth much attention. But not thoughtful enough initialization of the random number generator can affect its proper operation.

std::default_random_engine e1; //implicit default initialization std::default_random_engine e2{}; //explicit default initialization

The first 2 initializations are equivalent. And for the most part, they have to do with taste or standards for writing beautiful code. But the next initialization is fundamentally different.

std::default_random_engine e3{31255}; //initialization with value 31255

"31255" - this is called seed (seed, primary source) - the number on the basis of which the generator creates random numbers. The key point here is that with this initialization, the seed type must be the same or castable to the type that the generator works with. This type is available through decltype(e()), or result_of, or typename.

## Why does the generator create identical sequences?

When a program is run multiple times, the generator always generates the same sequence of numbers, unless its initialization changes, that is, the definition of the generator is the same from run to run of the program. On the one hand, suchself-reproduction of numbers by the generator is useful, for example, when debugging. On the other hand, it is undesirable and can create problems.

Accordingly, in order to avoid repeating the sequence of numbers, the generator must be initialized with different values each time the program is started. Just for these purposes, you can use seed. The standard way to initialize a PRNG is to pass time(0) as its seed from the ctime header file. That is, the generator will be initialized with a value equal to the number of seconds that have passed since January 1, 00:00:00, 1970 UTC.

## PRNG initialization by another generator

Time initialization may not be enough for some tasks. Then you can determine the PRNG through another generator. Here I would like to digress and talk about one powerful tool that allows you to create truly random numbers.

## Random_device - true random number generator

All pseudo-random number generators are deterministic. That is, they have a definition. Or in other words, getting random numbers is based on mathematical algorithms. Random_device is non-deterministic. He creates numbers based on stochastic (random in Greek) processes. Such processes can be changes in the phase or amplitude of current oscillations, oscillations of molecular lattices, movement of air masses in the atmosphere, etc.

Obviously, not every computer and not every system can have a built-inthe ability to get a random number based on a stochastic process. Therefore, it is worth resorting to using random_device only if necessary. Its operation may differ from system to system, from computer to computer, or it may not be available at all. Therefore, when using a true random number generator, it is imperative to provide for error handling.

## Use random_device as seed for PRNG

std::random_device rd{}; std::default_random_engine e{ rd() };

There is nothing fundamentally new in this code. At the same time, with each launch, the PRNG is initialized with random values, which are created by the true random number generator rd.

It is also worth noting that the generator initialization value can be reset at any time:

e.seed(15027); // initialization with a number e.seed(); //initialization with default value e.seed(rd()); //initialization with another generator

## To summarize: generators and distributions

Generator (engine) is an object that allows you to create different equally probable numbers.

Distribution (distirbution) is an object that converts a sequence of numbers generated by the generator into distributions according to a certain law, for example:

- uniform (uniform);
- normal - Gaussian (normal);
- binomial etc.

Let's look at the C++ standard library generators.

- For beginners, it is enough to use default_random_engine, leaving the choice of generator to the library. Selectedwill be a generator based on a combination of factors such as performance, size, randomness quality.
- For advanced users, the library provides 9 preset generators. They differ greatly from each other in performance and size, but at the same time, their quality of work was subjected to serious tests. Often used is a generator called Mersenne twister engines and its instances mt19937 (generate 32-bit numbers) and mt19937_64 (generate 64-bit numbers). The generator is an optimal combination of speed and randomness. For most emerging tasks, it will be enough.
- For experts, the library provides configurable generator templates that allow you to create additional types of generators.

Let's look at the key aspects of distributions. There are 20 of them in the language standard. The example above used the uniform distribution of the random C++ library in the range [a, b] for integers - uniform_int_distribution. The same distribution can be used for real numbers: uniform_real_distribution with the same parameters a and b of the number generation span. In this case, the boundaries of the gap are included, that is, [a, b]. It makes no sense to list all 20 distributions and repeat the C++ documentation in the article.

It should be noted that each distribution has its own set of parameters. For a uniform distribution, this is the interval from a to b. And for the geometric (geometric_distribution) parameter isprobability of success p.

Most of the distributions are defined as a class template, for which the value type of the sequence is the parameter. However, some distributions produce sequences of only int values or only real values. Or, for example, the Bernoulli sequence (bernoulli_distribution) providing bool values. As with the RNG, the library user can create their own distributions and use them with built-in generators or with generators they create.

The possibilities of the library are not limited to this. They are much wider. But the information provided is enough to use and have a basic understanding of the random number generator in C++.

## Quick Reference:. Net style Random

The. Net framework also has a Random class for generating pseudo-random numbers. Consider an example of generating Random number C++/CLI.

For those who work in Visual Studio and can't figure out why the System namespace is not defined.

You need a CLR connection to work with.net. This is done in two ways. 1) Creating a project not with windows console app, but with CLR support - Console application CLR (CLR Console Application). 2) Enable CLR support in the settings of an already created project: project properties ("project" tab, not " service") -> config -> general -> defaults -> in the drop-down list of the item "common language runtime (CLR) support" select "SupportCLRs (/clr)".

include "stdafx.h" include //using namespace System; int main(array ^args) { System::Random ^rnd1=gcnew System::Random(); // RNG creation, initialized by default with the current time std::cout <Next() << "\n"; //returns a positive integer int upper=50; std::cout <Next(upper) << "\n"; //returns a positive integer no greater than upper int a=-1000; int b=-500; std::cout <Next(a, b) << "\n"; //returns an integer in the range [a, b] int seed=13977; System::Random ^rnd2=gcnew System::Random(seed); //initialize RNG with seed number std::cout <Next(500, 1000) << "\n"; //every time the program is run, the same number will be created. std::cout << std::endl; return 0; }

In this case, all the work happens thanks to the Random Next C++/CLI function.

It's worth noting that.net is a large, powerful library and uses its own version of the language called C++/CLI by the Common Language Infrastructure. In general, this is a C++ extension for the. Net platform.

Let's look at some examples at the end to better understand working with random numbers.

include include include int main() { std::mt19937 e1; e1.seed(time(0)); std::cout << e1() << std::endl; std::mt19937e2(time(0)); std::mt19937e3{}; std::uniform_int_distribution uid1(5, 10), uid2(1, 6); std::cout << uid1(e2) << ", " << uid2(e3) << std::endl;std::default_random_engine e4{}; std::uniform_real_distribution urd(0.5, 1.2); std::normal_distribution nd(5.0, 2.0); //normal distribution with mean 5.0 and standard deviation 2.0 std::cout << urd(e4) << ", " << nd(e4) << std::endl; std::cout << std::endl; system("pause"); return 0; }

## Conclusion

Any technologies and methods are constantly evolving and improving. This happened with the random number generation mechanism rand (), which is outdated and no longer meets modern requirements. The STL has the random library, and the. Net Framework has the Random class for working with random numbers. The use of rand should be abandoned in favor of newer methods, as they are in line with modern programming paradigms, and older methods will be deprecated.