我尝试使用该公式实现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,一个完全不同的结果。
答案 0 :(得分:3)
这种递归关系在数学上是很好的,但在使用浮点数的有限精度表示来实现算法时数值不稳定。
考虑以下比较:
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')
因此,您可以看到计算值在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;
}
}