8

我正在尝试实现以下类:

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。根据参考资料,每个分发实例都有一个reset()成员函数。这意味着它有一个潜在的大内部状态,或者可能是一个缓存。

我的问题是:

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

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

4

4 回答 4

6

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

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

  2. 分发是根据其他一些分发来实现的。你真的可以把它想象成一个更复杂的参数。例如,我的beta 发行版实现包含两个 gamma 发行版,每个发行版都有自己的配置。

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

您的实现似乎很好。Auniform_real_distribution<double>不太可能拥有比您构造它的参数更多的状态。

于 2013-04-15T14:36:58.067 回答
2

查看RandomNumberDistribution 模板策略的文档...

reset()

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

这意味着调用operator()可以更改影响后续调用的状态operator()。这就是reset()存在和operator()不存在的原因const

uniform_real_distribution应该是一个小的简单仿函数,就像你说的。它很可能只包含Real构建它的 2 s,没有其他任何东西。并且reset()不应该为那个做任何事情。

于 2013-04-15T14:35:02.587 回答
2

是的,发行版可以有内部状态。它们通常很小,但如果您担心尺寸,请检查一下。

于 2013-04-15T14:51:38.110 回答
2

状态规范reset()

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

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

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

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

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

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

于 2013-04-15T15:48:04.383 回答