Stop Using rand(): Random Numbers with C++11
Stop Using rand(): Random Numbers with C++11
I’m sure at some point we’ve all used rand()
to generate random numbers. If you’re using C you don’t have much of a choice, but if you’re using C++11 (or above), then you’re missing out.
C++11 adds an awesome <random>
header that gives you new ways to generate random numbers. At first, it’s a little bit more complicated than just calling rand()
If you’re pressed for time and just want the example code, scroll down. If you want a short explanation of how <random>
works, keep reading!
Engines
Random number engines are simply different random number generation algorithms that take a seed and generate pseudo-random numbers. They allow for a lot of configuration that, for your average software project, you don’t need to bother with. This is what random number generators are for, described below.
Generators
Like I said above, random number engines have a lot of settings and can be rather complicated. Random number generators are pre-configured random number engines that let you get going quickly and easily.
You’ll probably be using mt19937
, the Mersenne Twister algorithm. This algorithm has a good ratio of efficiency to randomness, and will probably be suitable for 99% of projects. If you decide to dig deeper and find out more about the other engines/generators and configuration, you probably know what you’re doing.
mt19937
(and several other generators/algorithms) needs to be seeded. To do this, we use the random_device
engine. This engine pulls random data from your operating system and hardware. This is what we use as the seed.
Distributions
The next part is random number distributions. These take the output from an engine and distribute it to turn it into the values you want. You’ll generally use uniform_int_distribution
or uniform_real_distribution
depending on if you want an integer or a real number. There’s also bernoulli_distribution
for boolean values.
Example
Here’s a simple example: generate an integer between 1 and 10 inclusive (inclusive means it includes the limits; therefore possible numbers would be 1, 2, 3, 4, 5, 6, 7, 8, 9, and 10).
We create our random_device
, use it to seed mt19937
, then create the distribution, passing the minimum (1) and maximum(10) in the constructor. Finally, to actually get a number, we use dist(mt)
, passing in our random number engine.
What if we wanted to get floating-point values? The example is nearly identical, except that we use uniform_real_distribution
, which will give us any real number.
If you’re ever debugging and you want to generate the same numbers every time, you can always replace rd()
in mt
’s constructor with a constant value to give it a constant seed.
Finally, booleans. Once again, the example is practically identical, except that we use bernoulli_distribution
:
Sources and Further Reading
Congratulations, now you can stop using rand()
! This post was inspired by the talk “rand() Considered Harmful” by Stephan T. Lavavej. If you have half an hour and want to learn more about why you should use rand()
and more about random number generation in C++, check it out here.
I’ve only talked about the very bare minimum of what most people will need during normal software development, but <random>
has many other engines and distributions. If you’re interested, check out the documentation here.