Encapsulated Random number generator in C++-11 usi

2019-06-01 11:16发布

问题:

I am trying to generate random numbers in C++-11 using boost. I have seen some nice posts about this and I was able to generate random numbers putting all my code in the main function, like suggested in this webpage (http://www.sitmo.com/article/generating-random-numbers-in-c-with-boost/).

However, I was not able to encapsulate a random number generator (RNG) that can be called by different functions. I would like this encapsulated RNG to receive as parameters the range of numbers and a random seed. I tried to do it by following the suggestions of this post (Boost random number generator).

I implemented the code below. But it fails in both: the change of random seed and the definition of the range of values.

#include <boost/random/variate_generator.hpp>
#include <boost/generator_iterator.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/normal_distribution.hpp>

typedef boost::mt19937                     ENG;    // Mersenne Twister
typedef boost::normal_distribution<double> DIST;   // Normal Distribution
typedef boost::variate_generator<ENG,DIST> GEN;    // Variate generator

class RNG {

  private:
     ENG eng;
     DIST dist;

  public:
     GEN gen;
     RNG(double min,double max,int seed)
     : dist(min,max), gen(eng,dist)
     {eng.seed(seed); }
};

And this is my main function.

int main(int argc, char **argv){
    if(argc == 2){
        int myseed = atoi(argv[1]);
        double minimum = 1.;
        double maximum = 10.;
        int N = 20;

        RNG myrn(minimum,maximum,myseed);

        for (int i = 0; i < N; i++){
            std::cout << myrn.gen() << std::endl;
        }
            return 0;
    }
    exit(1);
}

This implementation generates the same sequence of random numbers independently of the seed I use. Also, the random numbers are not in the range between minimum and maximum. Does any of you know how to fix these two issues?

回答1:

boost::variate_generator takes its constructor arguments by value, so when you pass it a default constructed mt19937 in the mem-initializer list it makes a copy of it. Seeding the engine within the body of the constructor will have no effect on that copy. To fix this, change the template argument type to a reference.

typedef boost::variate_generator<ENG&,DIST> GEN;
//                                  ^

Next, I don't think you want a normal_distribution, its constructor arguments are not the minimum and maximum of the range of values produced by the distribution, but the mean and standard deviation of the distribution.

The distribution you're probably looking for is uniform_real_distribution.

typedef boost::random::uniform_real_distribution<> DIST;

After making these fixes, your code should behave as you want it to.

Live demo


With a C++11 compiler you don't even need Boost to do this, the <random> header provides all the components you need. The only significant difference is the lack of a variate_generator class, instead you invoke the distribution object using the engine as the argument when you want to generate a random number.

#include <random>

typedef std::mt19937                     ENG;    // Mersenne Twister
typedef std::uniform_real_distribution<> DIST;   // Uniform Distribution

class RNG {

  private:
     ENG eng;
     DIST dist;

  public:
     DIST::result_type gen() { return dist(eng); }
     RNG(double min,double max,int seed)
     : dist(min,max)
     {eng.seed(seed); }
};

Live demo