These days, it’s easy to do the right thing.
Don’t do this:
- Don’t use
std::rand()
. Ever. - Don’t use
std::random_shuffle()
to permute containers. (Too easy to misuse; can usestd::rand()
under the hood.) - Don’t use any kind of clock for a seed.
- Don’t use mod (
%
) to get a random value into a range. It introduces bias. - Don’t use
std::default_random_engine
– it’s probably not the best choice and can vary by implementation.
Do this:
- Use
std::random_device()
as a source of entropy. It should be the best randomness your OS can supply. - Use
std::shuffle()
to permute containers. Its interface doesn’t permit misuse likestd::random_shuffle()
. - Use
std::mt19937
(orstd::mt19937_64
) as an RNG engine. (Or consider PCG, but that’s not standard – yet.) - Seed the engine with as much seed data as it requires for its internal state (using its
state_size
member), otherwise you’re throwing away entropy. Forstd::mt19937
this is 624 32-bit values (not just a single 32-bit value). Usestd::seed_seq
to help with initialization. - RNG engines (particularly
std::mt19937
) can be expensive to initialize (and have large amounts of internal state), so don’t put them on the stack unnecessarily. Consider usingthread_local
. - Use
std::uniform_int_distribution
(and the other distributions) to get your random number into the required range. The distributions are carefully crafted to be unbiased (why you shouldn’t use%
). Note that they work on closed (inclusive) ranges, not half-open ranges – otherwise the max value would be unreachable.
Wrong:
// init RNG
srand(time(NULL));
// get random number
int n = rand() % 100;
Right:
// init RNG
std::array 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 gen(seq);
// get random number
std::uniform_int_distribution dis(0,99);
int n = dis(gen);
Edit: Alternatively, Melissa O’Neill has produced randutils which wraps C++11 random number generation in a nice interface, and does the right thing. She knows what she’s talking about: her work on randomness is worth your time.
Hey Ben, is std::rand() the same as the older rand(), and that’s why we should avoid it due to being low quality and offering a low range of output values?
Yes, it’s just the old LCG.
It is worthing to write an input iterator instead of using std::array so that we can avoid extra memory usage.