1

The C++ TR1 random number generation scheme has improved the old C runtime library in terms of keeping a separate state for random engines in different threads, or for independent random sequences. The old library has a global state machine, and this is usually bad.

However, when implementing an algorithm that requires deterministic random sequences, I find it annoying to have to pass the engine down to any method that should be drawing numbers from such a sequence. From a design perspective, the code that initializes the random seed doesn't need to know which methods down the stack are using random numbers. Yet those inner methods cannot initialize their own random engines, because:

  1. they lack the knowledge to create a unique reproducible seed
  2. memory requirements prevent keeping a separate state for the many downstream clients

To clarify, the downstream methods do not need to draw numbers from the same sequence as the main method, but they do need to be independent and reproducible in different runs.

Any idea on how to solve this conundrum elegantly?

EDIT

Some code to clarify the situation

typedef std::mt19937 RandEng;

class PossibleRandomConsumer;

class RandomProvider {
public:
    void foo() {
        std::uniform_int<> uni;
        uni(eng, 17); // using the random engine myself
        std::for_each(children.begin(), children.end(), [](PossibleRandomConsumer& child) { 
            // may or may not need a random number. if it does, it has to be different than from other children, and from other providers
            child.DoSomething(eng); 
        });
    }
private:
    RandEng eng; // unique seed per RandomProvider
    std::array<PossibleRandomConsumer,10000> children; // lots of these...
};
killogre
  • 1,730
  • 15
  • 26
  • Do you actually need multiple different random number sequences or is one global RNG sufficient? – thiton Apr 17 '12 at 17:50
  • 1
    Each 'client' needs a different sequence. Passing the same engine to each client sequentially does the job (sort of a global RNG), but having to pass it is not elegant – killogre Apr 17 '12 at 17:59
  • @killogre: What's so inelegent about it? You're passing each function the information that it needs to do its job. It needs a RNG, so you pass it an RNG. – Nicol Bolas Apr 17 '12 at 18:02
  • 1
    @Nicol Bolas, it's inelegant, because if someone two or more steps down the stack needs a random number, it's an implementation detail I don't want to know about – killogre Apr 17 '12 at 18:25
  • 1
    @killogre: But they don't need ***a*** random number generator. They need a *specific* RNG. – Nicol Bolas Apr 17 '12 at 18:46

1 Answers1

0

Not an easy question without knowing some detail about your architecture. So this is just a try:
How about passing down references to an interface that knows how to provide random numbers. This interface may just have one function that returns an amount of random numbers if asked. In this way you can hide the implementation details from the downstream functions (methods) and passing constant references around is nearly for free. You can even decide at top level where those random numbers come from (file, random number generator, your room temperature, ...).

class RandomNumberProvider {
    public:  
        typedef std::vector<int> RandomArray;  
        virtual RandomArray Generate(unsigned numNumbers) = 0;
};  

void Consumer(const RandomNumberProvider& rndProvider) {  
    RandomNumberProvider::RandomArray rndNumber;   
    rndNumber = rndProvider(10); // get a sequence of 10 random numbers  
    ...
}  

something like this.

pag3faul7
  • 428
  • 2
  • 7
  • if I have to pass the interface downstream then I know that someone downstream might require it. Currently I'm passing the random engine down just in case some implementation needs it. – killogre Apr 17 '12 at 18:30
  • OK I just wanted to show you a level of abstraction that might be helpful. So if your implementation does not know if it needs an random generator you may want to implement an static instance that can be asked for an random number generator. Maybe a singleton or global instance of a class that can provide such stuff. – pag3faul7 Apr 17 '12 at 18:40
  • thanks, @pag3faul7. I was trying to avoid static instances (same as old C rand()). A singleton is not appropriate, but one instance per provider (see my edit example above) might be a good idea – killogre Apr 17 '12 at 18:51