我写了一个短程序来近似高斯函数f(x)= exp(-x ^ 2/2)的定积分,我的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
double gaussian(double x) {
return exp((-pow(x,2))/2);
}
int main(void) {
srand(0);
double valIntegral, yReal = 0, xRand, yRand, yBound;
int xMin, xMax, numTrials, countY = 0;
do {
printf("Please enter the number of trials (n): ");
scanf("%d", &numTrials);
if (numTrials < 1) {
printf("Exiting.\n");
return 0;
}
printf("Enter the interval of integration (a b): ");
scanf("%d %d", &xMin, &xMax);
while (xMin > xMax) { //keeps looping until a valid interval is entered
printf("Invalid interval!\n");
printf("Enter the interval of integration (a b): ");
scanf("%d %d", &xMin, &xMax);
}
//check real y upper bound
if (gaussian((double)xMax) > gaussian((double)xMin))
yBound = gaussian((double)xMax);
else
yBound = gaussian((double)xMin);
for (int i = 0; i < numTrials; i++) {
xRand = (rand()% ((xMax-xMin)*1000 + 1))/1000.00 + xMin; //generate random x value between xMin and xMax to 3 decimal places
yRand = (rand()% (int)(yBound*1000 + 1))/1000.00; //generate random y value between 0 and yBound to 3 decimal places
yReal = gaussian(xRand);
if (yRand < yReal)
countY++;
}
valIntegral = (xMax-xMin)*((double)countY/numTrials);
printf("Integral of exp(-x^2/2) on [%.3lf, %.3lf] with n = %d trials is: %.3lf\n\n", (double)xMin, (double)xMax, numTrials, valIntegral);
countY = 0; //reset countY to 0 for the next run
} while (numTrials >= 1);
return 0;
}
但是,我的代码输出与解决方案不匹配。我尝试调试并打印100次试验的所有xRand,yRand和yReal值(并使用Matlab检查yReal值和特定xRand值,以防我有任何拼写错误),这些值似乎没有无论如何......我都不知道我的错误在哪里。
[0,1]的试验数= 100的正确输出是0.810,而我的是0.880;试验#的正确输出= [-1,0]上的50是0.900,而我的是0.940。谁能找到我做错的地方?非常感谢。
另一个问题是,我找不到使用以下代码的参考:
double randomNumber = rand() / (double) RAND MAX;
但它是由教师提供的,他说它会生成一个从0到1的随机数。为什么他在'/'
之后使用'%'
而不是"rand()"
?
答案 0 :(得分:9)
您的代码中存在一些逻辑错误/讨论点,包括数学和编程方面。
首先,为了解决这个问题,我们在这里谈论标准高斯,即
除了line 6
上的高斯定义,省略了
规范化术语。鉴于您似乎期望的产出,这似乎是故意的。很公平。但是如果你想计算实际的积分,那么几乎无限的范围(例如[-1000,1000])总和为1,那么你需要那个术语
<强> 没有 即可。您的代码有两个逻辑错误:一个在line 29
上(即您的if
语句),一个在line 40
上(即valIntegral
的计算),这是直接后果第一个逻辑错误。
对于第一个错误,请考虑以下情节以了解原因:
你的蒙特卡罗过程有效地考虑了某个范围内的有界框,然后说&#34;我会随机将点放在这个框内,然后计算随机掉落的总点数的比例在曲线下;然后,积分估计就是有界框本身的面积,乘以这个比例&#34;。
现在,如果两者都有
<子>
子>
和
<子>
子>
是对于平均值的左(即0),那么你的if
语句正确设置了方框的上限(即yBound
)到
<子>
子>
这样盒子的最上面的边界包含该曲线的最高部分。因此,例如,要估计范围[-2,-1]的积分,可以将上限设置为
<子>
子>
。
同样,如果两者兼而有之
<子>
子>
和
<子>
子>
对于平均值的正确,然后您正确设置yBound
<子>
子>
然而 ,如果
<子>
子>
,您应该将yBound
设置为
<子>
子>
也不
<子>
子>
,因为0点高于!所以在这种情况下,你的yBound
应该只是高斯的峰值,即
<子>
子>
(在 unnormalised 高斯的情况下,这取值为&#39; 1&#39;)。
因此,正确的if
语句如下:
if (xMax < 0.0)
{ yBound = gaussian((double)xMax); }
else if (xMin > 0.0)
{ yBound = gaussian((double)xMin); }
else
{ yBound = gaussian(0.0); }
至于第二个逻辑错误,我们已经提到积分的值是边界框的&#34;区域&#34;乘以成功比例&#34;。但是,您似乎忽略了计算中框的高度。确实,在特殊情况下
<子>
子>
,非标准化高斯函数的高度默认为&#39; 1&#39;因此可以省略该项。我怀疑这就是它可能被遗漏的原因。但是,在另外两种情况下,边界框的高度必须 小于1,因此需要包含在计算中。因此line 40
的正确代码应为:
valIntegral = yBound * (xMax-xMin) * (((double)countY)/numTrials);
即使出现上述逻辑错误,正如我们上面所讨论的那样,特定的间隔[0,1]和[> -1,0](因为它们包括均值,因此包含正确的yBound
1)。那你为什么仍然得到一个错误的&#39;输出
答案是,你不是。你的输出是&#34;正确&#34;。除此之外,蒙特卡罗过程涉及随机性,100次试验的数量不足以导致一致的结果。如果您反复运行100次试验的相同范围,您将会看到每次都会得到非常不同的结果(但总的来说,它们会分布在正确的值周围)。运行1000000次试验,您将看到结果变得更加精确。
randomNumber
代码是什么? rand()
函数返回[0,RAND_MAX
]范围内的整数,其中RAND_MAX
是系统特定的(请查看{{ 1}})。
模数方法(即man 3 rand
)的工作原理如下:考虑范围[-0.1,0.3]。这个范围跨越0.4个单位。 0.4 * 1000 + 1 = 401.对于从0到%
的随机数,执行RAND_MAX
modulo rand()
将始终产生范围内的随机数[0400]。如果再将其除以1000,则得到[0,0.4]范围内的随机数。将其添加到xmin偏移量(此处为:-0.1),您将获得[-0.1,0.3]范围内的随机数。
从理论上讲,这是有道理的。然而,不幸的是,正如在此处的另一个答案中已经指出的那样,作为一种方法,它易受模偏差的影响,因为401
不一定完全可被401整除,因此该范围的顶部会导致到RAND_MAX
比其他人更多地代表了一些数字。
相比之下,老师给你的方法只是说:将RAND_MAX
函数的结果除以rand()
。 这有效地将返回的随机数标准化为[0,1] 范围。这是一个更直截了当的事情,它避免了模数偏差。
因此,我实现这个的方法是将它变成一个函数:
RAND_MAX
然后也简化了你的计算:
double randomNumber(void) {
return rand() / (double) RAND_MAX;
}
如果你使用规范化高斯,你可以看到这是一个更准确的事情,即
xRand = randomNumber() * (xMax-xMin) + xMin;
yRand = randomNumber() * yBound;
然后比较两种方法。你会看到{&#34;有效无限&#34;的double gaussian(double x) {
return exp((-pow(x,2.0))/2.0) / sqrt(2.0 * M_PI);
}
方法。范围(例如[-1000,1000])给出正确的结果1,而模数方法倾向于给出大于1的数字。
答案 1 :(得分:3)
你的代码没有明显的错误(尽管 是上限计算中的一个错误,正如@TasosPapastylianou指出的那样,尽管它不是你的测试用例中的问题)。在100次试验中,您对0.880的回答更接近于积分的实际值(0.855624 ...)而不是0.810,并且这些数字都不是真正的值,因此在代码中提出了一个彻头彻尾的错误。似乎在抽样误差范围内(尽管见下文)。以下是e^(-x^2/2)
上[0,1]
进行100次试验的1000次蒙特卡洛积分(在R中完成,但使用相同算法)的直方图:
除非您的教师详细指定算法和种子,否则您不应期望完全相同的答案。
至于你关于rand() / (double) RAND MAX
的第二个问题:它试图避免modulo bias。这样的偏差可能会影响你的代码(特别是考虑到你的方法可以舍入到3个小数位),因为它似乎高估了积分(基于运行它十几次左右)。也许您可以在代码中使用它,看看您是否获得了更好的结果。