HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppModerate

Random number generation seeding in C++

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
randomgenerationnumberseeding

Problem

I wrote a small function to return a random in a given range:

int random_in_range(int min, int max) {
    std::random_device rd;
    std::mt19937 rng(rd());
    std::uniform_int_distribution uni(min, max);
    return uni(rng);
}


But I read somewhere that you should only seed a random number generator once leading me to believe that the function should really be:

std::random_device rd;
std::mt19937 rng(rd());
int random_in_range(int min, int max) {
    std::uniform_int_distribution uni(min, max);
    return uni(rng);
}


I later tested both to see if one was clearly better than the other (in terms of randomness) and got results which do not make things any clearer.

First example result with 10 runs, making a decision of 1 or 0:

for (int i = 0; i 0100100001


The second example result with 10 runs, making a decision of 1 or 0:

for (int i = 0; i 1011000110


The two results don't seem too strange leading me to be confused about how I should initialize random number generators.
Basically, what I am asking is: which of these two example (or something else if both are wrong) would be used in order to guarantee the lowest amount of bias?

Solution

If you were going to get a number from random_device at every call, you might as well just use it directly:

int random_in_range(int min, int max) {
    std::random_device rd;
    std::uniform_int_distribution uni(min, max);
    return uni(rd());
}


std::random_device is intended to be a front-end for a truly random bit source. The major shortcoming is that in many cases it has fairly limited bandwidth, so you'd prefer to avoid calling it every time you need a number.

If you do want to use mt19937 (a perfectly fine idea in many cases) I'd personally use a function-object instead of a function:

class random_in_range { 
    std::mt19937 rng;
public:
    random_in_range() : rng(std::random_device()()) {}
    int operator()(int low, int high) { 
        std::uniform_int_distribution uni(low, high);
        return uni(rng);
    }
};


This does have some shortcoming though: people may use a temporary of this type in a loop:

for (int i=0; i<10; i++)
    std::cout << random_in_range()(0, 1);


...which puts you back where you started. You need to do something like:

random_in_range r;
for (int i=0; i<10; i++)
    std::cout << r(0, 1);


...to get the results you want (i.e., seed once, call multiple times).

Code Snippets

int random_in_range(int min, int max) {
    std::random_device rd;
    std::uniform_int_distribution<int> uni(min, max);
    return uni(rd());
}
class random_in_range { 
    std::mt19937 rng;
public:
    random_in_range() : rng(std::random_device()()) {}
    int operator()(int low, int high) { 
        std::uniform_int_distribution<int> uni(low, high);
        return uni(rng);
    }
};
for (int i=0; i<10; i++)
    std::cout << random_in_range()(0, 1);
random_in_range r;
for (int i=0; i<10; i++)
    std::cout << r(0, 1);

Context

StackExchange Code Review Q#101525, answer score: 12

Revisions (0)

No revisions yet.