Silversonic
Silversonic

Reputation: 1329

Creating a function to return a random number in a specified range

I'm running through C++ Primer and I just completed the chapter disussing the <random> utilities. A few questions ask me:

Exercise 17.28: Write a function that generates and returns a uniformly distributed random unsigned int each time it is called.

Exercise 17.29: Allow the user to supply a seed as an optional argument to the function you wrote in the previous exercise.

Exercise 17.30: Revise your function again this time to take a minimum and maximum value for the numbers that the function should return.

These questions want me to use the discussed random_default_engine and uniform_int_distribution classes. The first question is simple enough:

#include <random>
unsigned randomUns(){
    static default_random_engine e;
    static uniform_int_distribution<unsigned> u;
    return u(e);
}

Using the suggestion that the objects are made static so that they aren't destroyed after a call, and so every call to the function generates a new number rather than starting at the beginning of the sequence every time. The difficulty comes in the last two questions, my solution originally was:

unsigned randomUns(unsigned minV, unsigned maxV, default_random_engine::result_type seed = 0){
    static default_random_engine e(seed);
    static uniform_int_distribution<unsigned> u(minV, maxV);
    return u(e);
}

Except that was before I realised that static variables only get initialised once. So this function would initialise u with the arguments from the call to randomUns and then never again. I would only be able to use this function with one range. E.g.

cout << randomUns(2,3) << " " << randomUns(5,6);

might output 2 3 despite the second range being different.

A similar problem exists with seed. How would I approach this? For the engine/seed I might be able to rewrite the function like so:

static default_random_engine::result prev = 0;
static default_random_engine e(seed);
if(prev != seed){ 
    e.seed(seed)
    prev = seed;
}

But of course this wouldn't be a truly random function.

randomUns(1,2,50);
randomUns(1,2,60);
randomUns(1,2,50);

as I know that third call will produce the same output as the first, since the engine got reset back to the same state. For changing the range on the distribution I'm not sure about at all.

The variables can't be destroyed after each call because otherwise the function will always output the same sequence - so they must be static or exist outside the scope of the function. On the other hand I need to be able to control the range/seed and so the variables need to be modifiable within the function.

Is this possible with the utilities provided by random_default_engine and uniform_int_distribution?

Upvotes: 4

Views: 989

Answers (1)

Some programmer dude
Some programmer dude

Reputation: 409166

Keep the engine static but the distribution non-static, e.g.

unsigned randomUns(unsigned minV, unsigned maxV, default_random_engine::result_type seed = 0){
    static default_random_engine e(seed);
    uniform_int_distribution<unsigned> u(minV, maxV);
    return u(e);
}

To allow a custom seed being changed every call, store the seed too and if different then recreate the engine.

Maybe something like

unsigned randomUns(unsigned minV, unsigned maxV, default_random_engine::result_type seed = 0){
    static default_random_engine e(seed);
    static default_random_engine::result_type last_seed = seed;

    if (seed != last_seed){
        e = default_random_engine(seed);
        last_seed = seed;
    }

    uniform_int_distribution<unsigned> u(minV, maxV);
    return u(e);
}

Upvotes: 2

Related Questions