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().