Courses

Random Numbers with C++

Basic Concepts

We are going to use C++11 and more specifically the header random which introduces random number generation facilities.

This library allows to produce random numbers using combinations of generators and distributions:

  • Generators: Objects that generate uniformly distributed numbers.
  • Distributions: Objects that transform sequences of numbers generated by a generator into sequences of numbers that follow a specific random variable distribution, such as Uniform, Normal or Binomial.

Distribution objects generate random numbers by means of their operator() member, which takes a generator object as argument: 

std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(1,6);
int dice_roll = distribution(generator);  // generates number in the range 1..6

All standard generators defined in the library are random number engines, which are a kind of generators that use a particular algorithm to generate series of pseudo-random numbers. These algorithms need a seed as a source of randomness, and this seed can either be a single value or an object with a very specific generate() member function (see seed_seq for more info). A typical source of randomness for trivial tasks is time, such as the information provided by time or system_clock::now.

 

Generators/ engines

 They use an algorithm to generate pseudo-random numbers based on an initial seed:

 

Particular instantiations of generator engines:

  • default_random_engineIt is the library implemention's selection of a generator that provides at least acceptable engine behavior for relatively casual, inexpert, and/or lightweight use.
  • minstd_randA simple multiplicative congruential pseudo-random number generator. 
    x = x * 48271 % 2147483647
  • mt19937A Mersenne Twister pseudo-random generator of 32-bit numbers with a state size of 19937 bits.
    typedef mersenne_twister_engine<uint_fast32_t,
      32,624,397,31,0x9908b0df,11,0xffffffff,7,0x9d2c5680,15,0xefc60000,18,1812433253>
      mt19937;
  • mt19937_64A Mersenne Twister pseudo-random generator of 64-bit numbers with a state size of 19937 bits.
    typedef mersenne_twister_engine<uint_fast64_t,
      64,312,156,31,0xb5026f5aa96619e9,
      29,0x5555555555555555,
      17,0x71d67fffeda60000,
      37,0xfff7eee000000000,
      43,6364136223846793005> mt19937_64;

 

Distributions

Popular ones:

 

** For more information on C++ random library check this website & for more general info on random numbers check here.

 

True Random Number Generators? 

Traditionally, computers rely on mathematical algorithms to generate ‘random’ numbers. These type of random numbers are called pseudorandom numbers. For a finite sample, good pseudorandom number generators will also reproduce statistics that are consistent with true randomness.

But all pseudorandom number generators rely on a seed to generate the random sequences. This means that anybody who has access to the seed will be able to generate the same sequence of random numbers.

Moreover, most pseudorandom numbers have a finite period. Good pseudorandom number generators (e.g. Mersenne Twister MT19937) have huge periods. But eventually, if we wait long enough, the sequence will repeat itself.

** Check this website for more info on true random numbers and a method to produce them from quantum phenomena.

 

Example Codes

An example of generating random numbers using the default engine of C++ and a uniform distribution. 

  1. // To compile:
  2. // g++ -std=c++11 uniform_int_distribution.cpp
  3.  
  4. // uniform_int_distribution
  5. #include <iostream>
  6. #include <string>
  7. #include <random>
  8.  
  9. int main()
  10. {
  11. const int nrolls = 10000; // number of experiments
  12. const int nstars = 95; // maximum number of stars to distribute
  13.  
  14. int seed = 6;
  15. std::default_random_engine generator(seed);
  16. std::uniform_int_distribution<int> distribution(0,9);
  17.  
  18. int p[10]={};
  19.  
  20. std::cout << "Without changing the seed:" << "\n";
  21. for (int i=0; i<10; ++i) {
  22. std::cout << distribution(generator) << " " ;
  23. }
  24. std::cout << "\n";
  25.  
  26. for (int i=0; i<nrolls; ++i) {
  27. int number = distribution(generator);
  28. ++p[number];
  29. }
  30.  
  31. std::cout << "uniform_int_distribution (0,9):" << std::endl;
  32. for (int i=0; i<10; ++i)
  33. std::cout << i << ": " << std::string(p[i]*nstars/nrolls,'*') << std::endl;
  34.  
  35. return 0;
  36. }

An example of generating random numbers using various engines and a normal distribution. 

  1. // To compile:
  2. // g++ -std=c++11 normal_distribution.cpp
  3.  
  4. // normal_distribution
  5. #include <iostream>
  6. #include <string>
  7. #include <random>
  8.  
  9. int main()
  10. {
  11. const int nrolls=10000; // number of experiments
  12. const int nstars=100; // maximum number of stars to distribute
  13.  
  14. //std::mt19937 generator;
  15. //std::minstd_rand generator;
  16. std::default_random_engine generator;
  17. std::normal_distribution<double> distribution(5.0,2.0);
  18.  
  19. int p[10]={};
  20.  
  21. for (int i=0; i<nrolls; ++i) {
  22. double number = distribution(generator);
  23. if ((number>=0.0)&&(number<10.0)) ++p[int(number)];
  24. }
  25.  
  26. std::cout << "normal_distribution (5.0,2.0):" << std::endl;
  27.  
  28. for (int i=0; i<10; ++i) {
  29. std::cout << i << "-" << (i+1) << ": ";
  30. std::cout << std::string(p[i]*nstars/nrolls,'*') << std::endl;
  31. }
  32.  
  33. return 0;
  34. }

Remark:  In the previous examples, the seed is not changing, i.e., the produced random numbers are the same at every execution of the codes.

Below, by changing the seed we manage to produce different sequences at every execution.

  1. // To compile:
  2. // g++ -std=c++11 seed.cpp
  3.  
  4. #include <iostream>
  5. #include <chrono>
  6. #include <random>
  7.  
  8. int main()
  9. {
  10. // construct a trivial random generator engine from a time-based seed:
  11. unsigned int seed = std::chrono::system_clock::now().time_since_epoch().count();
  12. std::default_random_engine generator(seed);
  13.  
  14. std::normal_distribution<double> distribution (0.0,1.0);
  15.  
  16. std::cout << "some Normal-distributed(0.0,1.0) results:" << std::endl;
  17. for (int i=0; i<10; ++i)
  18. std::cout << distribution(generator) << " ";
  19. std::cout << std::endl;
  20.  
  21. return 0;
  22. }