【问题标题】:Generating random numbers with mt19937_64 for multithread monte carlo simulation使用 mt19937_64 为多线程蒙特卡罗模拟生成随机数
【发布时间】:2017-06-11 04:13:52
【问题描述】:

我正在多个线程上运行蒙特卡罗模拟。我有一个处理随机数生成的 Draw 类。它使用mt19937-64作为std::uniform_real_distribution的生成器。

template<typename T, typename R>
class Draw {
    T _dist;
    typedef decltype(_dist.min()) result_type;

    public:
    // Draw : T R -> Draw
    //! GIVEN
    //!   1. dist - A distribution.
    //!   2. gen - A random number generator.
    Draw(T dist, R gen) : _dist(dist), _gen(gen) {}

    virtual ~Draw() = default;

    // () : -> Value
    //! GIVEN
    //! RETURNS
    //! value drawn from the distribution, which can be any result type
    //!   supported by the distribution.
    result_type operator()() const { return _draw(); }

    // seed : NonNegInt -> Void
    //! GIVEN
    //!   1. seed - A random number generator (RNG) seed.
    //! EFFECT
    //!   Seeds the RNG with the given seed.
    void seed(unsigned long seed) { _gen.seed(seed);}

    private:
    R _gen;

    // draw : -> Value
    // GIVEN:
    // RETURNS: A value drawn from the distribution, which can be any result
    //          type supported by the distribution.
    std::function<result_type()> _draw = bind(_dist,_gen);
};

// standard uniform distribution ~ Unif(a=0, b=1)
class DrawUnif :
public Draw<std::uniform_real_distribution<double>,std::mt19937_64>
{
    typedef std::mt19937_64 genny;
    typedef std::chrono::system_clock clk;
    typedef std::uniform_real_distribution<double> dist;

    public:
    DrawUnif() :
      Draw(dist{0,1}, genny(clk::now().time_since_epoch().count())) {}

    //! GIVEN
    //! -----
    //!   1. seed - A seed for the random number generator.
    DrawUnif(unsigned long seed) : Draw(dist{0,1}, genny(seed)) {}

     virtual ~DrawUnif() = default;
};

每个线程都可以访问下面的共享指针

  typedef std::shared_ptr<DrawUnif> DrawUnifShrPtr;
  DrawUnifShrPtr    _unif;

由哪个初始化

_unif(DrawUnifShrPtr(new DrawUnif { seed }));

每个线程都有几个函数,它们经常调用draw(*_unif) 来生成一个随机数。结果似乎正确,但我想知道是否有两个线程调用

draw(*_unif) 

同时,会发生什么?

然后我为每个队列分配了一个具有不同种子的新 shared_pointer:

 _unif(std::make_shared<DrawUnif>(*c._unif));
 _unif->seed(a_new_seed);

现在结果看起来很奇怪!每个线程都得到完全相同的随机!数字并提供完全相同的结果。 总结一下:

1- 如果多个胎面同时调用平局会发生什么?

2- 为什么不同的种子会得到完全相同的结果。

【问题讨论】:

  • 我以前没有处理过这个问题,所以我不能 100% 确定这一点,但我认为 STL 随机库在同时被调用方面不是线程安全的. (您可以在多个线程中使用一个生成器/分发,只要您处理我相信的同步)。此外,在多线程环境中播种随机数生成器的另一个问题是,如果您基于时间播种,很多时间函数都有课程分辨率,因此多个线程在大约同一时间调用相同的东西很可能会得到结果相同。

标签: c++ multithreading random mersenne-twister


【解决方案1】:

STL 中的随机数支持不是线程安全的。您可以添加它(以牺牲一些运行时性能为代价),或者为每个线程使用单独的随机数生成器。每个线程的生成器都需要一个不同的种子。在最简单的形式中,这可以通过增加每个新线程的种子值来实现,但是如果新的模拟运行得太快,则会存在重复序列的风险。您还可以通过适当的常数缩放现有种子值,然后将线程索引添加到它。或者您可以查看seed_sequences,并使用线程索引或 id 作为序列的辅助输入。

【讨论】:

  • 我使用增量为 1 的种子。每个线程几乎在另一个线程之后立即被调用。如果种子是用户输入并且不依赖于时间,为什么要说“如果新的模拟运行太快会冒重复序列的风险”?
  • @g1368 指的是基于时间的种子。
  • @g1368 每个线程都需要自己的_unif 副本(以及在模拟期间写入的任何其他数据)。
  • 我知道使用 seed_sequence 可能是更好的方法。但是你知道为什么我用不同的种子得到完全相同的结果,增量为 1 吗?我希望这能正常工作!
  • 这不是意味着每个线程的 _unif 的单独副本吗? _unif(std::make_shared(*c._unif)); _unif->种子(a_new_seed);每个线程的输出被写入一个单独的文件。
猜你喜欢
  • 1970-01-01
  • 2010-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-11
  • 2023-03-08
  • 1970-01-01
相关资源
最近更新 更多