我使用适配器类包装boost随机数生成器来实现蒙特卡洛例程。在对类的成员函数编写单元测试时,我假设.discard(unsigned int N)的行为是绘制N个随机数而不存储它们,从而推进了rng的状态。增强代码是:
void discard(boost::uintmax_t z)
{
if(z > BOOST_RANDOM_MERSENNE_TWISTER_DISCARD_THRESHOLD) {
discard_many(z);
} else {
for(boost::uintmax_t j = 0; j < z; ++j) {
(*this)();
}
}
}
支持我的假设。但是,我发现.discard(1)产生的序列与没有丢弃的相同序列不是一个数字。代码:
#include <iostream>
#include <iomanip>
#include <random>
#include <boost/random.hpp>
int main()
{
boost::mt19937 uGenOne(1);
boost::variate_generator<boost::mt19937&, boost::normal_distribution<> > distOne(uGenOne, boost::normal_distribution<>());
boost::mt19937 uGenTwo(1);
boost::variate_generator<boost::mt19937&, boost::normal_distribution<> > distTwo(uGenTwo, boost::normal_distribution<>());
distTwo.engine().discard(1);
unsigned int M = 10;
std::vector<double> variatesOne(M);
std::vector<double> variatesTwo(M);
for (unsigned int m = 0; m < M; ++m) {
variatesOne[m] = distOne();
variatesTwo[m] = distTwo();
}
for (unsigned int m = 0; m < M; ++m)
std::cout << std::left << std::setw(15) << variatesOne[m] << variatesTwo[m] << std::endl;
return 0;
}
输出
2.28493 0.538758
-0.668627 -0.0017866
0.00680682 0.619191
0.26211 0.26211
-0.806832 -0.806832
0.751338 0.751338
1.50612 1.50612
-0.0631903 -0.0631903
0.785654 0.785654
-0.923125 -0.923125
我对.discard操作错误的解释是什么?为什么前三个输出中的两个序列不同,然后相同?
(此代码在msvc 19.00.23918上编译,g ++ 4.9.2在cygwin上编译,结果相同)。
答案 0 :(得分:0)
这里的问题似乎是引擎没有被正确修改,或者分布正在增加做一些额外的工作。如果我们直接使用引擎
1.7911e+09 4.28288e+09
4.28288e+09 3.09377e+09
3.09377e+09 4.0053e+09
4.0053e+09 491263
491263 5.5029e+08
5.5029e+08 1.29851e+09
1.29851e+09 4.29085e+09
4.29085e+09 6.30312e+08
6.30312e+08 1.01399e+09
1.01399e+09 3.96591e+08
它产生
boost::variate_generator
由于我们丢弃了第一个输出,因此我们预期的是1个移位序列。
所以你对丢弃的工作方式是正确的。通过{{1}}进行此操作时,我不确定为什么会出现差异。我不明白为什么前三个数字不同但所有其他输出都匹配。
答案 1 :(得分:0)
只是添加上一个答案评论中的重要细节。正如@NathanOliver所提到的,.discard会增加生成器,它会将制服发送到正态分布,这会将制服转换为正态分布。 boost :: normal_distribution使用Ziggurat algorithm这是一种“接受/拒绝”类型的算法。它绘制随机均匀,操纵它,然后检查它是否在所需的分布中。如果没有,它将被拒绝并且是一个新的随机统一。
for(;;) {
std::pair<RealType, int> vals = generate_int_float_pair<RealType, 8>(eng);
int i = vals.second;
int sign = (i & 1) * 2 - 1;
i = i >> 1;
RealType x = vals.first * RealType(table_x[i]);
if(x < table_x[i + 1]) return x * sign;
if(i == 0) return generate_tail(eng) * sign;
RealType y = RealType(table_y[i]) + uniform_01<RealType>()(eng) * RealType(table_y[i + 1] - table_y[i]);
if (y < f(x)) return x * sign;
}
关键是,如果最后一个if
失败,则会再次启动for
循环,并再次触发对generate_int_float_pair
的调用。这意味着基础生成器递增的次数是未知的。
因此,正常序列将具有不同的数字,直到子序列中的拒绝总和相同的点,此时剩余的均匀序列是相同的。这发生在问题中发布的示例中的第三个位置。 (它实际上有点微妙,因为基础生成器可以在Ziggurat算法中调用一次或两次,但实质是相同的 - 一旦序列同步,它们就永远不会产生不同的变量)。