C++11 introduces several pseudo-random number generators designed to replace the good-old rand
from the C standard library. I’ll show basic usage examples of std::mt19937
, which provides a random number generation based on Mersenne Twister algorithm. Using the Mersenne Twister implementation that comes with C++1 has advantage over rand()
, among them:
mt19937
has much longer period than that ofrand
, e.g. it will take its random sequence much longer to repeat itself.- It much better statistical behavior.
- Several different random number generator engines can be initiated simultaneously with different seed, compared with the single “global” seed
srand()
provides.
The downside is that mt19937
is a bit less straight-forward to use. However, I hope this post will help with this point :-).
We start with a basic example:
#include <iostream>
#include <random>
using namespace std;
int main()
{
mt19937 mt_rand(time(0));
cout << mt_rand() << endl;
return 0;
}
Line 7 creates a new std::mt19937
object and seeds it with time(0)
. This is just like calling srand(time(0))
. Subsequent calls to the newly created object, mt_rand
will return a random 32-bit unsigned int.
If you want integers in a specific range, instead all kinds of arithmetics, C++11 provides convinient wrappers:
mt19937::result_type seed = time(0);
auto dice_rand = std::bind(std::uniform_int_distribution<int>(1,6),
mt19937(seed));
mt19937::result_type seed = time(0);
auto real_rand = std::bind(std::uniform_real_distribution<double>(0,1),
mt19937(seed));
In the first example, each call to dice_rand()
will return an integer between 1 and 6 (inclusive). In the second example each call to real_rand()
will return a double in the range [0,1) (including 0, excluding 1). Note that usage of std::bind
requires #include <functional>
.
Finally if you want to go C++11 all the way, you can also replace time(0)
with a proper call for std::chrono
when seeding the random number generator:
auto seed = chrono::high_resolution_clock::now().time_since_epoch().count();
mt19937 mt_rand(seed);
The above code requires adding #include <chrono>
to the list of includes.
What is the advantage of using chrono::high_resolution_clock::now().time_since_epoch().count() as opposed to time(0), other than a desire to use C++11 for the sake of it.
Actually there is an advantage, seeding with
chrono::high_resolution_clock
would give more entropy as in most systems its tick duration should be much smaller than a second. The C equivalent would be (the unportable Linux)gettimeofday()
which provides high resolution clock.When compiling the first example I got the error “mt19937 was not declared in this scope”
Does your compiler support C++11? Some compilers (such as gcc and clang) require a special compilation flag to enable C++11 support.
The example is missing #include which is why the mt19937 could not be found..
Thanks, I’ve added the missing
#include <random>
std::mt19937 has a state size of 19968 bits. Seeding it with 32 bits, as you demonstrate, is woefully inadequate (see: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html).
SO has a solution here: https://stackoverflow.com/questions/15509270/does-stdmt19937-require-warmup
Which boils down to this:
Mersenne Twister is a shift-register based pRNG and is therefore subject to bad seeds with long runs of 0s or 1s that lead to relatively predictable results until the internal state is mixed up enough.
However the constructor which takes a single value uses a complicated function on that seed value which is designed to minimize the likelihood of producing such ‘bad’ states. There’s a second way to initialize mt19937 where you directly set the internal state, via an object conforming to the SeedSequence concept. It’s this second method of initialization where you may need to be concerned about choosing a ‘good’ state or doing warmup.
The standard includes an object conforming to the SeedSequence concept, called seed_seq. seed_seq takes an arbitrary number of input seed values, and then performs certain operations on these values in order to produce a sequence of different values suitable for directly setting the internal state of a pRNG.
Here’s an example of loading up a seed sequence with enough random data to fill the entire std::mt19937 state:
std::array<int, 624> seed_data;
std::random_device r;
std::generate_n(seed_data.data(), seed_data.size(), std::ref(r));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::mt19937 eng(seq);