插值(二)——拉格朗日插值(C++)

问题的提出

假设只有两个插值点: x 0 , x 1 ; y 0 , y 1 x_0,x_1;y_0,y_1 x0,x1;y0,y1,求 P 1 ( x ) = a 0 + a 1 x P_1(x)=a_0+a_1x P1(x)=a0+a1x使得 P 1 ( x 0 ) = y 0 , P 1 ( x 1 ) = y 1 P_1(x_0)=y_0,P_1(x_1)=y_1 P1(x0)=y0,P1(x1)=y1,由此可以得到 P 1 ( x ) = y 0 + y 1 − y 0 x 1 − x 0 ( x − x 0 ) = x − x 1 x 0 − x 1 y 0 + x − x 0 x 1 − x 0 y 1 = ∑ i = 0 1 l i y i \begin{align} P_1(x)&=y_0+\frac{y_1-y_0}{x_1-x_0}(x-x_0)\\ &=\frac{x-x_1}{x_0-x_1}y_0+\frac{x-x_0}{x_1-x_0}y_1\\ &=\sum_{i=0}^1l_iy_i\\ \end{align} P1(x)=y0+x1x0y1y0(xx0)=x0x1xx1y0+x1x0xx0y1=i=01liyi
这是对于只有两个插值点的情况,现在对其拓展到任意个插值点。

拉格朗日插值法

对于给出的n个插值坐标: x 0 , x 1 ⋯ x n ; y 0 , y 1 ⋯ y n x_0,x_1\cdots x_n;y_0,y_1\cdots y_n x0,x1xn;y0,y1yn,构造插值函数 P n ( x ) = ∑ i = 0 n l i ( x ) y i P_n(x)=\sum_{i=0}^nl_i(x)y_i Pn(x)=i=0nli(x)yi,显然 P n ( x i ) = y i P_n(x_i)=y_i Pn(xi)=yi,所以有 { l i ( x i ) = 1 l i ( x j ) = 0 ( i ≠ j ) \begin{cases} l_i(x_i)=1\\ l_i(x_j)=0(i\neq j) \end{cases} {li(xi)=1li(xj)=0(i=j)
因此构造出 l i ( x ) = ∏ j = 0 , j ≠ i n x − x j x i − x j l_i(x)=\prod_{j=0,j\neq i}^n\frac{x-x_j}{x_i-x_j} li(x)=j=0,j=inxixjxxj
代入原式,得到 L n ( x ) = ∑ i = 0 n l i ( x ) y i L_n(x)=\sum_{i=0}^nl_i(x)y_i Ln(x)=i=0nli(x)yi
对应的 l i ( x ) l_i(x) li(x)被称为拉格朗日插值基函数。

代码实现

//拉格朗日插值法
#include
#include
//拉格朗日插值法,输入插值点坐标、需要求值的x和插值点数n
double Lagrange(double *xi,double *yi,double x,int n)
{
    double Ln = 0;
    for (int i = 0; i < n;i++)
    {
        double li = 1.0;
        for (int j = 0; j < n;j++)
        {
            if(i!=j)
            {
                li*=((x-xi[j])/(xi[i]-xi[j]));    
            }
        }
        Ln+=yi[i]*li;
    }
    return Ln;
}
double fx(double x)
{
    return sqrt(x + 3);
}
int main()
{
    int n;
    std::cout << "输入插值点数:";
    std::cin>>n;
    double *x = new double[n];
    double *y = new double[n];
    double error[3]={0.0f,0.0f,0.0f};//误差评价
    double a = 3, b = 10;//插值区间
    for(int i=0;ierror[0])
        {
            error[0] = fabs(y_temp-y_temp2);
            //std::cout<

结果分析

插值(二)——拉格朗日插值(C++)_第1张图片

插值(二)——拉格朗日插值(C++)_第2张图片

插值(二)——拉格朗日插值(C++)_第3张图片

插值(二)——拉格朗日插值(C++)_第4张图片

与直接的多项式插值法结果比较,其速度更快,在阶数相同时保持一致的误差,并且随着插值点数的增加,误差还可以进一步减小,对于测试函数 x + 3 \sqrt{x+3} x+3 其龙格现象没那么明显。

误差分析

为了评估插值函数在计算某个x时的误差,计 R n ( x ) = f ( x ) − L n ( x ) R_n(x)=f(x)-L_n(x) Rn(x)=f(x)Ln(x),可以得到 R n ( x ) = f ( n + 1 ) ( ξ x ) ( n + 1 ) ! ∏ i = 0 n ( x − x i ) ( min ⁡ ( x 0 , x 1 , ⋯ x n ) ≤ ξ ≤ max ⁡ ( x 0 , x 1 , ⋯ x n ) ) R_n(x)=\frac{f^{(n+1)(\xi _x)}}{(n+1)!}\prod_{i=0}^n{(x-x_i)}(\min{(x_0,x_1,\cdots x_n)\le \xi \le \max{(x_0,x_1,\cdots x_n)}}) Rn(x)=(n+1)!f(n+1)(ξx)i=0n(xxi)(min(x0,x1,xn)ξmax(x0,x1,xn)),但是用插值函数的时候,意味着原函数在特定的平台较难计算,因此为了方便估计这个误差,使用事后估计法:设 L n ( x ) L_n(x) Ln(x) f ( x ) f(x) f(x) x 0 , x 1 , ⋯ x n x_0,x_1,\cdots x_n x0,x1,xn为节点的插值多项式,另外取一个节点 x n + 1 x_{n+1} xn+1,记 L n ( 1 ) ( x ) L_n^{(1)}(x) Ln(1)(x) f ( x ) f(x) f(x) x 1 , x 2 ⋯ x n , x n + 1 x_1,x_2\cdots x_n,x_{n+1} x1,x2xn,xn+1为节点的插值多项式,则有
f ( x ) − L n ( x ) = f ( n + 1 ) ( ξ 1 ) ( n + 1 ) ! ∏ i = 0 n ( x − x i ) f(x)-L_n(x)=\frac{f^{(n+1)(\xi _1)}}{(n+1)!}\prod_{i=0}^n{(x-x_i)} f(x)Ln(x)=(n+1)!f(n+1)(ξ1)i=0n(xxi)
f ( x ) − L n ( 1 ) ( x ) = f ( n + 1 ) ( ξ 2 ) ( n + 1 ) ! ∏ i = 1 n + 1 ( x − x i ) f(x)-L^{(1)}_n(x)=\frac{f^{(n+1)(\xi _2)}}{(n+1)!}\prod_{i=1}^{n+1}{(x-x_i)} f(x)Ln(1)(x)=(n+1)!f(n+1)(ξ2)i=1n+1(xxi)
由于 f ( n + 1 ) ( x ) f^{(n+1)}(x) f(n+1)(x)在插值区间上变化不大,因此可以认为 f ( x ) − L n ( x ) f ( x ) − L n ( 1 ) ( x ) ≈ x − x 0 x − x n + 1 \frac{f(x)-L_n(x)}{f(x)-L^{(1)}_n(x)}\approx \frac{x-x_0}{x-x_{n+1}} f(x)Ln(1)(x)f(x)Ln(x)xxn+1xx0
转化一下可以得到
f ( x ) − L n ( x ) ≈ x − x 0 x n + 1 − x 0 ( L n ( x ) − L n ( 1 ) ( x ) ) f(x)-L_n(x)\approx \frac{x-x_0}{x_{n+1}-x_0}(L_n(x)-L^{(1)}_n(x)) f(x)Ln(x)xn+1x0xx0(Ln(x)Ln(1)(x))

这个估计误差的方法就被称为时候估计法,当需要计算某次插值的计算误差时,就可以直接使用该方法进行误差估计。

你可能感兴趣的:(计算方法,c++,开发语言,线性代数,矩阵)