插值(四)——Hermite插值(C++)

hermite插值

实际问题中,对所构造的插值多项式,不仅要求函数值重合,而且若干阶导数也重合,即: p ( x i ) = f ( x i ) , p ′ ( x i ) = f ′ ( x i ) , ⋯   , p ( n ) ( x i ) = f ( n ) ( x i ) p(x_i)=f(x_i),p'(x_i)=f'(x_i),\cdots,p^{(n)}(x_i)=f^{(n)}(x_i) p(xi)=f(xi),p(xi)=f(xi),,p(n)(xi)=f(n)(xi)
一般情况都是使用二阶hermite插值,即要求: H ( x i ) = f ( x i ) , H ′ ( x i ) = f ′ ( x i ) H(x_i)=f(x_i),H'(x_i)=f'(x_i) H(xi)=f(xi),H(xi)=f(xi),构造其多项式为:
H ( x ) = ∑ i = 0 n g i ( x ) f ( x i ) + ∑ i = 0 n h i ( x ) f ′ ( x i ) H(x)=\sum_{i=0}^n g_i(x)f(x_i)+\sum_{i=0}^n h_i(x)f'(x_i) H(x)=i=0ngi(x)f(xi)+i=0nhi(x)f(xi)
其中
g i ( x ) = [ 1 − 2 ( x − x i ) ∑ i ≠ j 1 x i − x j ] l i 2 ( x ) h i ( x ) = ( x − x i ) l i 2 ( x ) g_i(x)=[1-2(x-x_i)\sum_{i \neq j}\frac{1}{x_i-x_j}]l_i^2(x) h_i(x)=(x-x_i)l^2_i(x) gi(x)=[12(xxi)i=jxixj1]li2(x)hi(x)=(xxi)li2(x)
这里的 l i ( x ) l_i(x) li(x)是Lagrange插值的基函数。

代码

这里以二阶hermite插值为例,给出代码。

//二阶hermite插值
#include <iostream>
#include <cmath>
#define DerivativeIntervalAccuracy 0.0001//求导区间精度

//求拉格朗日插值函数的基函数li(x)
double Lagrange_li(double *xi,double *yi,double x,int i,int n)
{

    double li = 1.0;
    for (int j = 0; j < n;j++)
    {
        if(i!=j)
        {
            li*=((x-xi[j])/(xi[i]-xi[j]));    
        }
    }
    return li;
}
double hermite(double *xi,double *yi,double *f_dx,double x,int n)
{
    double li;
    double hermite = 0.0f;
    for (int i = 0; i < n;i++)
    {
        li=Lagrange_li(xi, yi, x, i, n);
        double gi = 0.0f;
        for (int j = 0; j < n;j++)
        {
            if(i!=j)
            {
                gi += 1 / (xi[i] - xi[j]);
            }            
        }
        gi = (1 - 2 * (x - xi[i]) * gi) * li * li;
        double hi = 2 * (x - xi[i]) * li * li;
        hermite += gi * yi[i] + hi * f_dx[i];
    }
    return hermite;
}
//近似求导,传递函数进来
double f_derivative(double (*fx)(double),double x)
{
    return (fx(x + DerivativeIntervalAccuracy) - fx(x))/DerivativeIntervalAccuracy;
}

double fx(double x)
{
    //这里填入被插值函数
    //如果插值区间包括分母为0的情况需要手动调整
    return sqrt(x + 3);
}

int main()
{
    int n;//插值点数
    std::cout<<"请输入插值点数:";
    std::cin>>n;
    double error[3]={0.0f,0.0f,0.0f};//误差评价
    double *x = new double [n];
    double *y = new double [n];
    double *d_fx = new double[n];//导数
    double a=3, b=10;//定义插值区间
    //生成插值数据
    for (int i = 0;i<n;i++)
    {
        x[i] = a + (b-a)*i/(n-1);
        d_fx[i]=f_derivative(fx,x[i]);
        y[i] = fx(x[i]);
        //std::cout<<"["<<x[i]<<","<<y[i]<<"]:"<<d_fx[i]<<std::endl;
    }

    //std::cout << std::endl;
    // 验证误差,抽取区间内的100个点进行误差检测
    for (int i = 0; i < 100; i++)
    {
        double x_temp = a + (b - a) * (i+1) / 100;
        double y_temp = fx(x_temp);
        double y_temp2 =hermite(x,y,d_fx,x_temp,n);
        if (fabs(y_temp - y_temp2) > error[0])
        {
            error[0] = fabs(y_temp - y_temp2);
            // std::cout << y_temp2 << std::endl;
        }
        error[1] += fabs((y_temp - y_temp2) / y_temp);
        error[2] += ((y_temp - y_temp2)) * ((y_temp - y_temp2));
    }
    //err[0]得到的是在区间内最大的误差
    //err[1]得到的是平均最大相对误差
    //err[2]是均方根误差
    error[1] = error[1]/100;
    error[2] = sqrt(error[2]/100);
    for(int i = 0;i < 3;i++)
    {
        std::cout<<"误差"<<i+1<<":"<<error[i]<<std::endl;
    }
    delete[] x;
    delete[] y;
    delete[] d_fx;
    return 0;
}

结果分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其精度比牛顿插值法略差,但是hermite最重要的性质在于它的n阶导数也可以重合,此处是二阶Hermite插值,所以它的导数也会重合,所以这里插入一段代码查看导数的重合情况:

for (int j = 0; j < n;j++)
{
    if(x[j]==x_temp)
    {
        std::cout<<"x="<<x[j]<<":"<<fabs(y_temp - y_temp2)<<" d_f(x)_error:"<<
        fabs((hermite(x,y,d_fx,x_temp+DerivativeIntervalAccuracy,n)-hermite(x,y,d_fx,x_temp,n))/DerivativeIntervalAccuracy)-d_fx[j]
        <<std::endl;
        break;
    }
}

在这里插入图片描述
可以看到,这里的数据表示为验证过程中与插值点相等的x坐标,其y坐标也相等,而对应的导数存在些许误差,在区间内,导数的重合情况非常良好,误差非常小,这是因为本身也是近似求导,所以不为0。

要用牛顿插值计算Hermite插值C,我们需要先了解Hermite插值和牛顿插值的基本概念。 Hermite插值是一种基于已知函数值和导数值来构造插值函数的方法。它的基本思想是,给定一些点的函数值和导数值,构造一个多项式函数,使得这个函数在这些点处的函数值和导数值都与给定的函数值和导数值相等。 牛顿插值是一种多项式插值方法,它的基本思想是构造一个针对给定数据点的多项式函数,通过这个函数来近似原函数。具体来说,牛顿插值使用一个递推公式来计算多项式的系数,可以有效地避免了高次插值多项式计算的数值不稳定性。 现在我们来看如何用牛顿插值计算Hermite插值C。假设我们有n个数据点,它们的函数值和导数值分别为(x_i,y_i,f_i),其中f_i是y_i的导数。我们要构造一个Hermite插值函数C(x),使得C(x_i)=y_i,C'(x_i)=f_i。 首先,我们需要将数据点按照x_i的大小进行排序,从小到大排列。然后,我们可以使用牛顿插值的递推公式来计算Hermite插值的系数a_i。具体来说,我们可以定义一个差商表,其中每一行的第一个元素是函数值,第二个元素是导数值,然后按照牛顿插值的递推公式计算差商表中的每一个元素,直到得到最终的多项式系数a_i。 最后,我们可以使用多项式函数的形式来表示Hermite插值函数C(x),即: C(x) = a_0 + a_1(x-x_0) + a_2(x-x_0)^2 + ... + a_n(x-x_0)(x-x_1)...(x-x_{n-1}) 其中,a_0,a_1,...,a_n是我们通过牛顿插值计算得到的多项式系数,x_0,x_1,...,x_n是我们给定的数据点的x坐标。这个多项式函数就是我们所要求的Hermite插值函数C(x)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值