3

I am trying to encapsulate a class to generate random numbers controlling by seed and by the range of random values. I want to call this class from different parts of my codes, with different ranges, but using the same seed.

Following suggestions of other post (Encapsulated Random number generator in C++-11 using boost) I implemented the following code:

My Random Number Generator class:

    #include <random>
    #include <iostream>

    typedef std::mt19937                     ENG;    // Mersenne Twister
    typedef std::uniform_int_distribution<> iDIST;   // Uniform Integer Distribution

    class RNG {
      private:
         ENG eng;
         iDIST idist;

      public:
         iDIST::result_type igen() { return idist(eng); }
         RNG(ENG eng_,int imin,int imax)
         : idist(imin,imax)
         {eng = eng_; }

    };

A function to create an object of RNG class and print the random values:

    void myfunc(ENG eng_,int imin, int imax, int N){
        RNG myirn(eng_,imin,imax);
        for (int i = 0; i < N; i++){
            std::cout << myirn.igen() << std::endl;
        }
        return;
    }

My main function:

    int main(int argc, char **argv){
        int myseed = 1;
        int N = 5;
        int imin1 = 1;
        int imax1 = 10;

    //Seed globally
        ENG eng_;
        eng_.seed(myseed);

        std::cout << "Range = [" << imin1 << "," << imax1 << "]" << std::endl;
        myfunc(eng_,imin1,imax1,N);
        std::cout << "Range = [" << imin1 << "," << imax1 << "]" << std::endl;
        myfunc(eng_,imin1,imax1,N);


    return 0;
    }

As you can see, my strategy was to seed my random number generator globally (in the main function) and pass the variable eng_ as a parameter to the function func, that will instantiate the RNG object and print the random values. If everything were correct, this code should print 2 sequences of 5 random numbers within the same range but with different values. However, they are exactly the same sequence. Could anyone help me to fix this?

Community
  • 1
  • 1
Charles Santana
  • 130
  • 1
  • 1
  • 12
  • You're passing ENG by value and not by reference. – Svalorzen Jul 15 '15 at 12:57
  • Thanks for your suggestion, @Svalorzen. Could you please be more specific? In my main function I pass an ENG object to myfunc. And in myfunc I pass an ENG object to the RNG constructor. Did you change the code and had a good result (two different sequences of 5 random values within the same range)? – Charles Santana Jul 15 '15 at 13:34

2 Answers2

5

Since you want to use the same engine, you have to use the same engine. (That much is a singleton.) Pass a reference to RNG, store and use a reference within RNG. Minor changes to your code make this so (one of the comments already pointed this out):

ENG &eng;
RNG(ENG &eng_, int imin, int imax)
    : idist(imin, imax), eng(eng_) {}

void myfunc(ENG &eng_, int imin, int imax, int N)

But I like it better if the engine is hidden in RNG like this:

class RNG {
private:
    static ENG eng;
    iDIST idist;
public:
    static void seed(int s) { eng.seed(s); }
    RNG(int imin, int imax) : idist(imin, imax) {}
    int generate() { return idist(eng); }
};

// the one generator, stored as RNG::eng
ENG RNG::eng;

// print some generated numbers from a range
void printRandomNumbers(int imin, int imax, int N){
    std::cout << "Range = [" << imin << "," << imax << "]" << std::endl;
    RNG myirn(imin, imax);
    for (int i = 0; i < N; i++){
        std::cout << myirn.generate() << std::endl;
    }
    return;
}

int main()
{
    //Seed globally
    int myseed = 1;
    RNG::seed(myseed);
    printRandomNumbers(1, 10, 5);
    printRandomNumbers(11, 20, 5);
    printRandomNumbers(21, 30, 5);
    return 0;
}
Kenny Ostrom
  • 5,639
  • 2
  • 21
  • 30
  • looking at the code, I see how easy it is to accidentally pass it by value in one place and mess everything up. I'm going to see what happens when I make the seed and engine class static inside RNG. – Kenny Ostrom Jul 15 '15 at 13:47
  • Exactly, @KennyOstrom. You got exactly what I want to do. I want to be able to inject a new range if I need. Your suggestion worked like a charm. Thank you! – Charles Santana Jul 15 '15 at 13:51
  • I accepted your answer because it was clear and did exactly what I needed to do. But, of course, any improvement is very welcome, it will not change my vote. – Charles Santana Jul 15 '15 at 13:54
  • Amazing! It seems to be a better option when I need to split the functions into many files. Thanks. – Charles Santana Jul 15 '15 at 14:27
2

Singleton Pattern

In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The concept is sometimes generalized to systems that operate more efficiently when only one object exists, or that restrict the instantiation to a certain number of objects. The term comes from the mathematical concept of a singleton.

class RandomGenerator
{
    public:
        static RandomGenerator& get_instance( void )
        {
            static RandomGenerator instance;
            return instance;
        }

    private:
        RandomGenerator() { /*seed generator*/ };

        RandomGenerator( RandomGenerator const& ) = delete;
        void operator=( RandomGenerator const& ) = delete;
};

Alternatively

Dependency Injection

In software engineering, dependency injection is a software design pattern that implements inversion of control for resolving dependencies. An injection is the passing of a dependency (a service or software module) to a dependent object (a client). The service is made part of the client's state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.

class MyClass
{
    public:
        void set_random_generator( const RandomGenerator& value );
}
Ben Crowhurst
  • 8,204
  • 6
  • 48
  • 78
  • That was my first thought, but he specifically wants different ranges, but coming from the same engine. – Kenny Ostrom Jul 15 '15 at 13:36
  • He might be able to inject a new range as needed, but I think it's safer and easier to keep the engine as the singleton, like he originally posted, and just make a new RNG for each separate range. – Kenny Ostrom Jul 15 '15 at 13:42