I am doing some random number generation and getting fishy behaviour. Here is my code:
// initialized earlier... in the constructor of a class
boost::mt19937 *rng = new boost::mt19937();
rng->seed(time(NULL));
// actual use here.
for (int i = 0; i < 10; ++i)
{
test();
}
void test()
{
boost::normal_distribution<> distribution(10, 10);
boost::variate_generator< boost::mt19937, boost::normal_distribution<> > resampler(*rng, distribution);
const double sample = (resampler)(); // always the same value.
}
Am I misusing the random sampling in boost? What have I done wrong to make that always be the same value. I initialize the random number generator in the constructor so it should always spit out a different value (not getting reinitialized)
The problem is with the line boost::variate_generator< boost::mt19937, boost::normal_distribution<> > resampler(*rng, distribution);
. This constructor take its parameters by value (see the documentation). So each resampler
starts off with an identical copy of the generator and calls it once.
Edit: Shafik noticed the same thing just a little after I did. If you really can't hoist the initialization out of the loop, you can also re-seed the generator. There are many ways to accomplish this depending on your application. Below is just one example:
void test()
{
static unsigned int seed = 0
rng->seed((++seed) + time(NULL));
boost::normal_distribution<> distribution(10, 10);
boost::variate_generator< boost::mt19937, boost::normal_distribution<> > resampler(*rng, distribution);
const double sample = (resampler)(); // always the same value.
}
Note: do not re-seed rng
with just time(NULL)
, as that may return the same value several times if you call test()
in a tight loop.
It is because you are instantiating the class in the function test
if you move them outside then it works as expected. You are starting off each instance with the same generator. see this minimal case:
int main()
{
boost::mt19937 *rng2 = new boost::mt19937();
rng2->seed(time(NULL));
boost::normal_distribution<> distribution(0, 1);
boost::variate_generator< boost::mt19937, boost::normal_distribution<> > resampler(*rng2, distribution);
for (int i = 0; i < 10; ++i)
{
std::cout << resampler() << std::endl ;
}
}
If you change the loop to work like your posted code then you see the same problem:
for (int i = 0; i < 10; ++i)
{
boost::normal_distribution<> distribution(0, 1);
boost::variate_generator< boost::mt19937, boost::normal_distribution<> > resampler(*rng2, distribution);
std::cout << resampler() << std::endl ;
}
The 'why' has been addressed in other answers. Here is how to fix it without re-seeding (which defeat the point of using a generator) : initialize normal_distribution
and variate_generator
once, together with mt19937
.
In your class, take care of defining theses members in the proper order.
As a side note, 'new' is useless, you could simply write :
boost::mt19937 rng ;