4

有谁知道在处理大实数和负实数时如何使以下 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

4

2 回答 2

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

请注意,当您使用此类方法进行范围缩减时,虽然您可能会由于必要的额外计算而遇到数值问题,但由于您的系列的收敛性大大增强,您通常会领先一步。

于 2012-04-23T13:33:41.283 回答
5

为什么你认为这是错误的答案?查看该序列的最后一项,它的大小,并告诉我为什么您期望您的答案应该接近 0。

我最初的回答说舍入错误是问题所在。这是这种基本方法的一个问题,但是为什么您认为 40 项对于适当的数学(而不是计算机浮点算术)答案来说已经足够了。

100^40 / 40!〜= 10 ^ 31。

Woodchip 有减少射程的正确想法。这是人们用来快速实现这些功能的典型方法。一旦你弄清楚了这一切,你就可以处理交替序列的舍入误差,方法是对循环中的相邻项求和,并以 k = 1 : 2 : 40 步进(例如)。在您使用woodchips 的想法之前,这在这里不起作用,因为对于x = -100,和数会增长长时间。你需要 |x| < 1 以保证中间项正在缩小,因此重写将起作用。

于 2012-04-23T13:18:51.127 回答