C++11 introduced the header <random>
with declarations for random number engines and random distributions. That's great - time to replace those uses of rand()
which is often problematic in various ways. However, it seems far from obvious how to replace
srand(n);
// ...
int r = rand();
Based on the declarations it seems a uniform distribution can be built something like this:
std::default_random_engine engine;
engine.seed(n);
std::uniform_int_distribution<> distribution;
auto rand = [&](){ return distribution(engine); }
This approach seems rather involved and is surely something I won't remember unlike the use of srand()
and rand()
. I'm aware of N4531 but even that still seems to be quite involved.
Is there a reasonably simple way to replace srand()
and rand()
?
You can create a simple function like this:
And later use it like this:
As mentioned here
Full disclosure: I don't like
rand()
. It's bad, and it's very easily abused.The C++11 random library fills in a void that has been lacking for a long, long time. The problem with high quality random libraries is that they're oftentimes hard to use. The C++11
<random>
library represents a huge step forward in this regard. A few lines of code and I have a very nice generator that behaves very nicely and that easily generates random variates from many different distributions.Given the above, my answer to you is a bit heretical. If
rand()
is good enough for your needs, use it. As bad asrand()
is (and it is bad), removing it would represent a huge break with the C language. Just make sure that the badness ofrand()
truly is good enough for your needs.C++14 didn't deprecate
rand()
; it only deprecated functions in the C++ library that userand()
. While C++17 might deprecaterand()
, it won't delete it. That means you have several more years beforerand()
disappears. The odds are high that you will have retired or switched to a different language by the time the C++ committee finally does deleterand()
from the C++ standard library.You don't need a cryptographically secure PRNG for that. You don't even need Mersenne Twister. In this particular case,
rand()
probably is good enough for your needs.Update
There is a nice simple replacement for
rand()
andsrand()
in the C++11 random library:std::minstd_rand
.The function
std::minstd_rand::operator()()
returns astd::uint_fast32_t
. However, the algorithm restricts the result to between 1 and 231-2, inclusive. This means the result will always convert safely to astd::int_fast32_t
(or to anint
ifint
is at least 32 bits long).Abusing the fact that engines return values directly
All engines defined in
<random>
has anoperator()()
that can be used to retrieve the next generated value, as well as advancing the internal state of the engine.It shall however be noted that all engines return a value of some unsigned integral type, meaning that they can potentially overflow a signed integral (which will then lead to undefined-behavior).
If you are fine with using unsigned values everywhere you retrieve a new value, the above is an easy way to replace usage of
std::srand
+std::rand
.If you only need to seed the engine once
There is no need to first default-construct a
std::default_random_engine
(which is just a typedef for some engine chosen by the implementation), and later assigning a seed to it; this could be done all at once by using the appropriate constructor of the random-engine.If you however need to re-seed the engine, using
std::random-engine::seed
is the way to do it.If all else fails; create a helper-function
Even if the code you have posted looks slightly complicated, you are only meant to write it once.
If you find yourself in a situation where you are tempted to just copy+paste what you have written to several places in your code it is recommended, as always when doing copy+pasting; introduce a helper-function.
How about
randutils
by Melissa O'Neill of pcg-random.org?From the introductory blog post:
Assuming you want the behavior of the C-style
rand
andsrand
functions, including their quirkiness, but with good random, this is the closest I could get.The
replacement::*
functions can be used exactly like theirstd::*
counterparts from<cstdlib>
. I have added asrand
overload that takes no arguments and seeds the engine with a “real” random number obtained from astd::random_device
. How “real” that randomness will be is of course implementation defined.The engine and the distribution are held as
thread_local
static
instances so they carry state across multiple calls but still allow different threads to observe predictable sequences. (It's also a performance gain because you don't need to re-construct the engine or use locks and potentially trash other people's cashes.)I've used
std::default_random_engine
because you did but I don't like it very much. The Mersenne Twister engines (std::mt19937
andstd::mt19937_64
) produce much better “randomness” and, surprisingly, have also been observed to be faster. I don't think that any compliant program must rely onstd::rand
being implemented using any specific kind of pseudo random engine. (And even if it did, implementations are free to definestd::default_random_engine
to whatever they like so you'd have to use something likestd::minstd_rand
to be sure.)