三次样条插值(附上MATLAB代码)

三次样条插值(附上MATLAB代码)

前言:

工程数值方法课程上,老师讲了拉格朗日插值法、哈密顿插值法以及三次样条插值,但是三次样条插值实在难以理解,故写一篇博客,以后理解也方便。

问题起始:

龙格现象

使用均匀节点构造高次多项式时,由于节点数量不够,导致在边缘会出现误差极大的情况。

在这里插入图片描述

此时,有一种优化思路便为不考虑多个节点,而考虑相邻两个节点的影响,利用相邻两个节点的相关性质来寻找一条多项式进行插值。这里选择使用三次多项式(一次二次不够拟合,三次以上即可,高次多项式可以使用相似的条件。

基本思路:

核心思路:

对于给定点(x0,y0),……,(xn,yn),在每一个子区间[xi,xi+1]中,局部构建出一个三次曲线来进行拟合。此时,三次曲线的形式可以写成如下形式:
S i ( x ) = a i + b i ( x − x i ) + c i ( x − x i ) 2 + d i ( x − x i ) 3 S_i\left( x \right) =a_i+b_i\left( x-x_i \right) +c_i\left( x-x_{\mathrm{i}} \right) ^2+\mathrm{d}_{\mathrm{i}}\left( \mathrm{x}-\mathrm{x}_{\mathrm{i}} \right) ^3 Si(x)=ai+bi(xxi)+ci(xxi)2+di(xxi)3
解这个三次多项式,至少需要四个条件。它满足如下四个条件:
{ S i ( x i ) = y i , S i ( x i + 1 ) = y i + 1 S ′ i − 1 ( x i ) = S ′ i ( x i ) S i − 1 ′ ′ ( x i ) = S i ′ ′ ( x i ) \begin{cases} \mathrm{S}_{\mathrm{i}}\left( \mathrm{x}_{\mathrm{i}} \right) =\mathrm{y}_{\mathrm{i}},\mathrm{S}_{\mathrm{i}}\left( \mathrm{x}_{\mathrm{i}+1} \right) =\mathrm{y}_{\mathrm{i}+1}\\ \mathrm{S}\prime_{\mathrm{i}-1}\left( \mathrm{x}_{\mathrm{i}} \right) =\mathrm{S}\prime_{\mathrm{i}}\left( \mathrm{x}_{\mathrm{i}} \right)\\ \mathrm{S}''_{\mathrm{i}-1}\left( \mathrm{x}_{\mathrm{i}} \right) =\mathrm{S}''_{\mathrm{i}}\left( \mathrm{x}_{\mathrm{i}} \right)\\ \end{cases} Si(xi)=yi,Si(xi+1)=yi+1Si1(xi)=Si(xi)Si1′′(xi)=Si′′(xi)
这四个条件的含义为,对于该曲线,两端由两点确定,对于第i个曲线,满足前后的两条曲线在xi点的变化程度相等。但是注意,这里埋了一个小坑,在端点处,两端的斜率的条件是无法计算的。所以实际求解时,需要对端点处进行条件补充。

求解关系:

此时,根据这三个限制可以进行求解。我们假定在[xi,xi+1]这个区间上进行求解,则有:

(1) 约束一:

ai此时由第一个条件,可以直接用yi来代替,利用第二个条件可以得到:
S i ( x i + 1 ) = y i + 1 \mathrm{S}_{\mathrm{i}}\left( \mathrm{x}_{\mathrm{i}+1} \right) =\mathrm{y}_{\mathrm{i}+1} Si(xi+1)=yi+1

y i + 1 = y i + b i ( x − x i ) + c i ( x − x i ) 2 + d i ( x − x i ) 3 \mathrm{y}_{\mathrm{i}+1}=\mathrm{y}_{\mathrm{i}}+\mathrm{b}_{\mathrm{i}}\left( \mathrm{x}-\mathrm{x}_{\mathrm{i}} \right) +\mathrm{c}_{\mathrm{i}}\left( \mathrm{x}-\mathrm{x}_{\mathrm{i}} \right) ^2+\mathrm{d}_{\mathrm{i}}\left( \mathrm{x}-\mathrm{x}_{\mathrm{i}} \right) ^3 yi+1=yi+bi(xxi)+ci(xxi)2+di(xxi)3

为方便起见,我们令:
{ Δ i = y i + 1 − y i δ i = x i + 1 − x i \begin{cases} \Delta _{\mathrm{i}}=\mathrm{y}_{\mathrm{i}+1}-\mathrm{y}_{\mathrm{i}}\\ \delta _{\mathrm{i}}=\mathrm{x}_{\mathrm{i}+1}-\mathrm{x}_{\mathrm{i}}\\ \end{cases} {Δi=yi+1yiδi=xi+1xi
此时则有式子一:
Δ i δ i = b i + δ i c i + δ i 2 d i \frac{\Delta _{\mathrm{i}}}{\delta _{\mathrm{i}}}=\mathrm{b}_{\mathrm{i}}+\delta _{\mathrm{i}}\mathrm{c}_{\mathrm{i}}+{\delta _{\mathrm{i}}}^2\mathrm{d}_{\mathrm{i}} δiΔi=bi+δici+δi2di
(2)约束二:
S ′ i − 1 ( x i ) = S ′ i ( x i ) \mathrm{S}\prime_{\mathrm{i}-1}\left( \mathrm{x}_{\mathrm{i}} \right) =\mathrm{S}\prime_{\mathrm{i}}\left( \mathrm{x}_{\mathrm{i}} \right) Si1(xi)=Si(xi)
由于式子中具有x-xi项,对于右边求导后代入不难得到:
S ′ i ( x i ) = b i \mathrm{S}\prime_{\mathrm{i}}\left( \mathrm{x}_{\mathrm{i}} \right) =\mathrm{b}_{\mathrm{i}} Si(xi)=bi
此时左边为:
S ′ i − 1 ( x ) = b i − 1 + 2 c i − 1 ( x − x i − 1 ) + 3 d i − 1 ( x − x i − 1 ) 2 S ′ i − 1 ( x i ) = b i − 1 + 2 c i − 1 δ i − 1 + 3 d i − 1 δ i − 1 2 = b i \mathrm{S}\prime_{\mathrm{i}-1}\left( \mathrm{x} \right) =\mathrm{b}_{\mathrm{i}-1}+2\mathrm{c}_{\mathrm{i}-1}\left( \mathrm{x}-\mathrm{x}_{\mathrm{i}-1} \right) +3\mathrm{d}_{\mathrm{i}-1}\left( \mathrm{x}-\mathrm{x}_{\mathrm{i}-1} \right) ^2 \\ \mathrm{S}\prime_{\mathrm{i}-1}\left( \mathrm{x}_{\mathrm{i}} \right) =\mathrm{b}_{\mathrm{i}-1}+2\mathrm{c}_{\mathrm{i}-1}\delta _{\mathrm{i}-1}+3\mathrm{d}_{\mathrm{i}-1}{\delta _{\mathrm{i}-1}}^2=b_i Si1(x)=bi1+2ci1(xxi1)+3di1(xxi1)2Si1(xi)=bi1+2ci1δi1+3di1δi12=bi
(3)约束三思路与约束二相同,直接写出最后结果:
S i − 1 ′ ′ ( x i ) = 2 c i − 1 + 6 δ i − 1 d i − 1 = S i ′ ′ ( x i ) = 2 c i \mathrm{S}''_{\mathrm{i}-1}\left( \mathrm{x}_{\mathrm{i}} \right) =2\mathrm{c}_{\mathrm{i}-1}+6\delta _{\mathrm{i}-1}\mathrm{d}_{\mathrm{i}-1}=\mathrm{S}''_{\mathrm{i}}\left( \mathrm{x}_{\mathrm{i}} \right) =2\mathrm{c}_{\mathrm{i}} Si1′′(xi)=2ci1+6δi1di1=Si′′(xi)=2ci
此时,我们有了三个未知数以及三个约束方程,可以进行求解,求解之前先对方程进行化简,求解出其中一个未知数,另外两个未知数用这个未知数进行比较即可。考虑到将ci进行取代较为复杂,因此选择ci为求解的未知数,用ci表示di和bi,可以在约束二,约束三中进行取代,则有:
{ d i − 1 = 1 3 δ i − 1 ( c i − c i − 1 ) → d i = 1 3 δ i ( c i + 1 − c i ) b i = Δ i δ i − 2 3 δ i c i − δ i 3 c i + 1 \begin{cases} \mathrm{d}_{\mathrm{i}-1}=\frac{1}{3\delta _{\mathrm{i}-1}}\left( \mathrm{c}_{\mathrm{i}}-\mathrm{c}_{\mathrm{i}-1} \right) \rightarrow \mathrm{d}_{\mathrm{i}}=\frac{1}{3\delta _{\mathrm{i}}}\left( \mathrm{c}_{\mathrm{i}+1}-\mathrm{c}_{\mathrm{i}} \right)\\ \mathrm{b}_{\mathrm{i}}=\frac{\Delta _{\mathrm{i}}}{\delta _{\mathrm{i}}}-\frac{2}{3}\delta _{\mathrm{i}}\mathrm{c}_{\mathrm{i}}-\frac{\delta _{\mathrm{i}}}{3}\mathrm{c}_{\mathrm{i}+1}\\ \end{cases} {di1=3δi11(cici1)di=3δi1(ci+1ci)bi=δiΔi32δici3δici+1
代入约束一的方程中,整理后可写成:
δ i − 1 c i − 1 + 2 ( δ i − 1 + δ i ) c i + δ i c i + 1 = 3 ( Δ i δ i − Δ i − 1 δ i − 1 ) \delta _{\mathrm{i}-1}\mathrm{c}_{\mathrm{i}-1}+2\left( \delta _{\mathrm{i}-1}+\delta _{\mathrm{i}} \right) \mathrm{c}_{\mathrm{i}}+\delta _{\mathrm{i}}\mathrm{c}_{\mathrm{i}+1}=3\left( \frac{\Delta _{\mathrm{i}}}{\delta _{\mathrm{i}}}-\frac{\Delta _{\mathrm{i}-1}}{\delta _{\mathrm{i}-1}} \right) δi1ci1+2(δi1+δi)ci+δici+1=3(δiΔiδi1Δi1)
注意,此时ci为未知数,此时为特殊的一种方程形式,最后整理可以化成三对角方程,三对角方程可以利用追赶法进行快速求解。追赶法的函数如下,具体原理不再赘述:

function x=tridecomposition(up,down,angle,b)
    n=length(angle);
    for i = 2:1:n
        down(i-1) = down(i-1)/angle(i-1);
        angle(i) = angle(i) - up(i-1) * down(i-1);
        b(i) = b(i) - b(i-1) * down(i-1);
    end
    x(n)=b(n)/angle(n);
    for i=n-1:-1:1
        x(i)=(b(i)-up(i)*x(i+1))/angle(i);
    end
end

此时的方程形式为:
[ δ 0 2 ( δ 0 + δ 1 ) δ 1 δ 1 2 ( δ 1 + δ 2 ) δ 2 ⋱ ⋱ ⋱ δ n − 2 2 ( δ n − 2 + δ n − 1 ) δ n − 1 ] [ c o c 1 ⋮ c n ] = 3 [ Δ 1 δ 1 − Δ 0 δ 0 Δ 2 δ 2 − Δ 1 δ 1 ⋮ Δ n − 1 δ n − 1 − Δ n − 2 δ n − 2 ] \left[ \begin{array}{l} \delta _0& 2\left( \delta _0+\delta _1 \right)& \delta _1& & & \\ & \delta _1& 2\left( \delta _1+\delta _2 \right)& \delta _2& & \\ & & \ddots& \ddots& \ddots& \\ & & & \delta _{\mathrm{n}-2}& 2\left( \delta _{\mathrm{n}-2}+\delta _{\mathrm{n}-1} \right)& \delta _{\mathrm{n}-1}\\ \end{array} \right] \left[ \begin{array}{c} \mathrm{c}_{\mathrm{o}}\\ \mathrm{c}_1\\ \vdots\\ \mathrm{c}_{\mathrm{n}}\\ \end{array} \right] =3\left[ \begin{array}{c} \frac{\Delta _1}{\delta _1}-\frac{\Delta _0}{\delta _0}\\ \frac{\Delta _2}{\delta _2}-\frac{\Delta _1}{\delta _1}\\ \vdots\\ \frac{\Delta _{\mathrm{n}-1}}{\delta _{\mathrm{n}-1}}-\frac{\Delta _{\mathrm{n}-2}}{\delta _{\mathrm{n}-2}}\\ \end{array} \right] δ02(δ0+δ1)δ1δ12(δ1+δ2)δ2δn22(δn2+δn1)δn1 coc1cn =3 δ1Δ1δ0Δ0δ2Δ2δ1Δ1δn1Δn1δn2Δn2
此时方程左边为n-1个条件,有n+1个未知数,仍然缺两个条件,即上文提到的端点条件

端点条件:

端点条件,老师介绍了三种思路:

  1. 自然样条(Natural Spline):

S 0 ′ ′ ( x 0 ) = 0 ; S n − 1 ′ ′ ( x n ) = 0 \mathrm{S}''_0\left( \mathrm{x}_0 \right) =0;\mathrm{S}''_{\mathrm{n}-1}\left( \mathrm{x}_{\mathrm{n}} \right) =0 S0′′(x0)=0;Sn1′′(xn)=0

​ 评价为简单粗暴的直接赋值

  1. 夹紧的三次样条(Clamped Cubic Spline)
    S ′ 0 ( x 0 ) = v 0 ; S ′ n − 1 ( x n ) = v n S\prime_0\left( x_0 \right) =v_0;S\prime_{n-1}\left( x_n \right) =v_n S0(x0)=v0;Sn1(xn)=vn
    需要引入额外的条件进行赋值,对自然样条的一些补充,但基本思路一致。

  2. 非结三次样条(Not-a-knot cubic Spline)-MATLAB默认的spline函数命令

寻找三次导数的值进行代入,其实就是带入
S 0 ′ ′ ′ ( x 1 ) = 6 d 1 ; S n − 2 ′ ′ ′ ( x n − 1 ) = 6 d n − 1 \mathrm{S}_0'''\left( \mathrm{x}_1 \right) =6\mathrm{d}_1;\mathrm{S}_{\mathrm{n}-2}'''\left( \mathrm{x}_{\mathrm{n}-1} \right) =6\mathrm{d}_{\mathrm{n}-1} S0′′′(x1)=6d1;Sn2′′′(xn1)=6dn1
这里只编写前两个(懒,作业只要求前两个qaq)

那么前两种可以转化为:

自然样条:
{ c 0 = 0 c n = 0 \begin{cases} \mathrm{c}_0=0\\ \mathrm{c}_{\mathrm{n}}=0\\ \end{cases} {c0=0cn=0
加紧的三次样条:
{ 2 δ 0 c 0 + δ 0 c 1 = 3 ( Δ 0 δ 0 − v 0 ) δ n − 1 c n − 1 + 2 δ n − 1 c n = 3 ( v n − Δ n − 1 δ n − 1 ) \begin{cases} 2\delta _0\mathrm{c}_0+\delta _0\mathrm{c}_1=3\left( \frac{\Delta _0}{\delta _0}-\mathrm{v}_0 \right)\\ \delta _{\mathrm{n}-1}\mathrm{c}_{\mathrm{n}-1}+2\delta _{\mathrm{n}-1}\mathrm{c}_{\mathrm{n}}=3\left( \mathrm{v}_{\mathrm{n}}-\frac{\Delta _{\mathrm{n}-1}}{\delta _{\mathrm{n}-1}} \right)\\ \end{cases} 2δ0c0+δ0c1=3(δ0Δ0v0)δn1cn1+2δn1cn=3(vnδn1Δn1)
补充进方程即可求解。求解出cn后就是简单的回代,然后写成多项式即可。

最终求解方程的形式:
[ 2 δ 0 δ 0 δ 0 2 ( δ 0 + δ 1 ) δ 1 δ 1 2 ( δ 1 + δ 2 ) δ 2 ⋱ ⋱ ⋱ δ n − 2 2 ( δ n − 2 + δ n − 1 ) δ n − 1 δ n − 1 2 δ n − 1 ] [ c o c 1 ⋮ c n ] = 3 [ Δ 0 δ 0 − V 0 Δ 1 δ 1 − Δ 0 δ 0 Δ 2 δ 2 − Δ 1 δ 1 ⋮ Δ n − 1 δ n − 1 − Δ n − 2 δ n − 2 v n − Δ n − 1 δ n − 1 ] \left[ \begin{array}{l} 2\delta _0& \delta _0& & & & \\ \delta _0& 2\left( \delta _0+\delta _1 \right)& \delta _1& & & \\ & \delta _1& 2\left( \delta _1+\delta _2 \right)& \delta _2& & \\ & & \ddots& \ddots& \ddots& \\ & & & \delta _{\mathrm{n}-2}& 2\left( \delta _{\mathrm{n}-2}+\delta _{\mathrm{n}-1} \right)& \delta _{\mathrm{n}-1}\\ & & & & \delta _{\mathrm{n}-1}& 2\delta _{\mathrm{n}-1}\\ \end{array} \right] \left[ \begin{array}{c} \mathrm{c}_{\mathrm{o}}\\ \mathrm{c}_1\\ \vdots\\ \mathrm{c}_{\mathrm{n}}\\ \end{array} \right] =3\left[ \begin{array}{c} \frac{\Delta _0}{\delta _0}-\mathrm{V}_0\\ \frac{\Delta _1}{\delta _1}-\frac{\Delta _0}{\delta _0}\\ \frac{\Delta _2}{\delta _2}-\frac{\Delta _1}{\delta _1}\\ \vdots\\ \frac{\Delta _{\mathrm{n}-1}}{\delta _{\mathrm{n}-1}}-\frac{\Delta _{\mathrm{n}-2}}{\delta _{\mathrm{n}-2}}\\ \mathrm{v}_{\mathrm{n}}-\frac{\Delta _{\mathrm{n}-1}}{\delta _{\mathrm{n}-1}}\\ \end{array} \right] 2δ0δ0δ02(δ0+δ1)δ1δ12(δ1+δ2)δ2δn22(δn2+δn1)δn1δn12δn1 coc1cn =3 δ0Δ0V0δ1Δ1δ0Δ0δ2Δ2δ1Δ1δn1Δn1δn2Δn2vnδn1Δn1
标准三对角形式,函数一套结束。Perfect!!!!!!!!!!!

MATLAB代码:

调用指南:

当flag=0时,端点条件取为Natural Spline;

当flag=1时,端点条件取为Clamped Cubic Spline,若斜率不输入,默认为0);

当flag=2时,调用Matlab自带的spline命令。

function v=myCubicSpline(x,y,u,flag,varargin)
     if(nargin==4)
        v0=0;
        vn=0;
     end
     if(nargin==5)
        v0=varargin{1};
        vn=0;
     end
     if(nargin==6)
        v0=varargin{1};
        vn=varargin{2};
     end
     %初始化
     delta=zeros(length(x)-1,1);
     Delta=zeros(length(x)-1,1);
     for i=1:1:length(x)-1
        delta(i)=x(i+1)-x(i);
        Delta(i)=y(i+1)-y(i);
     end
     angle=zeros(length(x),1);
     rightb=zeros(length(x),1);
     for i=1:1:length(x)
        if(i==1) 
            angle(i)=2*delta(i);
            rightb(i)=3*(Delta(i)/delta(i)-v0);
        end
        if(i>1)&&(i<length(x))
            angle(i)=2*(delta(i)+delta(i-1));
            rightb(i)=3*(Delta(i)/delta(i)-Delta(i-1)/delta(i-1));
        end
        if(i==length(x))
            angle(i)=2*delta(i-1);
            rightb(i)=3*(vn-Delta(i-1)/delta(i-1));
        end
     end
     if(flag==1)
     %三对角求解
        c=tridecomposition(delta,delta,angle,rightb);%含c0;
        d=zeros(length(x)-1,1);
        b=zeros(length(x)-1,1);
        for i=1:1:length(x)-1
            d(i)=(c(i+1)-c(i))/(3*delta(i));
            b(i)=Delta(i)/delta(i)-2/3*delta(i)*c(i)-delta(i)*c(i+1)/3;
        end
        v=zeros(length(u),1);
        %回代
        for i=1:1:length(u)
            for j=1:1:length(x)-1
                if(u(i)>=x(j))&&(u(i)<=x(j+1))
                    v(i)=y(j)+b(j)*(u(i)-x(j))+c(j)*(u(i)-x(j))^2+d(j)*(u(i)-x(j))^3;
                    break;
                end
            end
        end
        return;
     end
     if(flag==0)
         rightb(1)=0;
         rightb(length(x))=0;
         angle(1)=1;
         angle(length(x))=1;
         up=delta;
         up(length(x)-1)=0;
        c=tridecomposition(up,up,angle,rightb);%含c0;
        d=zeros(length(x)-1,1);
        b=zeros(length(x)-1,1);
        for i=1:1:length(x)-1
            d(i)=(c(i+1)-c(i))/(3*delta(i));
            b(i)=Delta(i)/delta(i)-2/3*delta(i)*c(i)-delta(i)*c(i+1)/3;
        end
        v=zeros(length(u),1);
        %回代
        for i=1:1:length(u)
            for j=1:1:length(x)-1
                if(u(i)>=x(j))&&(u(i)<=x(j+1))
                    v(i)=y(j)+b(j)*(u(i)-x(j))+c(j)*(u(i)-x(j))^2+d(j)*(u(i)-x(j))^3;
                    break;
                end
            end
        end
        return;
     end
     if(flag==2)
        v=spline(x,y,u);
     end
end

function x=tridecomposition(up,down,angle,b)
    n=length(angle);
    for i = 2:1:n
        down(i-1) = down(i-1)/angle(i-1);
        angle(i) = angle(i) - up(i-1) * down(i-1);
        b(i) = b(i) - b(i-1) * down(i-1);
    end
    x(n)=b(n)/angle(n);
    for i=n-1:-1:1
        x(i)=(b(i)-up(i)*x(i+1))/angle(i);
    end
end

比较图像

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值