Generate random numbers using C++11 random library
Stephan T. Lavavej(stl) from Microsoft did a talk at Going Native about how to use the new C++11 random functions and why not to use rand()
. In it, he included a slide that basically solves your question. I've copied the code from that slide below.
You can see his full talk here:
#include <random>
#include <iostream>
int main() {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_real_distribution<double> dist(1.0, 10.0);
for (int i=0; i<16; ++i)
std::cout << dist(mt) << "\n";
}
We use random_device
once to seed the random number generator named mt
. random_device()
is slower than mt19937
, but it does not need to be seeded because it requests random data from your operating system (which will source from various locations, like RdRand for example).
Looking at this question / answer, it appears that uniform_real_distribution
returns a number in the range [a, b)
, where you want [a, b]
. To do that, our uniform_real_distibution
should actually look like:
std::uniform_real_distribution<double> dist(1, std::nextafter(10, DBL_MAX));
My 'random' library provide a high convenient wrapper around C++11 random classes. You can do almost all things with a simple 'get' method.
Examples:
-
Random number in a range
auto val = Random::get(-10, 10); // Integer auto val = Random::get(10.f, -10.f); // Float point
-
Random boolean
auto val = Random::get<bool>( ) // 50% to generate true auto val = Random::get<bool>( 0.7 ) // 70% to generate true
-
Random value from a std::initilizer_list
auto val = Random::get( { 1, 3, 5, 7, 9 } ); // val = 1 or 3 or...
-
Random iterator from iterator range or all container
auto it = Random::get( vec.begin(), vec.end() ); // it = random iterator auto it = Random::get( vec ); // return random iterator
And even more things ! Check out the github page:
https://github.com/effolkronium/random
I red all the stuff above, about 40 other pages with c++ in it like this and watched the video from Stephan T. Lavavej "STL" and still wasn't sure how random numbers works in praxis so I took a full Sunday to figure out what its all about and how it works and can be used.
In my opinion STL is right about "not using srand anymore" and he explained it well in the video 2. He also recommend to use:
a) void random_device_uniform()
-- for encrypted generation but slower (from my example)
b) the examples with mt19937
-- faster, ability to create seeds, not encrypted
I pulled out all claimed c++11 books I have access to and found f.e. that german Authors like Breymann (2015) still use a clone of
srand( time( 0 ) );
srand( static_cast<unsigned int>(time(nullptr))); or
srand( static_cast<unsigned int>(time(NULL))); or
just with <random>
instead of <time> and <cstdlib>
#includings - so be careful to learn just from one book :).
Meaning - that shouldn't be used since c++11 because:
Programs often need a source of random numbers. Prior to the new standard, both C and C++ relied on a simple C library function named rand. That function produces pseudorandom integers that are uniformly distributed in the range from 0 to a system- dependent maximum value that is at least 32767. The rand function has several problems: Many, if not most, programs need random numbers in a different range from the one produced by rand. Some applications require random floating-point numbers. Some programs need numbers that reflect a nonuniform distribution. Programmers often introduce nonrandomness when they try to transform the range, type, or distribution of the numbers generated by rand. (quote from Lippmans C++ primer fifth edition 2012)
I finally found a the best explaination out of 20 books in Bjarne Stroustrups newer ones - and he should know his stuff - in "A tour of C++ 2019", "Programming Principles and Practice Using C++ 2016" and "The C++ Programming Language 4th edition 2014" and also some examples in "Lippmans C++ primer fifth edition 2012":
And it is really simple because a random number generator consists of two parts: (1) an engine that produces a sequence of random or pseudo-random values. (2) a distribution that maps those values into a mathematical distribution in a range.
Despite the opinion of Microsofts STL guy, Bjarne Stroustrups writes:
In , the standard library provides random number engines and distributions (§24.7). By default use the default_random_engine , which is chosen for wide applicability and low cost.
The void die_roll()
Example is from Bjarne Stroustrups - good idea generating engine and distribution with using
(more bout that here).
To be able to make practical use of the random number generators provided by the standard library in <random>
here some executable code with different examples reduced to the least necessary that hopefully safe time and money for you guys:
#include <random> //random engine, random distribution
#include <iostream> //cout
#include <functional> //to use bind
using namespace std;
void space() //for visibility reasons if you execute the stuff
{
cout << "\n" << endl;
for (int i = 0; i < 20; ++i)
cout << "###";
cout << "\n" << endl;
}
void uniform_default()
{
// uniformly distributed from 0 to 6 inclusive
uniform_int_distribution<size_t> u (0, 6);
default_random_engine e; // generates unsigned random integers
for (size_t i = 0; i < 10; ++i)
// u uses e as a source of numbers
// each call returns a uniformly distributed value in the specified range
cout << u(e) << " ";
}
void random_device_uniform()
{
space();
cout << "random device & uniform_int_distribution" << endl;
random_device engn;
uniform_int_distribution<size_t> dist(1, 6);
for (int i=0; i<10; ++i)
cout << dist(engn) << ' ';
}
void die_roll()
{
space();
cout << "default_random_engine and Uniform_int_distribution" << endl;
using my_engine = default_random_engine;
using my_distribution = uniform_int_distribution<size_t>;
my_engine rd {};
my_distribution one_to_six {1, 6};
auto die = bind(one_to_six,rd); // the default engine for (int i = 0; i<10; ++i)
for (int i = 0; i <10; ++i)
cout << die() << ' ';
}
void uniform_default_int()
{
space();
cout << "uniform default int" << endl;
default_random_engine engn;
uniform_int_distribution<size_t> dist(1, 6);
for (int i = 0; i<10; ++i)
cout << dist(engn) << ' ';
}
void mersenne_twister_engine_seed()
{
space();
cout << "mersenne twister engine with seed 1234" << endl;
//mt19937 dist (1234); //for 32 bit systems
mt19937_64 dist (1234); //for 64 bit systems
for (int i = 0; i<10; ++i)
cout << dist() << ' ';
}
void random_seed_mt19937_2()
{
space();
cout << "mersenne twister split up in two with seed 1234" << endl;
mt19937 dist(1234);
mt19937 engn(dist);
for (int i = 0; i < 10; ++i)
cout << dist() << ' ';
cout << endl;
for (int j = 0; j < 10; ++j)
cout << engn() << ' ';
}
int main()
{
uniform_default();
random_device_uniform();
die_roll();
random_device_uniform();
mersenne_twister_engine_seed();
random_seed_mt19937_2();
return 0;
}
I think that adds it all up and like I said, it took me a bunch of reading and time to destill it to that examples - if you have further stuff about number generation I am happy to hear about that via pm or in the comment section and will add it if necessary or edit this post. Bool