The new C++11 Standard has a whole chapter dedicated to random number generators. But how do I perform the simplest, most common task that used to be coded like this, but without resorting to the standard C library:
srand((unsigned int)time(0)); int i = rand();
Are there reasonable defaults for random-number engines, distributions, and seeds that one could use out of the box?
C library function - rand()The C library function int rand(void) returns a pseudo-random number in the range of 0 to RAND_MAX. RAND_MAX is a constant whose default value may vary between implementations but it is granted to be at least 32767.
You should be able to do something like:
std::default_random_engine e((unsigned int)time(0)); int i = e();
The quality of the default_random_engine
is implementation dependent. You could also use std::min_rand0
or std::min_rand
.
Probably a better way to seed a random engine is with as true a random number as is available from the implementation rather than use time
.
E.g.
std::random_device rd; std::default_random_engine e( rd() );
Unifying and simplifying some of the samples already provided I will summarize to:
// Good random seed, good engine auto rnd1 = std::mt19937(std::random_device{}()); // Good random seed, default engine auto rnd2 = std::default_random_engine(std::random_device{}()); // like rnd1, but force distribution to int32_t range auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}())); // like rnd3, but force distribution across negative numbers as well auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}()));
Then I ran some tests to see what the defaults look like:
#include <random> #include <functional> #include <limits> #include <iostream> template<class Func> void print_min_mean_max(Func f) { typedef decltype(f()) ret_t; ret_t min = std::numeric_limits<ret_t>::max(), max = std::numeric_limits<ret_t>::min(); uint64_t total = 0, count = 10000000; for (uint64_t i = 0; i < count; ++i) { auto res = f(); min = std::min(min,res); max = std::max(max,res); total += res; } std::cout << "min: " << min << " mean: " << (total/count) << " max: " << max << std::endl; } int main() { auto rnd1 = std::mt19937(std::random_device{}()); auto rnd2 = std::default_random_engine(std::random_device{}()); auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}())); auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}())); print_min_mean_max(rnd1); print_min_mean_max(rnd2); print_min_mean_max(rnd3); print_min_mean_max(rnd4); }
Produces the output:
min: 234 mean: 2147328297 max: 4294966759 min: 349 mean: 1073305503 max: 2147483423 min: 601 mean: 1073779123 max: 2147483022 min: -2147481965 mean: 178496 max: 2147482978
So as we can see, mt19937 and default_random_engine have a different default range, so use of uniform_int_distribution is advised.
Also, default uniform_int_distribution is [0, max_int] (non-negative), even when using a signed integer type. Must provide range explicitly if you want full range.
Finally, its important to remember this at times like these.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With