fuji
fuji

Reputation: 1203

share random_number_engine between different methods within a class in c++11

I have a class which needs random numbers in a couple of different member methods. I'm using C++11 and I think it is not feasible to newly create a random number generator in each method.

Is it possible to share the random number generator across the class by making it a member attribute of the class or maybe a typedef, while still ensuring good randomness?

How would I does this, maybe you can point me to a small example? Where should I set the seed for the random engine, I want to use a Mersenne twister engine with different types of distributions (normal & uniform).

Upvotes: 2

Views: 2517

Answers (2)

bames53
bames53

Reputation: 88225

Engines and distributions are values and can be members just like other objects with value types.

You should seed the engine when it is created, which means, if it's a member, when your object is constructed. My example uses an in-class initializer with random_device to seed the engine by default. It also allows the seed to be specified, for reproducible, testable results.

I would avoid exposing too much of the implementation details, such as providing a more complete interface for interacting with the engine, as that breaks encapsulation. These should be internal, hidden details of the implementation.

std::mt19937 make_seeded_engine() {
    std::random_device r;
    std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
    return std::mt19937(seed);
}

struct Foo {
    std::mt19937 eng = make_seeded_engine();
    std::uniform_int_distribution<> dist1 {1, 20};
    std::uniform_real_distribution<> dist2 {0.0, 100.0};

    Foo() = default;

    template<typename SeedSeq>
    Foo(SeedSeq &&seed) : eng(seed) {}

    int bar() {
        return dist1(eng);
    }

    double baz() {
        return dist2(eng);
    }    
};

You need to at least store the engine between usages, because the guarantees for a random sequence to be uniformly distributed only hold for sequences of repeated calls to the same engine object. There's no guarantee about a sequence produced by using different engines.

And in fact the same is true of distributions, although I don't know of implementations where creating a new distribution each time would actually produce incorrect results (there are implementations where distributions cache values for various reasons, however, so creating distributions every time could perform worse and produce a different sequence).

For example, the common algorithm for computing a normal distribution produces two values at once. Implementations of `std::normal_distribution do this and cache the second value to use every other call. The following program exhibits this.

#include <iostream>
#include <random>

int main() {
    typedef std::mt19937 Engine;
    typedef std::normal_distribution<> Distribution;
    Engine eng(1);
    Distribution dist;

    for (int i=0; i<10; ++i)
        std::cout << dist(eng) << ' ';
    std::cout << '\n';

    eng.seed(1);
    for (int i=0; i<10; ++i)
        std::cout << Distribution()(eng) << ' ';
    std::cout << '\n';
}

With VC++2012 I get the output:

0.156066 0.3064 -0.56804 -0.424386 -0.806289 -0.204547 -1.20004 -0.428738 -1.18775 1.30547
0.156066 -0.56804 -0.806289 -1.20004 -1.18775 -0.153466 0.133857 -0.753186 1.9671 -1.39981

Notice that the sequence produced when creating a new distribution every iteration contains only every other value of the sequence produced with a single distribution.

Upvotes: 7

Matt Kline
Matt Kline

Reputation: 10507

Is it possible to share the random number generator across the class by making it a member attribute of the class or maybe a typedef, while still ensuring good randomness?

Absolutely. Provided that you initialize it with a varying seed (or let it use the default), you should get "good" randomness each time you call the RNG. In fact, I would suggest that using a separate RNG for each method is not only costly, but a bad design.

As for how to implement various distributions, http://en.cppreference.com/w/cpp/numeric/random has some good examples (such as this one).

Upvotes: 1

Related Questions