Recently I was trying out a program on generating random numbers in C++ using the random engines
defined in #include
. My program goes
Every time you run random
you reseed the generator with the same or very close to the same seed so you get the same output each iteration or if it does change it will not be by much.
To fix this you only seed the generator once and then keep calling it. By making it static it works as expected as the generator is only created and seeded once you so keep going on its random sequence instead of getting the first random number it would create.
Typically random number generators have an internal value that they put through an algorithm and what the algorithm spits out is what they return for a random number. That number is then retained for the next iteration of the algorithm. This is how we get an random sequence.
If we use the same seed we will get the same output sequence as we start from the same number. In your case your seed will only change each time chrono::steady_clock::now()
advances and your loop is running faster than that so you are getting the same time(seed) each call.
One important thing to know about pseudo random number generators is that they produce a sequence of numbers, and 'randomness' is a quality that the sequence is tested for. That is, you can't say that a particular number is random or not (is '4' a random number?). You instead say whether the sequence of numbers is random or not.
When you "seed" a pRNG you are generally selecting one of the sequences it can generate, and those numbers are random with respect to the rest of the sequence. So in general if you want 'randomness' you want to first select a particular sequence and then to use successive numbers from that particular random sequence.
In your code without static
, you're selecting a sequence on each iteration of the loop, and then using one number from that sequence. Since you're not using many values from the same sequence it's not really much surprise that the result doesn't look random.
When you add static
the engine is no longer being seeded on each iteration of the loop. Instead it's seeded once and each iteration of the loop uses successive values from that sequence, just like you're supposed to do. And this is what static
means on block scoped variables: it means that the variable lives externally to the block it's declared in and that it will only be initialized the first time.
static
does fix your problem, however I would argue that it's not a good solution. static
variables have some similarities to global variables and cause some of the same problems. Instead what I would recommend is for you to explicitly move the engine outside the loop. For example:
int random (int lim, default_random_engine &dre)
{
uniform_int_distribution<> uid(1,lim);
return uid(dre);
}
int main()
{
default_random_engine dre (chrono::steady_clock::now().time_since_epoch().count());
for (int i = 0; i < 10; ++i)
{
cout << random(100, dre) << " ";
}
}
Now the state is being explicitly managed and passed into the random()
function, which is better than relying on the hidden and implicit behavior of a block scoped static
variable.
Also in general I would not recommend using time as a sole source of seed data. A low resolution clock might not provide new seeds frequently enough, and the program being run in different places at the same time can use the same seed.
My recommendation for seeding is:
std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 eng(seed);
(There's no need for beginners to know what mt19937 or any of the other bits are about. They just need to know to paste this at the appropriate location and how to use eng
. In your code you'd need to replace each use of default_random_engine
with mt19937
.)