通过有理切比雪夫近似对指数积分 Ei 的数值评估失败

计算科学 C 近似 特殊功能 数值限制
2021-11-29 09:52:40

我正在尝试评估指数积分Ei(x)=xettdt为了x>0(解释为柯西主值)通过使用有理切比雪夫近似,可以在 Cody & Thacher 的论文“Chebyshev Approximations for the Exponential IntegralEi(x)”。该论文可在ams.org在线获得。我正在尝试使用第 292 页上的等式作为间隔0<x6

Ei(x)log(x/x0)+(xx0)j=0npjTj(x/6)j=0nqjTj(x/6)

在哪里x00.37是零Ei(x),pjqj是论文表 II 中的系数(我正在使用n=9) 和Tj(x)=Tj(2x1)是移位的切比雪夫多项式。所以我在一个简单的 C 程序中实现了这个方程

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#define N_1 10

double chebyshev(int n, double x){
    switch(n){
        case 0:
            return 1.0;
            break;
        case 1:
            return x;
            break;
        default:
            return 2.0*x*chebyshev(n-1, x)-chebyshev(n-2, x);
            break;
    }
}

double shifted_chebyshev(int n, double x){
    return chebyshev(n, 2.0*x-1.0);
}

double interval1(double x){
    static const double pj_1[N_1] = {
                            -4.1658081333604994241879E11,
                            +1.2177698136199594677580E10,
                            -2.5301823984599019348858E10,
                            +3.1984354235237738511048E8,
                            -3.5377809694431133484800E8,
                            -3.1398660864247265862050E5,
                            -1.4299841572091610380064E6,
                            -1.4287072500197005777376E4,
                            -1.2831220659262000678155E3,
                            -1.2963702602474830028590E1
    };
    static const double qj_1[N_1] = {
                            -1.7934749837151009723371E11,
                            +9.8900934262481749439886E10,
                            -2.8986272696554495342658E10,
                            +5.4229617984472955011862E9,
                            -7.0108568774215954065376E8,
                            +6.4698830956576428587653E7,
                            -4.2648434812177161405483E6,
                            +1.9418469440759880361415E5,
                            -5.5648470543369082846819E3,
                            +7.6886718750000000000000E1
    };
    static const double x0 = 0.372507410781366634461991866580;

    int j;
    double result, numerator, denominator, sum;

    result = log(x/x0);
    numerator = 0.0;
    denominator = 0.0;
    sum = 0.0;

    for(j=0; j<N_1; j++){
        numerator += pj_1[j] * shifted_chebyshev(j, x/6.0);
        denominator += qj_1[j] * shifted_chebyshev(j, x/6.0);
    }
    sum = numerator / denominator;
    sum *= (x-x0);
    result += sum;

    return result;
}

double ei(double x){
    if( x < 0.0 ){
        printf("Argument of ei(x) must be positive\n");
        exit(1);
    }

    if( x <= 6.0 ){
        return interval1(x);
    }

    printf("Argument range not yet implemented for ei(x).\n");
    exit(1);
}

int main(void){
    double x = 5.0;
    printf("ei(%g)=%g\n", x, ei(x));

    return 0;
}

但是对于有问题的间隔,结果完全不正确。例如我想评估Ei(5)40.2,但我得到19.0654. 该论文声称最大相对误差为81019,所以我想我一定做错了什么。我仔细检查了系数和方程式的实现,但我没有看到任何错误。

当我绘制 C 计算值与实际/预期值(由 Mathematica 的 ExpIntegralEi 函数返回)相比的相对误差时,我可以看到只有第一个间隔(0<x6) 错误高得不合理:

相对误差,与 Mathematica 相比

绘制的是y(x)=|C(x)M(x)M(x)|, 在哪里C(x)是我的 C 程序返回的值,而 M(x) 与 Mathematica 返回的值相同。

任何帮助将非常感激。

参考

  1. Cody、WJ 和 Henry C. Thacher。“指数积分 𝐸𝑖 (𝑥) 的切比雪夫近似。” 计算数学 23.106(1969):289-303。
1个回答

您的代码和问题的表达式中都缺少一个素数。在原始论文中,表达式为:

log(x/x0)+(xx0)0jnpjTj(x/6)0jnqjTj(x/6).
在处理切比雪夫级数时,这个引数和是非常常见和标准的:这意味着和的第一项,对应于第零个切比雪夫多项式(常数项),必须除以 2

这种修改的起源是恒等式(mathworld 上的 (25)

1kmTi(xk)Tj(xk)=[i=j]m1+[i=j0]
在哪里xk是的根Tm.

分开后p0q0经过2,我认为它似乎工作正常。

PS我想指出,评估切比雪夫级数的标准且非常简单的方法是使用 Clenshaw 递归,这比您自己实现的切比雪夫多项式要快得多(这需要时间指数n评估Tn),更准确。在系数具有非常大的幅度的情况下,它可能非常重要,即使由于某种原因在这里似乎并非如此。