如何引用随机发生器?

时间:2015-03-17 15:16:53

标签: c++ c++11 visual-c++

我正在尝试在一个范围内生成randoms。我有多个线程中的多个对象将调用此函数,该函数在单例类中,因此只有一个对象被调用。但是我注意到我的随机数更接近范围的中间,因此,对于前200次调用,它们开始慢慢分散在给定范围内。

  1. 每次重新实例化和再播种都会造成伤害 功能叫?我确实读过this answer,但并不完全 理解它。
  2. 我尝试将std::uniform_real_distribution<double> dis保留为类变量,但我不断收到uniform_real_distribution不是std::成员的错误,为什么会这样?
  3. 我试图将std::shared_ptr< std::uniform_real_distribution<double> > _uniform_distribution保留为类变量,但仍然出错。
  4. 引用随机生成器的正确方法是什么?
  5. 这个问题的正确解决方案是什么?

  6. double getRandom(Object* foo){  
     std::random_device rnd;
     std::mt19937 gen(rnd());
     std::uniform_real_distribution<double> dis(0, foo->limit);
     double random = dis(gen);
     return random;
    }
    

4 个答案:

答案 0 :(得分:6)

您应该只为一次随机数生成器播种。除此之外,您的getRandom功能是正确的。至于如何在类上下文中使用所有这些,这里是你如何做到的:

#include <random>

class RNG
{
public:
    RNG::RNG() : gen(std::random_device()()) {} // Seeds the mt19937.

    double getRandom(Object* foo){  
        std::uniform_real_distribution<double> dis(0, foo->limit);
        double random = dis(gen);
        return random;
    }

private:
    std::mt19937 gen;
};

因此,在程序开始时,您实例化一个RNGmt19937将被播种一次。然后,只要您需要随机数,就可以在同一getRandom上拨打RNG

答案 1 :(得分:2)

如果要使分发成为成员变量,则必须在每次调用时创建新参数:

#include <random>
#include <iostream>

class Object
{
public:
    double limit = 100.0;
};

class MyRandomGen
{
    // typedef this for readability
    typedef typename std::uniform_real_distribution<double>::param_type param_type;

    std::mt19937 gen;
    std::uniform_real_distribution<double> dis;

public:
    MyRandomGen(): gen(std::random_device()()) {} // seed only once

    double getRandom(Object* foo)
    {
        param_type range {0, foo->limit}; // new distribution parameters
        return dis(gen, range);
    }
};


int main()
{
    Object o;
    MyRandomGen gen;

    for(auto i = 0; i < 10; ++i)
        std::cout << gen.getRandom(&o) << '\n';
}

<强>输出:

48.4072
11.9905
39.0123
49.2113
69.3635
0.369986
19.9654
42.4251
92.9024
29.7522

答案 2 :(得分:0)

您可以制作genrng个静态局部变量。这将确保随机数生成器初始化一次。

在每个使用新随机种子的调用期间,函数生成器都会重新初始化。

答案 3 :(得分:0)

将分发作为类成员应该不是问题。将std::random_device的实例作为类成员将是一个问题(至少在许多情况下)是因为它无法复制。幸运的是,你真的不需要它作为类成员的实例,因为(如前所述)你只想调用它一次。由于其他人已经发布了他们对更正的发电机类的想法,我想我也会添加我的:

class generator {
    std::uniform_real_distribution<double> dis;
    std::mt19937 gen;
public:
    generator(double lower = 0.0, double upper = 100.0) 
        : gen(std::random_device()()), dis(lower, upper) {}

    double operator()() {
        return dis(gen);
    }
};

你可以使用这个(例如):

generator g;

std::generate_n(std::ostream_iterator<double>(std::cout, "\n"), 20, generator());

至少在一些快速测试中,我没有看到任何明显的中心趋势。例如:

22.832
82.3414
20.7805
28.9464
6.72104
95.8591
1.92738
70.2699
0.447961
70.591
0.549306
27.9672
10.243
23.0606
76.155
67.6821
63.7346
20.4228
77.9004
39.6607

在前20名中击中0.4到95.9之间的数字似乎可以很好地覆盖范围。

作为一个与大多数无关的东西,在64位编译器上,您可能希望使用mt19937_64而不是mt19937,至少作为规则。