如何引用随机生成器?
How to reference random generator?
我正在尝试生成一个范围内的随机数。我在多个线程中有多个对象将调用此函数,该函数在单例中 class 因此只有一个对象被调用。但我注意到我的随机数更接近范围的中间,因此,在前 200 次调用中彼此接近,然后它们开始慢慢分散在给定范围内。
- 每次重新实例化和重新播种是否有害?
函数调用?我读过 this answer 但没读完
明白了。
- 我试图将
std::uniform_real_distribution<double> dis
保留为 class 变量,但我不断收到错误消息,指出 uniform_real_distribution
不是 std::
的成员,这是为什么?
- 我试图将
std::shared_ptr< std::uniform_real_distribution<double> > _uniform_distribution
保留为 class 变量,但仍然出现错误。
- 引用随机生成器的正确方法是什么?
- 这个问题的正确解决方案是什么?
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;
}
您应该为随机数生成器设置种子一次。除此之外,您的 getRandom
函数是正确的。至于如何在 class 上下文中使用所有这些,您可以按照以下方法进行操作:
#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;
};
因此在程序开始时,您实例化一个 RNG
,mt19937
将被播种一次。然后,只要您需要随机数,就可以在同一个 RNG
上调用 getRandom
。
如果你想让你的分布成为一个成员变量,那么你必须在每次调用时创建新的参数:
#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
您可以创建 gen
和 rng
静态局部变量。这将确保随机数生成器被初始化一次。
在您的函数生成器中,每次使用新的随机种子调用时都会重新初始化。
将分发作为 class 成员应该不是问题。将 std::random_device
的实例作为 class 成员将是一个问题(至少在许多情况下),因为它无法复制。幸运的是,您实际上并不需要它的实例作为 class 成员,因为(如前所述)您只想调用它一次。由于其他人已经发布了他们对更正生成器的想法 class,我想我也将添加我的想法:
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
,至少通常是这样。
我正在尝试生成一个范围内的随机数。我在多个线程中有多个对象将调用此函数,该函数在单例中 class 因此只有一个对象被调用。但我注意到我的随机数更接近范围的中间,因此,在前 200 次调用中彼此接近,然后它们开始慢慢分散在给定范围内。
- 每次重新实例化和重新播种是否有害? 函数调用?我读过 this answer 但没读完 明白了。
- 我试图将
std::uniform_real_distribution<double> dis
保留为 class 变量,但我不断收到错误消息,指出uniform_real_distribution
不是std::
的成员,这是为什么? - 我试图将
std::shared_ptr< std::uniform_real_distribution<double> > _uniform_distribution
保留为 class 变量,但仍然出现错误。 - 引用随机生成器的正确方法是什么?
- 这个问题的正确解决方案是什么?
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;
}
您应该为随机数生成器设置种子一次。除此之外,您的 getRandom
函数是正确的。至于如何在 class 上下文中使用所有这些,您可以按照以下方法进行操作:
#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;
};
因此在程序开始时,您实例化一个 RNG
,mt19937
将被播种一次。然后,只要您需要随机数,就可以在同一个 RNG
上调用 getRandom
。
如果你想让你的分布成为一个成员变量,那么你必须在每次调用时创建新的参数:
#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
您可以创建 gen
和 rng
静态局部变量。这将确保随机数生成器被初始化一次。
在您的函数生成器中,每次使用新的随机种子调用时都会重新初始化。
将分发作为 class 成员应该不是问题。将 std::random_device
的实例作为 class 成员将是一个问题(至少在许多情况下),因为它无法复制。幸运的是,您实际上并不需要它的实例作为 class 成员,因为(如前所述)您只想调用它一次。由于其他人已经发布了他们对更正生成器的想法 class,我想我也将添加我的想法:
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
,至少通常是这样。