我最近偶然发现了一个C ++错误/功能,我无法完全理解,希望有更好的C ++知识的人能指出我正确的方向。
下面你会发现我尝试使用蒙特卡罗积分找出高斯曲线下的区域。食谱是:
下面的代码包含两个简单的函数:rand_uni
,它返回一个随机变量,均匀分布在0和1之间,rand_norm
,它是一个(相当差,但是'足够好'政府工作')近似正态分布的随机变量。
main
遍历循环十亿次,每次调用rand_norm
,将其与pow
进行平方并添加到累加变量。在此循环之后,累计结果将除以运行次数并以Result=<SOME NUMBER>
打印到终端。
问题在于下面代码的非常古怪的行为:当每个生成的随机变量打印到cout
(是,十亿次)时,无论使用何种编译器,最终结果都是正确的(1.0015,这是非常接近,我想要的)。如果我不在每次循环迭代中打印随机变量,我会在inf
下获得gcc
,在clang
下获得448314。
坦率地说,这只是令人难以置信,因为这是我第一次遇到C ++,我真的不知道问题可能是什么:它是pow
的东西吗? cout
行为怪异吗?
任何提示都会非常感激!
// Monte Carlo integration of the Gaussian curve
#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;
enum {
no_of_runs = 1000000
};
// uniform random variable
double rand_uni() {
return ((double) rand() / (RAND_MAX));
};
// approximation of a normaly distributed random variable
double rand_norm() {
double result;
for(int i=12; i > 0; --i) {
result += rand_uni();
}
return result - 6;
};
int main(const int argc,
const char** argv) {
double result = 0;
double x;
for (long i=no_of_runs; i > 0; --i) {
x = pow(rand_norm(), 2);
#ifdef DO_THE_WEIRD_THING
cout << x << endl; // MAGIC?!
#endif
result += x;
}
// Prints the end result
cout << "Result="
<< result / no_of_runs
<< endl << endl;
}
CLANG=clang++
GCC=g++
OUT=normal_mc
default: *.cpp
$(CLANG) -o $(OUT).clang.a *.cpp
$(CLANG) -o $(OUT).clang.b -DDO_THE_WEIRD_THING *.cpp
$(GCC) -o $(OUT).gcc.a *.cpp
$(GCC) -o $(OUT).gcc.b -DDO_THE_WEIRD_THING *.cpp
答案 0 :(得分:3)
result
中的double rand_norm()
在启动+ =随机值之前未初始化为0.
所有cout的使用和重置堆栈内存的某些部分,后来被rand_norm调用使用,当你执行cout时“修复”你的问题。
将第一行更改为double result = 0.0;
以修复它。
double rand_norm() {
double result = 0.0;
for(int i=12; i > 0; --i) {
result += rand_uni();
}
return result - 6;
};
另外,你应该为随机数生成器播种,你将总是得到完全相同的伪随机数序列,不加它,添加一个
srand(time(NULL));
在您第一次调用rand()之前,或者查看一些更好的随机数生成器,例如Boost.Random
答案 1 :(得分:3)
在你的rand_norm()函数中,结果未初始化,那就是你的错误。为了防止在打印值时它为什么会起作用,因为单位化变量将具有它所具有的内存区域的值,在代码中它是堆栈,因此调用cout&lt;&lt;改变了你的堆栈值。以下是您的功能的固定版本:
double rand_norm() {
double result = 0.0;
for(int i=12; i > 0; --i) {
result += rand_uni();
}
return result - 6;
};