本帖最后由 winner245 于 2014-7-14 22:36 编辑 前言 本帖将通过一个典型的数值积分案例来介绍如何在 MATLAB 中有效地计算特殊函数(special functions)的数值积分。特殊函数的数值积分(尤其是无穷积分)比较常见的一个问题是:积分在满足收敛条件下积分结果为 NaN。如果我们使用的是 integral 或 quadgk,这一问题通常会以警告的形式给出: “Warning:Infinite or Not-a-Number value encountered,” 伴随该警告的恶果是,数值积分的结果为 NaN。 为了直观地说明问题,本帖将针对一个在数学、物理学、以及工程应用里较为常见的特殊函数 —— Modified Bessel Function of the First Kind(数学符号记作: 最后,在分享本案例的过程中,我还会顺便指出 MATLAB R2014a 符号积分系统的一个计算错误,应该算是一个 bug。 先给出我要计算的积分: 首先,我们有必要先对 可以看出:
从 (2) 式可以得到, 下面,我们来看看直接用 MATLAB integral 函数计算的结果(MATLAB 版本低于2012a的话可以用 quadgk 代替)。
Warning:Infinite or Not-a-Number value encountered. > Infunfun\private\integralCalc>iterateScalarValued at 349 In funfun\private\integralCalc>vadapt at132 In funfun\private\integralCalc at 83 In integral at 88 ans = NaN可以看到,直接计算的结果为NaN。 产生NaN的原因 产生NaN的原因其实很简单,因为按MATLAB的运算规则,一旦计算过程中出现了NaN,最终结果就为NaN。所以,这里一定是因为计算被积函数的函数值时得到了NaN。简单的说,当 x 很大时(如1000),快速递增单独计算的结果为Inf,而快速衰减项的结果为0,二者相乘,MATLAB 里会得到 Inf*0 = NaN。为了详细说明问题,我们先来计算一下被积函数当 x = 1000 的取值。直接用MATLAB数值计算
ans = NaN 数值计算得到结果为NaN。这正是因为单独的两项 besseli(0,x) 和 exp(-x^2) 分别计算时得到了 Inf 和 0,如下所示:
0
Inf 二者再相乘得到 NaN。既然在计算函数值的过程中就出现了NaN,难怪积分结果也是NaN。 我们再看看符号计算的结果:
8.195e-433857 按照双精度数值计算的精度,这个结果在MATLAB数值计算里可以认为是0。所以,数值计算中如果能将这个结果作为0处理,也就能避免积分结果为NaN了。 解决NaN的方法 这里介绍的方法适用于一类具备共同特征的特殊函数积分:被积函数里有一个无上界的快速递增项和一个快速衰减项,二者共同作用的结果使得被积函数在积分区间上或者积分变量 x 取值较大时快速衰减,从而积分收敛。 最简单的解决办法:既然问题出现在大 x 下,可以适当减小积分上限,将上限 Inf 换成一个有限的数,该数必须满足两个条件:1)必须足够大使得被积函数整体的取值很小(近似为0),2)又不能太大而导致数值计算里出现 Inf*0 = NaN。我们可以尝试将积分上限设置为100:
ans = 0.637956643215776 比较通用的解决办法: 有时候直接将被积函数里快速衰减因子和快速增长因子的乘积重新定义一下可能会更方便。针对之前的案例,我们可以考虑重新定exp(-x.^2).*besseli(0,x),使得新的定义里 x 很大时直接返回结果0。为此,我们可以先大致估算一下besseli(0,x)在 x >700 时得到Inf,故我们可以定义: 然后,重新使用下列命令计算积分
ans = 0.637956643215780 注意,上述代码里,积分上限依然是Inf,因为我们已经重定义了expBesseli0。 MATLAB/MuPad符号积分的错误 为了验证上述解决办法的合法性,我特意使用了 MuPad 符号积分来验证。出乎意料的是,MuPad 符号积分的结果完全错误!
ans = -(3*exp(1/4))/8 符号积分的结果为负数,这显然是不可能的。被积函数在积分区间上是非负的,积分结果不可能是负的,这足以说明 MuPad 符号积分计算错误。但为了进一步验证,我们可以看看 Maple 的计算结果:![]() 显然,Maple的结果是一个正数,与上述解决办法的结果相同。 最后,让我们来看看MuPad符号积分的更一般的 bug。积分式如下: MATLAB/MuPad
ans = -(exp(b^2/(4*a))*(b^2+ 2*a))/(8*a^(5/2)) MATLAB/MuPad结果里依然包含了负号,这显然是一个错误。 Maple ![]() Mathematica ![]() Maple 和 Mathematica 的结果相同,而且不含负号。 这应该算是 MATLAB 2014a 的一个 bug 了把。 |
10 条回复