简体   繁体   中英

std::uniform_real_distribution regenerating same random number

In running the code below, the first half of arr equals the last half. Why? I even tried various seeds, eg std::chrono::system_clock::now().time_since_epoch().count() . Thanks.

#include <algorithm>
#include <iostream>
#include <random>


template<typename DistributionType>
class Rng 
{
public:
        template<typename ...Args>
        Rng(Args&&... args) : dist(args...) { } 

        typename DistributionType::result_type operator()()
        {
                return dist(gen);
        }

private:
        std::default_random_engine gen;

        DistributionType dist;
};


class UniformRealRng : public Rng<std::uniform_real_distribution<double>>
{
public:
        UniformRealRng(const double a, const double b) : Rng(a, b) { } 
};

int main()
{
        constexpr int sz = 6;
        constexpr int k  = sz / 2;
        double arr[sz];

        UniformRealRng rng(0.0, 1.0);
        std::generate(arr, arr + k, rng);
        std::generate(arr + k, arr + sz, rng);

        for (int i = 0; i < sz; ++i)
        {
                std::cout << arr[i];
        }
        std::cout << "\n";
}

std::generate takes its third argument by value, so rng was being copied.

For safety, one could delete copying:

Rng(Rng&)             = delete;   
Rng& operator=(Rng&)  = delete;

Your answer is factually correct but it doesn't solve the problem. It only produces a compilation error when mistakenly(?) one tries to copy the random number generator.

It turns out that the code can be made to be semantically correct by using the std::reference_wrapper library features.

    std::generate(arr, arr + k, std::ref(rng));
    std::generate(arr + k, arr + sz, std::ref(rng));

In this way you are basically forcing to pass the argument by reference. Luckily the reference wrapper overloads the operator() and therefore can be used for the generator without any additional code.

Complete code:

#include <algorithm>
#include <iostream>
#include <random>
#include <functional> //ref

template<typename DistributionType>
class Rng 
{
public:
        template<typename ...Args>
        Rng(Args&&... args) : dist(args...) { } 
//      Rng(Rng&)             = delete;  // this is not needed for it to work 
//      Rng& operator=(Rng&)  = delete;  // you MAY want to copy the generator
        typename DistributionType::result_type operator()()
        {
                return dist(gen);
        }

private:
        std::default_random_engine gen;

        DistributionType dist;
};


class UniformRealRng : public Rng<std::uniform_real_distribution<double>>
{
public:
        UniformRealRng(const double a, const double b) : Rng(a, b) { } 
};

int main()
{
        constexpr int sz = 6;
        constexpr int k  = sz / 2;
        double arr[sz];

        UniformRealRng rng(0.0, 1.0);
        std::generate(arr, arr + k, std::ref(rng));
        std::generate(arr + k, arr + sz, std::ref(rng));

        for (int i = 0; i < sz; ++i)
        {
                std::cout << arr[i];
        }
        std::cout << "\n";
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM