23

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?

Raedwald
  • 46,613
  • 43
  • 151
  • 237
Bartosz Milewski
  • 11,012
  • 5
  • 36
  • 45
  • Wikipedia? http://en.wikipedia.org/wiki/C%2B%2B0x#Extensible_random_number_facility – flight Aug 27 '11 at 22:31
  • 2
    What's wrong with the code you have? AFAIK, the new random number generators were added for more "serious" applications where the aspects of the random number generation really matter. – GManNickG Aug 27 '11 at 22:33
  • 3
    @GMan: To be fair, several of the random number engines in the new standard could be described as simple and fast and I wouldn't view them as particularly "serious". – CB Bailey Aug 27 '11 at 22:57
  • 6
    Every time I use standard C library inside C++ I feel like I'm doing something unseemly. And look at the cast! There has to be a better way. – Bartosz Milewski Aug 27 '11 at 23:08
  • The cast is unnecessary. `time(0)` returns a `time_t` result, which is of some numeric type; it will be implicitly converted to unsigned int (`srand`'s argument type). And since the C++ standard library includes the C standard library, there's nothing wrong with using it. If the C++ library didn't (prior to C++11) have its own random number generation support, it's probably because it wasn't necessary, because it was already there. – Keith Thompson Aug 28 '11 at 00:06
  • @Keith: The cast is necessary to suppress a warning where time_t is defined as a 64 bit value. – Benjamin Lindley Aug 28 '11 at 00:12
  • `rand()` is obsolete- if you decide to use something from the standard C library use `arc4random()`, `arc4random_uniform()`, or `random()`. I prefer `arc4random()` but if you need a reproducible sequence of numbers `random()` is the best option. – sbooth Aug 28 '11 at 13:13
  • @sbooth: But none of `random`, `arc4random` or `arc4random_uniform` are part of the standard C library? – CB Bailey Aug 28 '11 at 13:21
  • @Charles Bailey At least on my system, `arc4random()`, `random()`, and `rand()` are all declared in `stdlib.h`. – sbooth Aug 28 '11 at 15:14
  • @sbooth: I'm not quite sure what point you are making. – CB Bailey Aug 28 '11 at 15:32
  • 1
    @Charles Bailey I'm not sure either! You're right, only `rand` is part of standard C, and `random` is part of POSIX and `arc4random` is provided in BSD. – sbooth Aug 28 '11 at 16:24

7 Answers7

30

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() );
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 1
    To clarify, do you mean "should" as in "this doesn't exist but it ought to" or sd "this exists and your compiler ought to support it"? – GManNickG Aug 27 '11 at 23:30
  • 8
    Informational: The biggest difference between this answer and the OP's example is that the coder controls where the engine's state is: It is in `e`. In the OP's code the state is hidden as static data inside `rand`. This is an important difference when dealing with multithreaded programs. `rand` has to have some kind of protection to make it thread safe. `default_random_engine` doesn't. If you want to call it from multiple threads, you provide the synchronization mechanism yourself, externally. This means `default_random_engine` can be faster if you don't need to synchronize it. – Howard Hinnant Aug 27 '11 at 23:36
  • @GMan: It means that the only implementation that I have access to at the moment doesn't support it so I haven't been able to test my code ;) . – CB Bailey Aug 27 '11 at 23:38
  • 1
    @Charles: I just tested your code on libc++. Given the proper includes, it works. One might need `std::` on `time` . `std::default_random_engine` is required by conforming hosted C++11 platforms. On libc++ it is equivalent to `minstd_rand` which is a `linear_congruential_engine`. – Howard Hinnant Aug 27 '11 at 23:44
  • 1
    @Howard: What about seeding? Is there a default in the Standard to seed a random number generator using some implementation dependent magic? Or is time(0) still the only practical and portable way? I'm not using random numbers for anything fancy like encryption -- just to randomize my tests. – Bartosz Milewski Aug 28 '11 at 18:35
  • @BartoszMilewski: Do you have an objection to the `random_device` approach? – CB Bailey Aug 28 '11 at 18:58
  • 1
    @Bartosz: I rather like Charles' `random_device` suggestion if you don't need or want repeatability in the random sequence. Or you can continue using `time`. Or there is this thing called `seed_seq` which is a souped-up seeder on steroids. I've implemented the thing and am still not quite sure when to use it. :-) And for extra fun, `seed_seq` itself can be seeded! – Howard Hinnant Aug 29 '11 at 16:20
  • 1
    Quote from cppreference.com: `std::random_device` may be implemented in terms of an implementation-defined pseudo-random number engine if a non-deterministic source (e.g. a hardware device) is not available to the implementation. In this case each `std::random_device` object may generate the same number sequence. **This means, on some systems, using a default constructed randomdevice will always seed your mt19937 etc. with the same number!** – Marti Nito Dec 16 '15 at 13:07
6

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.

mmocny
  • 8,775
  • 7
  • 40
  • 50
  • 2
    Of note, there is a 64 bit version of `std::mt19937`: `std::mt19937_64` that returns 64 bits of randomness per call. `auto rnd5 = std::mt19937_64(std::random_device{}()); // min: 4879020137534 mean: 1655417118684 max: 18446741225191893648` – Sean Apr 06 '13 at 08:17
  • By the way, is it safe to re-use the same random number engine with many distributions and std::bind() or is it better to bind each distribution to a new engine instance? How's this: `std::mt19937_64 random = std::mt19937_64(std::random_device{}());` `auto randomCardinality = std::bind(std::uniform_int_distribution(1, 4), random);` `auto randomValue = std::bind(std::uniform_real_distribution(-1.0, 1.0), random);` – Xharlie Sep 15 '15 at 10:27
2

Here you go. Random doubles in a range:

// For ints
// replace _real_ with _int_, 
// <double> with <int> and use integer constants

#include <random>
#include <iostream>
#include <ctime>
#include <algorithm>
#include <iterator>

int main()
{
    std::default_random_engine rng(std::random_device{}()); 
    std::uniform_real_distribution<double> dist(-100, 100);  //(min, max)

    //get one
    const double random_num = dist(rng);

    //or..
    //print 10 of them, for fun.
    std::generate_n( 
        std::ostream_iterator<double>(std::cout, "\n"), 
        10, 
        [&]{ return dist(rng);} ); 
    return 0;
}
Carl
  • 43,122
  • 10
  • 80
  • 104
2

If your existing code was appropriate before the new standard, then it will continue to be. The new random number generators were added for applications which require a higher quality of pseudo-randomness, e.g. stochastic simulation.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
2

I use the following code in my project. 'engine' and 'distribution' can be one of the provided by the library.

#include <random>
#include <functional>
#include <iostream>
...
std::uniform_int_distribution<unsigned int> unif;
std::random_device rd;
std::mt19937 engine(rd());
std::function<unsigned int()> rnd = std::bind(unif, engine);

std::cout << rnd() << '\n';
dimitri
  • 874
  • 8
  • 11
0

Random number generation is a difficult problem. There is no truly random way to do it. If you are just generating randomness to seed a game environment then your approach should be fine. rand() has several shortcomings.

If you are needing randomness to generate encryption keys then you're S.O.L. The best way in that case is to go out to the operating system, which usually has mechanism. On POSIX that's random() (or read from /dev/random if you're so disposed). On Windows you can use the CryptoAPI:

https://www.securecoding.cert.org/confluence/display/seccode/MSC30-C.+Do+not+use+the+rand%28%29+function+for+generating+pseudorandom+numbers

Adam Hawes
  • 5,439
  • 1
  • 23
  • 30
0

You could use RC4 to generate random bytes. This probably has the properties that you want. It is fast and fairly simple to implement. The sequence is repeatable across all implementations when the seed is known, and completely unpredictable when the seed is not known. http://en.wikipedia.org/wiki/RC4