【问题标题】:What is a C++11 random distribution made of?什么是 C++11 随机分布?
【发布时间】:2013-04-07 16:53:47
【问题描述】:

我正在尝试实现以下类:

typedef std::mt19937 Engine;

class Interval 
{
public:
    double upperBoundary;
    double lowerBoundary;
    double generateUniformRandomNumber(Engine& engine);
};

我希望该类在多线程环境中工作。每个线程都有自己的 Engine 对象实例,并将 Engine 传递给任何具有随机行为的类的对象。

为了以 C++11 的方式统一生成随机数,generateUniformRandomNumber 的实现必须是这样的:

uniform_real_distribution<double> distribution_; // private member of Interval

double Interval::generateUniformRandomNumber(Engine& engine)
{
    return distribution_(engine); 
}

问题是我不懂 C++11 发行版。我知道 C++11 随机数引擎可以是非常大的对象(几千字节),但是分布呢?起初我认为分布只是简单的函子,其中operator() 是一个pure const 函数,但它似乎既不是pure 也不是const。根据reference,每个分发实例都有一个reset()成员函数。这意味着它有一个潜在的大内部状态,或者可能是一个缓存。

我的问题是:

  1. 发行版是否有内部状态?如果是,为什么?标准是否说明了该州的规模?

  2. 像我一样进行实施是个好主意吗?有没有更好的办法?

【问题讨论】:

  • 例如,我实现了一个beta distribution,它的状态实际上是两个伽马分布。 reset 什么都不做,因为它唯一的要求是任何后续值都独立于以前使用的引擎。
  • @sftrabbit 那么reset() 是否存在,以防有人在您调用operator() 时进行累积状态的分配?例如,每次对 operator() 的后续调用都可以为您提供越来越大的范围......这也解释了为什么我猜 operator() 不是 const?
  • @Dave 是的,当您不断调用 operator() 时,没有理由无法更改分布。这只是意味着结果值取决于以前的调用。

标签: c++ random c++11


【解决方案1】:

查看文档以获取 RandomNumberDistribution template policy...

reset():

重置分发对象的内部状态。致电后 此函数,下一次调用分发对象上的 operator() 将不依赖于之前对 operator() 的调用。

这意味着对operator() 的调用可以更改状态,从而影响对operator() 的后续调用。这就是为什么reset() 存在以及为什么operator() 不是const

uniform_real_distribution 应该是一个小的简单仿函数,就像你说的那样。它可能只包含它构建的 2 个Reals,没有别的。而reset() 不应该为此做任何事情。

【讨论】:

    【解决方案2】:

    一个发行版可能非常好并且通常会有一些状态。该标准在这里没有给出任何限制。我可以想到可能使用状态的几个原因:

    1. 随机数分布通常有一些参数来配置它。例如,正态分布具有均值和方差参数。这些是其状态的一部分,因为它们必须在调用之间保持。

    2. 该发行版是根据其他发行版实现的。你真的可以把它想象成一个更复杂的参数。例如,我的beta distribution 实现保留了两个伽马分布,每个分布都有自己的配置。

    3. 分布可能会随着时间而改变。没有什么可以说分发的重复调用需要是独立的。这就是reset 成员函数的用武之地。大多数发行版都有对operator() 的独立调用,因此reset 函数实际上什么都不做(它的定义是空的)。但是,如果您的调用是依赖的,reset 应该将分发恢复到下一个调用独立的状态。

    您的实现看起来不错。 uniform_real_distribution&lt;double&gt; 不太可能拥有比您构造它的参数更多的状态。

    【讨论】:

      【解决方案3】:

      是的,分布可以有内部状态。它们通常很小,但如果您担心大小,请检查一下。

      【讨论】:

        【解决方案4】:

        reset() 的规范声明:

        d 的后续使用不依赖于任何引擎在调用 reset 之前生成的值。

        这意味着operator() 的正常调用可能取决于先前调用operator() 时使用的引擎产生的值。也就是说,分发可以缓存或以其他方式依赖于以前的引擎结果。

        例如,伯努利分布可能只需要一个位即可产生结果,而给定引擎一次提供 32 位,因此该分布可能会缓存 32 位,并且在生成 32 个值之前不会再次调用任何引擎。

        另一个例子是一个分布(我忘记了),其中常用算法自然地一次产生两个值,因此分布可能会保存第二个值以供下次调用。

        所以是的,发行版可以有内部状态。标准对这种状态的大小没有要求。

        如果您问是否可以在线程之间共享分布,那么不,这不是一个好主意。一方面,这样做会导致数据竞争并导致未定义的行为,除非您添加同步或使分发为 const(即使您可以通过标准库的实现来做到这一点,也不是可移植的)。其次,该标准仅在您以特定方式使用发行版和引擎时做出保证,并且在多个引擎之间共享发行版不是这种方式。共享分布实际上不太可能产生不良数据,但 IMO 不这样做仍然是个好主意。相反,您可能让每个线程都保留自己的引擎和自己的分布。

        【讨论】:

          猜你喜欢
          • 2013-04-07
          • 1970-01-01
          • 1970-01-01
          • 2020-01-08
          • 1970-01-01
          • 2012-12-10
          • 1970-01-01
          • 1970-01-01
          • 2016-04-26
          相关资源
          最近更新 更多