我有一个类需要几个不同成员方法中的随机数。我使用C++11
,我认为在每种方法中新建一个随机数生成器是不可行的。
是否可以通过使类成为该类的成员属性或者类型为typedef来共享整个类中的随机数生成器,同时仍然确保良好的随机性?
我怎么做到这一点,也许你能指出一个小例子?我应该在哪里设置随机引擎的种子,我想使用具有不同类型分布的Mersenne twister
引擎(normal
& uniform
)。
答案 0 :(得分:6)
引擎和发行版是值,可以像其他具有值类型的对象一样成员。
您应该在创建引擎时播种引擎,这意味着,如果它是成员,则构建对象时。我的示例使用带有random_device
的类内初始化程序默认为引擎设定种子。它还允许指定种子,以获得可重复的,可测试的结果。
我会避免暴露太多的实现细节,比如提供一个更完整的界面来与引擎交互,因为这打破了封装。这些应该是实现的内部隐藏细节。
std::mt19937 make_seeded_engine() {
std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
return std::mt19937(seed);
}
struct Foo {
std::mt19937 eng = make_seeded_engine();
std::uniform_int_distribution<> dist1 {1, 20};
std::uniform_real_distribution<> dist2 {0.0, 100.0};
Foo() = default;
template<typename SeedSeq>
Foo(SeedSeq &&seed) : eng(seed) {}
int bar() {
return dist1(eng);
}
double baz() {
return dist2(eng);
}
};
您需要至少将引擎存储在用法之间,因为随机序列的均匀分布保证仅适用于对同一引擎对象的重复调用序列。不能保证使用不同引擎产生的序列。
事实上,对于发行版也是如此,虽然我不知道每次实际产生不正确结果时创建新发行版的实现(但是有些实现,其中发行版由于各种原因而缓存值,因此创建发行版每次都会表现得更糟,并产生不同的序列。)
例如,用于计算正态分布的通用算法一次产生两个值。 `std :: normal_distribution的实现执行此操作并缓存第二个值以使用每个其他调用。以下程序展示了这一点。
#include <iostream>
#include <random>
int main() {
typedef std::mt19937 Engine;
typedef std::normal_distribution<> Distribution;
Engine eng(1);
Distribution dist;
for (int i=0; i<10; ++i)
std::cout << dist(eng) << ' ';
std::cout << '\n';
eng.seed(1);
for (int i=0; i<10; ++i)
std::cout << Distribution()(eng) << ' ';
std::cout << '\n';
}
使用VC ++ 2012,我得到输出:
0.156066 0.3064 -0.56804 -0.424386 -0.806289 -0.204547 -1.20004 -0.428738 -1.18775 1.30547
0.156066 -0.56804 -0.806289 -1.20004 -1.18775 -0.153466 0.133857 -0.753186 1.9671 -1.39981
请注意,每次迭代创建新分布时产生的序列仅包含使用单个分布生成的序列的每个其他值。
答案 1 :(得分:1)
是否有可能通过使类成为该类的成员属性或者类型定义来共享整个类中的随机数生成器,同时仍然确保良好的随机性?
绝对。如果您使用不同的种子初始化它(或让它使用默认值),每次调用RNG时都应该获得“良好”的随机性。事实上,我建议对每种方法使用单独的RNG不仅代价高昂,而且设计糟糕。
至于如何实现各种发行版,http://en.cppreference.com/w/cpp/numeric/random有一些很好的例子(例如this one)。