有没有人知道如何在处理大的和负的实数时更准确地使下面的Matlab代码接近指数函数?
例如,当x = 1时,代码效果很好,当x = -100时,它应该更接近3.7201e-44时返回8.7364e + 31的答案。
代码如下:
s=1
a=1;
y=1;
for k=1:40
a=a/k;
y=y*x;
s=s+a*y;
end
s
欢迎任何帮助,欢呼。
编辑:
好的,问题如下:
这段代码接近哪个数学函数? (我说指数函数。) x = 1时是否有效? (是的。)不幸的是,当x = -100时使用它会产生答案s = 8.7364e + 31。您的同事认为该程序中存在一个愚蠢的错误,并请求您的帮助。仔细解释行为并给出一个简单的修复,产生更好的结果。 [您必须建议对上述代码进行修改,或者使用它。您还必须检查简单修复的工作原理。]
所以我有点明白,当术语之间存在16个(或更多)数量级时,问题就围绕着大数字,精度会丢失,但解决方案却让我望而却步。
由于
编辑:
所以最后我接受了这个:
s = 1;
x = -100;
a = 1;
y = 1;
x1 = 1;
for k=1:40
x1 = x/10;
a = a/k;
y = y*x1;
s = s + a*y;
end
s = s^10;
s
不确定它是否完全正确,但它会返回一些好的近似值。
exp(-100)= 3.720075976020836e-044
s = 3.722053303838800e-044
经过进一步分析(不幸的是提交作业),我意识到增加迭代次数,从而增加术语,进一步提高了效率。事实上,以下效率更高:
s = 1;
x = -100;
a = 1;
y = 1;
x1 = 1;
for k=1:200
x1 = x/200;
a = a/k;
y = y*x1;
s = s + a*y;
end
s = s^200;
s
由此给出:
exp(-100)= 3.720075976020836e-044
s = 3.720075976020701e-044
答案 0 :(得分:7)
正如约翰在评论中指出的那样,你在循环中有一个错误。 y = y * k线不能满足你的需要。仔细查看exp(x)系列中的术语。
无论如何,我认为这就是为什么你被赋予这个家庭作业的原因,要知道像这样的系列对于大值不能很好地收敛。相反,你应该考虑如何减少范围。
例如,您可以使用身份
exp(x+y) = exp(x)*exp(y)
对你有利吗?假设您存储exp(1)= 2.7182818284590452353 ... 的值
现在,如果我要求你计算exp(1.3)的值,你会如何使用上述信息?
exp(1.3) = exp(1)*exp(0.3)
但我们已经知道exp(1)的价值了。实际上,只需稍加思考,就可以将指数范围缩小到需要系列仅在abs(x)<= 0.5时快速收敛的范围。
编辑:第二种方法是使用相同身份的变体进行范围缩减。
exp(x) = exp(x/2)*exp(x/2) = exp(x/2)^2
因此,假设您希望计算大数的指数,可能是12.8。让这个快速收敛可以在简单的系列中使用很多术语,并且会发生大量的减法取消,所以无论如何你都不会获得良好的准确性。但是,如果我们认识到
12.8 = 2*6.4 = 2*2*3.2 = ... = 16*0.8
然后,如果您能够有效地计算0.8的指数,那么期望的值很容易恢复,可能是通过重复的平方。
exp(12.8)
ans =
362217.449611248
a = exp(0.8)
a =
2.22554092849247
a = a*a;
a = a*a;
a = a*a;
a = a*a
362217.449611249
exp(0.8)^16
ans =
362217.449611249
请注意,除非您使用此类方法进行范围缩小,但由于需要进行额外的计算,您可能会遇到数值问题,但由于系列的收敛性大大提高,您通常会提前退出。
答案 1 :(得分:5)
为什么你认为这是错误的答案?看看那个序列的最后一个术语,它的大小,告诉我为什么你应该得到一个接近0的答案。
我原来的答案说明了问题就是出现了错误。这个将成为这个基本方法的问题,但为什么你认为40对于适当的数学(与计算机浮点运算相反)答案是足够的。
100 ^ 40/40! 〜= 10 ^ 31。
Woodchip有一个正确的想法,减少范围。这是人们用来快速实现这些功能的典型方法。一旦你弄明白了,就可以处理交替序列的舍入误差,通过对循环内的相邻项求和,并以k = 1:2:40(例如)步进。在你使用木片的想法之前,这不起作用,因为对于x = -100,对于非常长时间的加法增长。你需要| x | &LT; 1保证中间术语缩小,因此重写将起作用。