我正在尝试实现以下类:
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
。根据{{3}},每个分发实例都具有reset()
成员函数。这意味着它具有潜在的大内部状态或缓存。
我的问题是:
发行版是否具有内部状态?如果是,为什么?标准是否说明了这个州的规模?
按照我的方式进行实施是个好主意吗?有没有更好的办法?
答案 0 :(得分:6)
分布可能很好,通常会有一些状态。该标准在此不作任何限制。我可以想到可以使用状态的几个原因:
随机数分布通常有一些参数来配置它。例如,正态分布具有均值和方差参数。这些是其州的一部分,因为它们必须保持在调用之间。
分发是根据其他一些分发实现的。你真的可以把它想象成一个更复杂的参数。例如,我的beta distribution实现保留了两个gamma分布,每个分布都有自己的配置。
分发可能会随着时间而改变。没有什么可说的,分发的重复调用需要是独立的。这是reset
成员函数的用武之地。大多数发行版都有operator()
的独立调用,因此reset
函数实际上什么都不做(它的定义是空的)。但是,如果您的呼叫是相关的,reset
应该将分发恢复到下一个呼叫独立的状态。
您的实施似乎很好。 uniform_real_distribution<double>
不太可能有更多的状态表明您使用它构造的参数。
答案 1 :(得分:2)
查看RandomNumberDistribution template policy ...
的文档 <强> reset()
强>
重置分发对象的内部状态。打电话给 这个函数,下一次调用分发对象上的operator() 将不依赖于之前对operator()的调用。
这意味着对operator()
的调用可以改变影响后续operator()
调用的状态。这就是为什么reset()
存在以及为什么operator()
不是const
。
uniform_real_distribution
应该是一个简单的小函子,就像你说的那样。它可能只是持有它构造的2 Real
而没有别的。并且reset()
不应该为那个做任何事情。
答案 2 :(得分:2)
是的,分发可以有内部状态。它们通常很小,但如果您担心尺寸,请检查它。
答案 3 :(得分:2)
reset()
州的规范:
d的后续使用不依赖于任何引擎在调用reset之前产生的值。
这意味着通常operator()
的调用可能取决于先前调用operator()
时使用的引擎生成的值。也就是说,分发可以缓存或以其他方式依赖于先前的引擎结果。
例如,伯努利分布可能只需要一位来产生结果,而给定引擎一次提供32位,因此分布可能会缓存32位而不会再次调用任何引擎,直到生成了32个值。
另一个例子是分布(我忘了哪个),其中公共算法一次自然地产生两个值,因此分布可能会保存下一个调用的第二个值。
所以是的,分发可以有内部状态。该标准没有对这种状态的大小提出要求。
如果你问是否可以在线程之间共享分配,那么不,这不是一个好主意。这样做的一件事是数据争用并导致未定义的行为,除非您添加同步或使分布const(即使您可以使用标准库的实现,这是不可移植的)。其次,标准只有在以特定方式使用分布和引擎并且在多个引擎之间共享分配时才能提供保证。共享分发实际上不太可能产生不良数据,但是IMO仍然不应该这样做。相反,您可能让每个线程都保留自己的引擎和自己的分发。