Random Engine in Modern C++

Random numbers are widely used in different applications. For example, when you are playing a video game, and some random event like weather changes from a sunny day to a rainy day, we might need to be able to generate the random number in order to determine whether we need to have the weather change. In C language, we can easily generate a pseudo-random number through a rand() function call. The following code will provide us with a random number between 1 – 10, inclusively:


/* initialize random seed: */
srand (time(NULL));
/* generate secret number between 1 and 10: */
iSelect = rand() % 10 + 1;

If we do not really care about the distribution of the random number, the above approach is acceptable. If we care about the distribution, we need to consider another approach. The reason is that rand() is good to generate a uniformly distributed random number between 0 and RAND_MAX, however, if we do the modular operation, does the random number still remain uniformly distributed? No. So in this article, we will introduce the random engine and distribution objects in the <random> library of C++ and see how we can apply them.

First, let’s look at the random engine. A random engine is a stateful generator that generates random values within predefined min and max. We cannot change the range here. Not truly random — pseudorandom (which is pretty similar to rand() in terms of pseudorandom)! Please take a look at the following code with comments to see how we can define and use random engine:

#include<iostream>
#include<string>
#include<random>
#include<vector>
#include<sstream>
#include<chrono>
using namespace std;
void printRandom(default_random_engine e){
for(int i = 0; i < 10; i++)
cout << e() << " ";
cout << endl;
}
int main(){
//Define the default random engine.
default_random_engine eng;
//generate two random numbers
cout << "random number 1:" << eng() <<endl;
cout << "random number 2:" << eng() <<endl;
//output the min and max values of default random engine. The two values
//define the range.
cout << "min value: " << eng.min() <<endl;
cout << "max value: " << eng.max() <<endl;
//Random engine has an internal state which determines which number
//it should generate. The engine with the same state will always produce
//the same random number.
stringstream state;
state << eng; //save the current state of the engine
cout << "random number 3:" << eng() <<endl;
cout << "random number 4:" << eng() <<endl;
state >> eng; //restore the state we have saved.
//Now the number 5 and 6 will be exactly the same as number 3 and 4
cout << "random number 5:" << eng() <<endl;
cout << "random number 6:" << eng() <<endl;
//All the default engine will produce the same value;
default_random_engine e;
default_random_engine e1;
//The following code will produce exactly the same result!
//3499211612 581869302 3890346734 3586334585 545404204 4161255391 3922919429
//949333985 2715962298 1323567403
printRandom(e);
printRandom(e1);
//We typically use chrono library method as our seed. Then each time when we
//run our program, we are guaranteed to have a distinct set of random numbers.
//The following function gent the total number of counts since the epoch
//of computer time.
//(00:00 January 1st, 1970 (coordinated Universal Time – UTC))
unsigned int seed = chrono::steady_clock::now().time_since_epoch().count();
//with seed, the random engine will be able to generate different
//sets of random numbers! It determines the internal state of the engine
default_random_engine e3(seed);
printRandom(e3);
//seed() method can change the state of default random engine.
e.seed(); //set engine to initial state
e.seed(109); //set engine to a state to seed 109
//Random engine can also be applied to shuffle a vector or deque.
vector<int> d = {1, 2, 3, 4, 5, 6, 7, 8, 9};
shuffle(d.begin(), d.end(), default_random_engine());
for (int num : d) {
cout << num << " ";
}
cout << endl;
system("pause");
return 0;
}

view raw
RandomEngine.cpp
hosted with ❤ by GitHub

Note we generally utilize chrono library to generate the seed for the random engine, for more detail about the chrono library, please refer to here.  C++ standard template library provides 16 different engine types. The default engine is a balanced random number generator, with reasonable cost and randomization.

Now, let’s take a look at the distribution objects in <random> library. Suppose we want to generate a number between 1 – 5 uniformly,  what can we do? Please take a look at the following code, you will know how to do it.

#include<iostream>
#include<string>
#include<random>
#include<vector>
#include<sstream>
#include<chrono>
using namespace std;
int main(){
unsigned int seed = chrono::steady_clock::now().time_since_epoch().count();
default_random_engine e(seed);
//The range of the generated number is between [e.min(), e.max()]
cout << e() << endl;
//If we want to generate a random number from the range [0, 5]
cout << e() % 6 << endl;
// Issues with the above code:
/* 1. Bad quality of randomness. The default engine can produce the
* random number pretty good between [e.min(), e.max()]. With %
* operation, this randomness does not holds.
* 2. Can only provide a uniform distribution!
*/
//Uniform distribution between [0, 5], inclusive
uniform_int_distribution<int> distr(0, 5);
//Now the random engine provides the randomness and distribution
//object provides the distribution!
cout << distr(e) << endl;
//Here Range is [0, 5), 5 not included
uniform_real_distribution<float> distrR(0, 5);
cout << distrR(e) << endl;
//We can easily generate other distributions.
//poisson distribution
poisson_distribution<int> distrP(1.0); // 1.0 is the mean
cout << distrP(e) << endl;
cout << "Normal distribution: " << endl;
//10.0 is mean, 3.0 is standard deviation
normal_distribution<double> distrN(10.0, 3.0);
vector<int> v(20);
for(int i = 0; i < 800; i++){
int num = distrN(e); //convert double to int!
if(num >= 0 && num < 20){
v[num]++; //v[num] represents the frequency of num
}
}
//Try to run the code, and you can see the distribution
//yourself!
for(int i = 0; i < 20; i++){
cout <<" " << std::string(v[i], '+') << endl;
}
cout << endl;
system("pause");
return 0;
}

view raw
Distributions.cpp
hosted with ❤ by GitHub

That’s all about the random engine and random distribution objects in <random> library. I hope these techniques can be helpful for your project. Thank you for reading.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s