【问题标题】:How to use the C++11 random generators efficiently?如何有效地使用 C++11 随机生成器?
【发布时间】:2014-02-23 22:21:50
【问题描述】:

我正在执行计算实验,这些实验需要可重现。因此,每个实验都使用自己的随机数生成器并记住它的种子:

class Experiment
{
public:
    void operator()();
private:
    unsigned seed_;
    std::mt19937 engine_;
};

问题是引擎需要向下传递到最基本的功能。

假设调用堆栈的下 10 层有一个简单的函数,它需要一个引擎来生成一个介于 0 和 1 之间的随机数。然后需要将该引擎传递给这 10 个调用中的每一个,从而使代码成为乱七八糟的。

我考虑并拒绝了这两种方法:

1. global engine:

我会有一个全局引擎,所有的基本函数都会调用这个引擎。但是,如果我想在不同的线程中运行多个实验,这可能会导致问题。我在多线程方面的经验为零,但我得到了很多针对全局的建议,尤其是在多线程应用程序中,我不想朝着错误的方向迈出一步。

2. local engine in each small function.

每个函数都会在堆栈上创建一个引擎,使用它并在返回时销毁它。然而,这可能会导致性能问题,因为随机数生成器是一个大而复杂的对象。在我的实现中,它有 5000 个字节。

我应该使用什么方法?

【问题讨论】:

  • “然后,需要将该引擎传递给这 10 个调用中的每一个调用,从而使代码变得一团糟。” 你只需要传递 the 引擎,即std::mt19937。您可以在本地创建 distributions,这可能很便宜,例如对于简单的分布,例如uniform_int_distribution。传递一个额外的参数到底有什么麻烦?
  • 可能是线程本地随机生成器。主要缺点:它是隐藏的可变状态。
  • @MartinDrozdik 您现在可以使用全局,以后可以机械地切换到使用线程局部。但是,仍然很难让它与并行性一起工作。您可能希望使用给定的种子启动每个并行工作程序。现在您必须将该种子推入该工人的线程本地。一切都是可行的,但隐含着很多魔法。我宁愿接受 Kerrek 的明确传递所有可变状态的想法。
  • @Martin 如果没有真正充分的理由,我真的不会使用本地线程。从可读性和维护的角度来看,它甚至比全局状态更糟糕(我编写的代码使用了很多 - 出于完全合理的原因 - 这并不有趣)。我对你为什么不能在课堂上共享引擎的解释仍然不完全满意。当然,您可能必须将该参数传递给标准 lib 函数一次,但这至多是单级间接。
  • @dyp 一些分发实现会缓存未使用的熵位,因此每次创建一个可能是浪费的。

标签: c++ multithreading c++11 random


【解决方案1】:

获得可重现随机数的唯一方法是在初始化随机数生成器时使用种子。 对于速度,您不必太在意,因为这些对象并没有那么大。

这是一个例子

#include "stdafx.h"
// uniform_real_distribution
#include <iostream>
#include <random>
#include <vector>
#include <thread>


void generateNumbers(std::vector<double>& vRes, unsigned int nbNumbers, int seed, double & sum)
{
    std::default_random_engine generator(seed);//forcing this parameter will force the results to      be the samme so you only need to keep track  of one number
    std::uniform_real_distribution<double> distribution(0.0,1.0);//uniform distribution between 0 and 1.0

   sum=0.0;
   vRes.resize(nbNumbers);
   for (unsigned int i=0;i<nbNumbers;++i)
   {
       vRes[i]=distribution(generator);
       sum+=vRes[i];
   }
}

int _tmain(int argc, _TCHAR* argv[])
{
    const unsigned int nbNumbers=1000000;
    const int seed=100;
    const int nbThreads=300;
    std::vector<std::vector<double > > vTest(nbThreads);
    std::vector<double> vSum(nbThreads);//vector of checksums: all numbers should be the same as we sum the same random numbers

    for (int currThread=0;currThread<nbThreads;++currThread)
    {
        std::thread th(&generateNumbers, vTest[currThread],nbNumbers,seed,std::ref(vSum[currThread]));
        th.join();
    }

    return 0;
}

此代码在 Release Visual Studio 2012 中的运行时间不到 10 秒。通过使用更少的线程(线程创建很耗时)可以大大改进它,但这给出了想法。

希望对你有帮助,

【讨论】:

  • 请注意,发行版(相对于随机数引擎)在 C++ 中没有标准实现,因此这些发行版提供的随机数不一定是可重现的。另请注意,在您的代码中,所有线程都传递相同的种子。
  • 关于种子,它更多的是具有可复制的行为,而不是具有纯随机函数。我假设在我的示例中所有线程都是独立的,因此使用相同的种子不会有问题。没有什么能阻止你使用不同的
猜你喜欢
  • 2016-06-25
  • 2014-10-07
  • 2013-11-09
  • 2013-12-04
  • 1970-01-01
  • 2012-01-06
  • 2015-06-17
  • 2011-11-05
  • 1970-01-01
相关资源
最近更新 更多