How to use a pseudo-random number generator:
- Include the necessary headers:
If you did this previously...
#include <ctime>
#include <cstdlib>
now do this instead:
#include <ctime>
#include <random>
- Initialize (seed) the pseudo-random number generator:
If you did this previously in main()
:
std::srand(std::time(nullptr));
now do this instead, in global scope:
std::mt19937 generator(std::time(nullptr));
One difference is instead of having a global generator that's used by everything, we have a global generator specific to our application. That's a good thing.
- Use a distribution to generate numbers:
If you did this previously:
// generate a random integer from [0, 200)
int x = rand() % 200;
// generate a random integer from [10, 200)
int y = rand() % (200-10) + 10;
// generate a random integer from [0, 5000000)
long long larger_number = (static_cast<long long>(rand()) * (RAND_MAX+1) + rand()) % 5000000;
// generate a random double from [0, 1)
double r = static_cast<double>(rand()) / (RAND_MAX+1);
now do this instead:
// generate a random integer from [0, 200)
int x = std::uniform_int_distribution<int>(0, 199)(generator);
// generate a random integer from [10, 200)
int y = std::uniform_int_distribution<int>(10, 199)(generator);
// generate a random integer from [0, 5000000)
long long larger_number = std::uniform_int_distribution<long long>(0, 4999999)(generator);
// generate a random double from [0, 1)
double r = std::uniform_real_distribution<double>(0.0, 1.0)(generator);
That's it!
Q: Why does this exist?
A: This post exists to prove that "rand()
is simpler to use than <random>
" argument is not true. The concepts map directly. Now, in my opinion, C++11's <random>
has its own problems (see below), but that one is not it. This usage of <random>
API C++11 is lacking in some areas, but it's not worse than existing rand()
code, and yet, it's still simpler to use, which is what typical existing rand()
user cares about the most.
Q: This doesn't seed properly.
A: Yes, it doesn't. Unfortunately, there's no good solution that doesn't involve writing ~50 lines of code, or using an external library. Still, it's no worse than existing rand()
code.
Q: Why aren't you using std::random_device()()
to seed the generator?
A: You're going to have a lot of fun on MinGW where it will generate the same number every time. I was so annoyed at that I wrote my own wrapper around OS-specific APIs.. But you don't need to use that specific one, you may as well use boost::random_device
, which is even better than std::random_device
, because it provides .generate(Iter begin, Iter end)
that allows to be used as a seed sequence, seeding with as much as the generator wants, by passing the boost::random_device
instance directly to the seed operation, and not the result of calling it once.
Q: You're not reusing distributions.
A: This is a potential performance problem (but not for std::uniform_int_distribution
and std::uniform_real_distribution
). Again, it's not worse than existing rand()
code.
Q: A global generator isn't thread safe.
A: Neither is rand()
.