【问题标题】:Using same random number generator across multiple functions在多个函数中使用相同的随机数生成器
【发布时间】:2015-11-26 03:39:11
【问题描述】:

我相信随机数生成器 (RNG) 应该只播种一次,以确保结果的分布符合预期。

我正在用 C++ 编写一个蒙特卡罗模拟,它由一个主函数(“A”)多次调用另一个函数(“B”)组成,其中在 B 中生成了大量随机数。

目前,我在 B 中执行以下操作:

void B(){
    std::array<int, std::mt19937::state_size> seed_data;
    std::random_device r;

    std::generate(seed_data.begin(), seed_data.end(), std::ref(r));
    std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); //perform warmup
    std::mt19937 eng(seq);

    std::uniform_real_distribution<> randU(0,1);

    double myRandNum = randU(eng);

    //do stuff with my random number
}

如您所见,每次调用函数 B 时,我都会创建一个新的随机数生成器。据我所知,这是浪费时间 - RNG 仍然可以生成更多的随机数!

我已经尝试过制作“eng”extern,但这会在使用 g++ 时产生错误:

错误:“eng”同时具有“extern”和初始化程序 extern std::mt19937 eng(seq);

我怎样才能使随机数生成器“全局”,以便我可以多次使用它?

【问题讨论】:

  • 为什么不将所有需要的 RNG 对象放在一个结构中,并通过引用将其传递到任何你想要的地方?

标签: c++ random global-variables global


【解决方案1】:

注意一刀切的规则。 “全球人是邪恶的”就是其中之一。 RNG 应该是一个全局对象。 (警告:每个线程都应该有自己的 RNG!)我倾向于将我的线程包裹在一个单例地图中,但只需在 main() 的开头播种和预热一个就足够了:

std::mt19937 rng;

int main()
{
  // (seed global object 'rng' here)
  rng.dispose(10000); // warm it up

对于您的使用场景(每次调用生成多个 RN),为每个函数调用创建本地分布应该没有任何问题。

另外一件事:std::random_device 不是你的朋友——它可以在任何时候出于各种愚蠢的原因throw。确保将其包装在 try..catch 块中。或者,我建议这样做,使用 平台特定 方式来获取真正的随机数。 (在 Windows 上,使用 Crypto API。在其他一切上,使用 /dev/urandom/。)

希望这会有所帮助。

【讨论】:

  • 非常有帮助,谢谢。但是我现在如何播种rng?我试过(例如)eng(r);,其中rstd::random_device,但我收到错误“错误:不匹配调用'(std::mt19937 {aka std::mersenne_twister_engine [...cut out因为非常长...] /usr/include/c++/4.9/bits/random.h:546:7: 注意:候选人期望 0 个参数,提供 1 个“。你如何播种?
  • 使用seed()成员函数:rng.seed(your_seed_sequence)
  • 为什么需要预热随机数生成器?我从来没有听说过预热发电机。
【解决方案2】:

您不需要传递任何内容或声明任何内容,因为 mt19937 和 uniform_real_distribution 之间的交互是通过全局变量进行的。

std::array<int, std::mt19937::state_size> seed_data;
std::random_device r;

std::generate(seed_data.begin(), seed_data.end(), std::ref(r));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); //perform warmup
std::mt19937 eng(seq);


B()

...

void B() 
{

    std::uniform_real_distribution<> randU(0,1);
...

【讨论】:

  • 感谢您的回答。但是我希望 std::mt19937 成为我通过引用传递的对象,并且我无法在函数 B 之外声明它,因为似乎我无法在函数 B 外部初始化( std::mt1997 eng; )然后稍后再说函数内部,( eng(seq); )。我该如何解决这个问题?
  • eng(seq) 正在调用构造函数。您想重新播种还是使用新种子?然后你需要做eng.seed(seq);
  • 我只想播种一次。之后每次调用randU(eng)获取随机数。
  • 那么您是否尝试过在 A() 中进行初始化,然后在 B() 中使用 std::uniform_real_distribution&lt;&gt; randU(0,1);
  • 在我看来已经在使用全局对象,而您实际上不需要传递任何东西。
猜你喜欢
  • 1970-01-01
  • 2021-12-31
  • 2010-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-30
  • 1970-01-01
相关资源
最近更新 更多