使用Jm + 1 = 2mj(m)-j(m-1)公式在MATLAB中计算bessel函数

时间:2011-10-18 15:51:31

标签: math matlab recurrence bessel-functions

我尝试使用该公式实现bessel函数,这是代码:

function result=Bessel(num);


if num==0
    result=bessel(0,1);
elseif num==1
    result=bessel(1,1);
else
    result=2*(num-1)*Bessel(num-1)-Bessel(num-2);
end;

但是如果我使用MATLAB的bessel函数将它与这个函数进行比较,我会得到太高的不同值。 例如,如果我键入贝塞尔(20),它给我3.1689e + 005作为结果,如果我输入bessel(20,1)它给我3.8735e-025,一个完全不同的结果。

3 个答案:

答案 0 :(得分:3)

recurrence_bessel

这种递归关系在数学上是很好的,但在使用浮点数的有限精度表示来实现算法时数值不稳定。

考虑以下比较:

x = 0:20;
y1 = arrayfun(@(n)besselj(n,1), x);   %# builtin function
y2 = arrayfun(@Bessel, x);            %# your function
semilogy(x,y1, x,y2), grid on
legend('besselj','Bessel')
title('J_\nu(z)'), xlabel('\nu'), ylabel('log scale')

comparison_plot

因此,您可以看到计算值在9之后开始显着不同。

根据MATLAB:

  

BESSELJ使用D. E. Amos的Fortran库的MEX接口。

并提供以下作为其实施的参考:

  

d。 E. Amos,“复杂贝塞尔函数的子程序包   论证和非负序“,桑迪亚国家实验室报告,   SAND85-1018,1985年5月。

     

d。 E. Amos,“用于贝塞尔函数的便携式软件包   论证和非负序“,Trans.Math.Software,1986。

答案 1 :(得分:2)

您使用的前向递归关系不稳定。要了解原因,请考虑BesselJ(n,x)的值变得越来越小,大约为1/2n因子。你可以通过查看J的泰勒级数的第一项来看到这一点。

所以,你所做的是从较小数字的倍数中减去一个较大的数字,以得到更小的数字。在数字上,这不会很好。

以这种方式看待它。我们知道结果是10^-25的顺序。你可以从大约1的数字开始。因此,为了得到一个准确的数字,我们必须知道前两个数字,精度至少为25位。我们显然没有,而且复发实际上有所不同。

使用相同的递归关系向后,从高订单到低订单, 稳定。当您从J(20,1)和J(19,1)的正确值开始时,您也可以完全准确地计算低至0的所有订单。为什么这样做?因为现在每个步骤的数字都在变大。你从一个较大数字的精确倍数中减去一个非常小的数字,以获得更大的数字。

答案 2 :(得分:0)

您可以修改下面的代码,用于Spherical bessel功能。它经过了充分测试,适用于所有参数和订单范围。我很抱歉这是在C#

     public static Complex bessel(int n, Complex z)
    {
        if (n == 0) return sin(z) / z;
        if (n == 1) return sin(z) / (z * z) - cos(z) / z;

        if (n <= System.Math.Abs(z.real))
        {
            Complex h0 = bessel(0, z);
            Complex h1 = bessel(1, z);
            Complex ret = 0;
            for (int i = 2; i <= n; i++)
            {
                ret = (2 * i - 1) / z * h1 - h0;
                h0 = h1;
                h1 = ret;
                if (double.IsInfinity(ret.real) || double.IsInfinity(ret.imag)) return double.PositiveInfinity;
            }
            return ret;
        }
        else
        {
            double u = 2.0 * abs(z.real) / (2 * n + 1);

            double a = 0.1;
            double b = 0.175;
            int v = n - (int)System.Math.Ceiling((System.Math.Log(0.5e-16 * (a + b * u * (2 - System.Math.Pow(u, 2)) / (1 - System.Math.Pow(u, 2))), 2)));

            Complex ret = 0;
            while (v > n - 1)
            {
                ret = z / (2 * v + 1.0 - z * ret);
                v = v - 1;
            }


            Complex jnM1 = ret;
            while (v > 0)
            {
                ret = z / (2 * v + 1.0 - z * ret);
                jnM1 = jnM1 * ret;
                v = v - 1;
            }
            return jnM1 * sin(z) / z;
        }
    }