Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

random number generators from c++ <random> library in realistic programs

Tags:

c++

random

c++11

I'm learning about the library, which improves on the old rand and srand in many ways. But with the rand it's clear that there is one and only one random number generator that gets called and updated whenever rand is used, wherever that is in your program. With the new way I'm not sure how to imitate this behaviour efficiently and with good style. For instance what if I want a dice roll and, aping online examples written in the main procedure, I write an object with a method like this:

class foo{
    public:
    float getDiceRoll(){
        std::random_device rd;
        std::default_random_engine e1(rd());
        std::uniform_int_distribution<int> uniform_dist(1, 6);
        return uniform_dist(e1);
   }
}

This looks terrible because the engine is re-created every time you want a dice roll. This is a bit of a contrived case, but in a large program you are going to have to put the declaration of the random number generator somewhere. As a first attempt to use I just want there to be one generator for all random numbers, like in the old days. What is the nicest way to achieve this? Easily available examples online are all written straight into the main procedure and so they do not answer this basic question. I cannot think of anything that doesn't seem like taking a sledgehammer to crack a nut. Any help would be great.

like image 201
Joe_H Avatar asked Jan 08 '23 06:01

Joe_H


2 Answers

For a trivial case like this, I'd make them all static and not worry about it too much. And definitely return an int!

int getDiceRoll() {
    static std::random_device rd;
    static std::default_random_engine gen(rd());
    static std::uniform_int_distribution<int> dis(1, 6);

    return dis(gen);
}
like image 173
Barry Avatar answered Jan 09 '23 21:01

Barry


You can wrap it all up in a class like this:

#include <random>
#include <iostream>

/**
 * (P)seudo (R)andom (N)umber (G)enerator
 */
template<typename Type = int>
class PRNG
{
    // easier to use param_type
    using param_type = typename std::uniform_int_distribution<Type>::param_type;

    // store an instance of the generator/distribution in the class object
    std::mt19937 gen;
    std::uniform_int_distribution<Type> dis;

public:
    // seed generator when creating
    PRNG(): gen(std::random_device()()) {}

    Type get(Type from, Type to)
    {
        // only need to create a light weigt param_type each time
        return dis(gen, param_type{from, to});
    }
};

int main()
{
    PRNG<int> prng;

    for(auto i = 0U; i < 10; ++i)
        std::cout << "die roll " << i << ": " << prng.get(1, 6) << '\n';
}

Sample Output:

die roll 0: 2
die roll 1: 6
die roll 2: 1
die roll 3: 5
die roll 4: 6
die roll 5: 3
die roll 6: 3
die roll 7: 6
die roll 8: 3
die roll 9: 2
like image 43
Galik Avatar answered Jan 09 '23 21:01

Galik