c++c++11random

Why not just use std::random_device?


I am a bit confused about the C++11 random library.

What I understand: we need two separate concepts:

I know how to use the true random number generator random_device to seed a PRNG like mt19937.

But what I don't understand is why not just use the true random number generator alone:

std::random_device rd;
std::uniform_int_distribution<int> dist(1, 5);

// get random numbers with:
dist(rd);

As far as I can tell this works well.

Instead, what I found on most examples/sites/articles is that a PRNG is used (with the true random number generator being used just to seed the PRNG):

std::random_device rd;
std::mt19937 e{rd()}; // or std::default_random_engine e{rd()};
std::uniform_int_distribution<int> dist{1, 5};

// get random numbers with:
dist(e);

I am not talking about special use, e.g. cryptography, just your basic getting started articles.

My suspicion is because std::mt19937 (or std::default_random_engine) accepts a seed, it can be easier to debug by providing the same seed during a debug session.

Also, why not just:

std::mt19937 e{std::random_device{}()};

Solution

  • Also, why not just:

    std::mt19937 e{std::random_device{}()};

    It might be fine if you only will do this once, but if you will do it many times, it's better to keep track of your std::random_device and not create / destroy it unnecessarily.

    It may be helpful to look at the libc++ source code for implementation of std::random_device, which is quite simple. It's just a thin wrapper over std::fopen("/dev/urandom"). So each time you create a std::random_device you are getting another filesystem handle, and pay all associated costs. (And whenever you read from it, you are making a system call.)

    On windows, as I understand, std::random_device represents some call to a microsoft crypto API, so you are going to be initializing and destroying some crypto library interface everytime you do this.

    It depends on your application, but for general purposes I wouldn't think of this overhead as always negligible. Sometimes it is, and then this is great.

    I guess this ties back into your first question:

    Instead, this is what I found on most examples/sites/articles:

     std::random_device rd;
     std::mt19937 e{rd()}; // or std::default_random_engine e{rd()};
     std::uniform_int_distribution<int> dist{1, 5};
    

    At least the way I think about it is:

    The way I think about it is, std::random_device is supposed to be like an improved version of seeding with time(NULL) -- that's a low bar, because time(NULL) is a pretty crappy seed all things considered. I usually use it where I would have used time(NULL) to generate a seed, back in the day. I don't really consider it all that useful outside of that.