数值分析——三次样条插值

关键字:Matalb;曲线拟合;矩阵

系列文章目录

数值分析——拉格朗日插值
数值分析——牛顿插值多项式
数值分析——埃尔米特(Hermit)插值
数值分析——分段低次插值



前言

  在之前的博文中接受少了分段低次插值函数,该插值函数具有一致的收敛性,但是光滑性较差,对于像高速飞机的机翼形线,船体放样等型值线往往要求有二阶光滑度,即二阶导数连续。样条曲线是由分段三次曲线并接而成,在连接点上二阶导数连续。本文将介绍以此为理念设计的三次样条插值函数。

本文对一些复杂的流程与概念进行了简化与省略,如果想详细了解的小伙伴可以参考李庆扬老师的《数值分析 -第5版》的第2章(需要电子版请私戳)


一、理论推导

1.三次样条函数

  设计一个函数 S ( x ) S\left( x \right) S(x)属于在区间 [ a , b ] [a,b] [a,b]上二阶导数连续的函数空间,即 S ( x ) ∈ C 2 [ a , b ] S\left( x \right) \in C^2 \left[ a,b \right] S(x)C2[a,b]。该函数在每个小区间 [ x j , x j + 1 ] \left[ x_{j},x_{j+1} \right] [xj,xj+1]上是三次多项式,其中 a = x 0 < x 1 < ⋯ < x n = b a=x_0<x_1<\cdots<x_n=b a=x0<x1<<xn=b,则称该 S ( x ) S\left( x \right) S(x)是节点 x 0 , x 1 , ⋯   , x n − 1 , x n x_0,x_1,\cdots,x_{n-1},x_{n} x0,x1,,xn1,xn上的三次样条函数。如果在插值点上的函数值 f 0 , f 1 , f 2 , ⋯   , f n − 1 , f n f_0,f_1,f_2,\cdots,f_{n-1},f_n f0,f1,f2,,fn1,fn是已知的,且:
S ( x j ) = f ( x j ) = y j j = 0 , 1 , ⋯   , n − 1 , n (1) S\left( x_j \right)=f\left( x_j \right)=y_j \quad j=0,1,\cdots,n-1,n \tag{1} S(xj)=f(xj)=yjj=0,1,,n1,n(1)则称函数 S ( x ) S\left( x \right) S(x)为三次样条插值函数。

2.三次样条插值函数的求解条件

  由三次样条插值函数的定义可知,在每个区间上函数有四个未知量,共有n个小区间,故共需要求4n个未知量。现已知:

  • 函数在区间分割点上的二阶导数连续:
    S ( x j − 0 ) = S ( x j + 0 ) 、 S ′ ( x j − 0 ) = S ′ ( x j + 0 ) 、 S ′ ′ ( x j − 0 ) = S ′ ′ ( x j + 0 ) (2) S\left( x_j-0 \right)=S\left( x_j+0 \right)、S^{'}\left( x_j-0 \right)=S^{'}\left( x_j+0 \right)、S^{''}\left( x_j-0 \right)=S^{''}\left( x_j+0 \right) \tag{2} S(xj0)=S(xj+0)S(xj0)=S(xj+0)S′′(xj0)=S′′(xj+0)(2)
  • 插值函数在插值点上与原函数相等:
    S ( x j ) = f ( x j ) (3) S\left( x_j\right)=f\left( x_j\right) \tag{3} S(xj)=f(xj)(3)

上述总共给出了 3 ( n − 1 ) + ( n + 1 ) = 4 n − 2 3\left(n-1\right)+\left(n+1\right)=4n-2 3(n1)+(n+1)=4n2个条件,还需要两个边界条件:

  • 已知两端的一阶导数值:
    S ′ ( x 0 ) = f ′ ( x 0 ) 、 S ′ ( x n ) = f ′ ( x n ) (4) S^{'}\left( x_0\right)=f^{'}\left( x_0\right)、S^{'}\left( x_n\right)=f^{'}\left( x_n\right) \tag{4} S(x0)=f(x0)S(xn)=f(xn)(4)
  • 已知两端的二阶导数值:
    S ′ ′ ( x 0 ) = f ′ ′ ( x 0 ) 、 S ′ ′ ( x n ) = f ′ ′ ( x n ) (5) S^{''}\left( x_0\right)=f^{''}\left( x_0\right)、S^{''}\left( x_n\right)=f^{''}\left( x_n\right )\tag{5} S′′(x0)=f′′(x0)S′′(xn)=f′′(xn)(5)
  • 原函数为以 [ x 0 , x n ] \left[x_0,x_n\right] [x0,xn]为周期的周期函数:
    S ( x 0 + 0 ) = S ( x n − 0 ) 、 S ′ ( x 0 + 0 ) = S ′ ( x n − 0 ) 、 S ′ ′ ( x 0 + 0 ) = S ′ ′ ( x n − 0 ) (6) S\left( x_0+0 \right)=S\left( x_n-0 \right)、S^{'}\left( x_0+0 \right)=S^{'}\left( x_n-0 \right)、S^{''}\left( x_0+0 \right)=S^{''}\left( x_n-0 \right) \tag{6} S(x0+0)=S(xn0)S(x0+0)=S(xn0)S′′(x0+0)=S′′(xn0)(6)如此确定的样条函数被称为周期样条函数。

3.三次样条插值函数的建立

  满足上述条件的插值函数可以有多种形式,这里介绍只介绍一种由二阶导数进行推导得出的插值函数。首先由于插值函数 S ( x ) S\left( x \right) S(x)在区间 [ x j , x j + 1 ] \left[ x_j,x_{j+1} \right] [xj,xj+1]中是三次多项式 ,可知二阶导数 S ′ ′ ( x ) S^{''}\left( x \right) S′′(x)在区间 [ x j , x j + 1 ] \left[ x_j,x_{j+1} \right] [xj,xj+1]中是一次函数,故可设:
S ′ ′ ( x ) = M j x j + 1 − x h j + M j + 1 x − x j h j (7) S^{''}\left( x \right)=M_j\frac{x_{j+1}-x}{h_j}+M_{j+1}\frac{x-x_j}{h_j} \tag{7} S′′(x)=Mjhjxj+1x+Mj+1hjxxj(7)对上述公式进行积分:
S ( x ) = M j ( x j + 1 − x ) 3 6 h j + M j + 1 ( x − x j ) 3 6 h j + C 1 x + C 2 (8) S\left( x \right)=M_j\frac{\left(x_{j+1}-x\right)^3}{6h_j}+M_{j+1}\frac{\left(x-x_{j}\right)^3}{6h_j}+C_1x+C_2 \tag{8} S(x)=Mj6hj(xj+1x)3+Mj+16hj(xxj)3+C1x+C2(8)其中 C 1 、 C 2 C_1、C_2 C1C2分别是两个待定的常数。根据 S ( x j ) = y j S\left( x_j \right)=y_j S(xj)=yj S ( x j + 1 ) = y j + 1 S\left( x_{j+1} \right)=y_{j+1} S(xj+1)=yj+1可求得:
S ( x ) = M j ( x j + 1 − x ) 3 6 h j + M j + 1 ( x − x j ) 3 6 h j + ( y j − M j h j 2 6 ) x j + 1 − x h j + ( y j + 1 − M j + 1 h j 2 6 ) x − x j h j (9) \begin{align} S\left( x \right)=&M_j\frac{\left(x_{j+1}-x\right)^3}{6h_j}+M_{j+1}\frac{\left(x-x_{j}\right)^3}{6h_j} \\ &+\left( y_j-\frac{M_jh^2_j}{6} \right)\frac{x_{j+1}-x}{h_j}+\left( y_{j+1}-\frac{M_{j+1}h^2_j}{6} \right)\frac{x-x_{j}}{h_j} \end{align} \tag{9} S(x)=Mj6hj(xj+1x)3+Mj+16hj(xxj)3+(yj6Mjhj2)hjxj+1x+(yj+16Mj+1hj2)hjxxj(9)其中 M j ( j = 0 , 1 , ⋯   , n − 1 , n ) M_j\left( j=0,1,\cdots,n-1,n \right) Mj(j=0,1,,n1,n)是未知的。对 S ( x ) S\left( x \right) S(x)求导可得:
S ′ ( x ) = − M j ( x j + 1 − x ) 2 2 h j + M j + 1 ( x − x j ) 2 2 h j + y j + 1 − y j h j − M j + 1 − M j 6 h j (10) S^{'}\left( x \right)=-M_j\frac{\left( x_{j+1}-x \right)^2}{2h_j}+M_{j+1}\frac{\left( x-x_j \right)^2}{2h_j}+\frac{y_{j+1}-y_j}{h_j}-\frac{M_{j+1}-M_j}{6}h_j \tag{10} S(x)=Mj2hj(xj+1x)2+Mj+12hj(xxj)2+hjyj+1yj6Mj+1Mjhj(10) S ′ ( x j − 0 ) = S ′ ( x j + 0 ) S^{'}\left( x_j-0 \right)=S^{'}\left( x_j+0 \right) S(xj0)=S(xj+0)可得:
μ j M j − 1 + 2 M j + λ j M j + 1 = d j , j = 1 , 2 , ⋯   , n − 2 , n − 1 (11) \mu_jM_{j-1}+2M_j+\lambda_jM_{j+1}=d_j,\quad j=1,2,\cdots,n-2,n-1 \tag{11} μjMj1+2Mj+λjMj+1=dj,j=1,2,,n2,n1(11)其中
μ j = h j − 1 h j − 1 + h j λ j = h j h j − 1 + h j d j = 6 f [ x j , x j + 1 ] − f [ x j − 1 , x j ] h j − 1 + h j = 6 f [ x j − 1 , x j , x j + 1 ] j = 1 , 2 , ⋯   , n − 2 , n − 1 (12) \begin{align} \mu_j &=\frac{h_{j-1}}{h_{j-1}+h_{j}} \\ \lambda_j &=\frac{h_{j}}{h_{j-1}+h_{j}} \\ d_j &=6\frac{f\left[ x_j,x_{j+1} \right]-f\left[ x_{j-1},x_{j} \right]}{h_{j-1}+h_{j}}=6f\left[ x_{j-1},x_j,x_{j+1} \right] \\ j &=1,2,\cdots,n-2,n-1 \end{align} \tag{12} μjλjdjj=hj1+hjhj1=hj1+hjhj=6hj1+hjf[xj,xj+1]f[xj1,xj]=6f[xj1,xj,xj+1]=1,2,,n2,n1(12)针对边界条件公式(4)可得:
[ 2 λ 0 μ 1 2 λ 1 ⋯ ⋯ ⋯ μ n − 1 2 λ n − 1 μ n 2 ] [ M 0 M 1  ⁣ : M n − 1 M n ] = [ d 0 d 1  ⁣ : d n − 1 d n ] (13) \begin{bmatrix} 2 & \lambda_0 & & & \\ \mu_1 & 2 & \lambda_1 & & \\ & \cdots & \cdots & \cdots & \\ & & \mu_{n-1} & 2 & \lambda_{n-1}\\ & & & \mu_n & 2\\ \end{bmatrix} \begin{bmatrix} M_0\\ M_1\\ \colon\\ M_{n-1}\\ M_n\\ \end{bmatrix}= \begin{bmatrix} d_0\\ d_1\\ \colon\\ d_{n-1}\\ d_n\\ \end{bmatrix} \tag{13} 2μ1λ02λ1μn12μnλn12 M0M1:Mn1Mn = d0d1:dn1dn (13)其中
λ 0 = 1 d 0 = 6 h 0 ( f [ x 0 , x 1 ] − f 0 ′ ) μ n = 1 d n = 6 h n − 1 ( f n ′ − f [ x n − 1 , x n ] ) (14) \begin{align} \lambda_0 &=1 \\ d_0 &=\frac{6}{h_0}\left( f\left[ x_0,x_1 \right]-f^{'}_0 \right) \\ \mu_n &=1 \\ d_n &=\frac{6}{h_{n-1}}\left( f^{'}_n-f\left[ x_{n-1},x_n \right] \right) \end{align} \tag{14} λ0d0μndn=1=h06(f[x0,x1]f0)=1=hn16(fnf[xn1,xn])(14)针对边界条件公式(5)可得:
[ 2 λ 1 μ 1 μ 2 2 λ 2 ⋯ ⋯ ⋯ μ n − 1 2 λ n − 1 λ n μ n 2 ] [ M 1 M 2  ⁣ : M n − 1 M n ] = [ d 1 d 2  ⁣ : d n − 1 d n ] (15) \begin{bmatrix} 2 & \lambda_1 & & & \mu_1\\ \mu_2 & 2 & \lambda_2 & & \\ & \cdots & \cdots & \cdots & \\ & & \mu_{n-1} & 2 & \lambda_{n-1}\\ \lambda_n & & & \mu_n & 2\\ \end{bmatrix} \begin{bmatrix} M_1\\ M_2\\ \colon\\ M_{n-1}\\ M_n\\ \end{bmatrix}= \begin{bmatrix} d_1\\ d_2\\ \colon\\ d_{n-1}\\ d_n\\ \end{bmatrix} \tag{15} 2μ2λnλ12λ2μn12μnμ1λn12 M1M2:Mn1Mn = d1d2:dn1dn (15)其中
λ n = h 0 h n − 1 + h 0 μ n = h n − 1 h n − 1 + h 0 d n = 6 f [ x 0 , x 1 ] − f [ x n − 1 , x n ] h 0 + h n − 1 (16) \begin{align} \lambda_n &=\frac{h_{0}}{h_{n-1}+h_{0}} \\ \mu_n &=\frac{h_{n-1}}{h_{n-1}+h_{0}} \\ d_n &=6\frac{f\left[ x_0,x_1 \right]-f\left[ x_{n-1},x_n \right]}{h_{0}+h_{n-1}} \end{align} \tag{16} λnμndn=hn1+h0h0=hn1+h0hn1=6h0+hn1f[x0,x1]f[xn1,xn](16)求解上述矩阵方程公式(13)或公式(15)即可得到插值函数公式(9)。

二、MATLAB实现

1.一阶导数边界条件

  基于一阶导数已知的边界条件:

% 三次样条插值
function [yOutArr]=func_CubicSplineInterpolation1(xInArr,yInArr,dyInArr,xQuery)
    % ************************************************************
    % 识别误差
    % ************************************************************
    xInLength=length(xInArr);
    yInLength=length(yInArr);
    dyInLength=length(dyInArr);
    xQueryLength=length(xQuery);
    % 插值数组不匹配
    if xInLength~=yInLength
        error('Error:The xInArr=%d and the yInArr=%d are not equal in length.',xInLength,yInLength);
    end
    % 插值数组不匹配
    if xInLength~=dyInLength
        error('Error:The xInArr=%d and the dyInArr=%d are not equal in length.',xInLength,dyInLength);
    end
    % 插值数组不为递增数组
    for i=1:1:xInLength-1
        if xInArr(i)>xInArr(i+1)
            error('Error:The xInArr should be a incremental array.');
        end
    end
    % 查询数组不为递增数组
    for i=1:1:xQueryLength-1
        if xQuery(i)>xQuery(i+1)
            error('Error:The xQuerry should be a incremental array.');
        end
    end
    % 查询数组中的点超出插值数组的范围——查询数组有为一个点
    if xQueryLength==1
        % 查询数组中的点超出插值数组的范围
        if xQuery(xQueryLength)>xInArr(xInLength)||xQuery(xQueryLength)<xInArr(1)
            error('Error:The num of the query array xQuery should gainer than %d and lesser than %d.',xInArr(1),xInArr(xInLength));
        end
    % 查询数组中的点超出插值数组的范围——查询数组有多个点
    else
        % 查询数组中的点超出插值数组的范围
        if xQuery(xQueryLength)>xInArr(xInLength)||xQuery(1)<xInArr(1)
            error('Error:The num of the xQuery should gainer than %d and lesser than %d.',xInArr(1),xInArr(xInLength));
        end
    end
    % ************************************************************
    % 插值数组只有单个值时不存在区间
    % ************************************************************
    if xInLength==1
        yOutArr=yInArr;
        return;
    end
    % ************************************************************
    % 插值数组只有两个点时直接使用三次Hermit插值
    % ************************************************************
    if xInLength==2
        yOutArr=func_PiecewiseCubeHermitInterpolation(xInArr,yInArr,dyInArr,xQuery);
        return;
    end
    % ************************************************************
    % 插值数组至少有三个点时
    % ************************************************************
    prev=0;pros=0;
    % 识别xQuerry查询数组在插值数组中的范围
    for i=1:1:xInLength-1
        % 查询数组为一个点
        if xQueryLength==1
            if (xQuery(xQueryLength)>xInArr(i)&&xQuery(xQueryLength)<xInArr(i+1))||...
                    (xQuery(xQueryLength)==xInArr(i))
                prev=i;pros=i+1;
                break;
            end
        % 查询数组有多个点
        else
            if(prev~=0&&pros~=0)
                break;
            end
            if(prev==0)
                if (xQuery(1)>xInArr(i)&&xQuery(1)<xInArr(i+1))||(xQuery(1)==xInArr(i))
                    prev=i;
                end
            end
            if(pros==0)
                if (xQuery(xQueryLength)>xInArr(xInLength-i)&&xQuery(xQueryLength)<xInArr(xInLength+1-i))||(xQuery(xQueryLength)==xInArr(xInLength+1-i))
                    pros=xInLength+1-i;
                end
            end
        end
    end
    % ************************************************************
    % 查询数组为一个点直接使用三次Hermit插值
    % ************************************************************
    if pros-prev==1
        yOutArr=func_PiecewiseCubeHermitInterpolation(xInArr,yInArr,dyInArr,xQuery);
        return;
    end
    % ************************************************************
    % 查询数组有多个点使用三次样条插值
    % ************************************************************
    miu=zeros(1,pros-prev+1);
    lambda=zeros(1,pros-prev+1);
    M=zeros(pros-prev+1,1);
    D=zeros(pros-prev+1,1);
    h=zeros(1,pros-prev);
    K=zeros(pros-prev+1,pros-prev+1);
    % 计算λ、μ、d、h
    for i=1:1:pros-prev+1
        if i<=pros-prev
            h(i)=xInArr(prev+i)-xInArr(prev-1+i);
        end
        if i==1
            miu(i)=0;
            lambda(i)=1;
            D(i)=(6/h(i))*(func_DifferenceQuotient1(xInArr(prev-1+i:1:prev+i),yInArr(prev-1+i:1:prev+i),2)-dyInArr(prev-1+i));
            K(i,i)=2;K(i,i+1)=lambda(i);
        elseif i>1&&i<pros-prev+1
            miu(i)=h(prev+i-2)/(h(prev+i-2)+h(prev-1+i));
            lambda(i)=h(prev-1+i)/(h(prev+i-2)+h(prev-1+i));
            D(i)=6*func_DifferenceQuotient1(xInArr(prev+i-2:1:prev+i),yInArr(prev+i-2:1:prev+i),3);
            K(i,i-1)=miu(i);K(i,i)=2;K(i,i+1)=lambda(i);
        elseif i==pros-prev+1
            miu(i)=1;
            lambda(i)=0;
            D(i)=(6/h(i-1))*(dyInArr(prev-1+i)-func_DifferenceQuotient1(xInArr(prev+i-2:1:prev-1+i),yInArr(prev+i-2:1:prev-1+i),2));
            K(i,i-1)=miu(i);K(i,i)=2;
        end
    end
    % 计算M
    M=K^(-1)*D;
    % 计算yOutArr
    syms x;
    yOutArr=zeros(1,xQueryLength);
    for i=1:1:xQueryLength
        if i==1
            S=M(1)*(xInArr(prev+1)-x)^3/(6*h(1))+...
                M(2)*(x-xInArr(prev))^3/(6*h(1))+...
                (yInArr(prev)-(M(1)*h(1)^2)/6)*(xInArr(prev+1)-x)/h(1)+...
                (yInArr(prev+1)-(M(2)*h(1)^2)/6)*(x-xInArr(prev))/h(1);
            yOutArr(i)=polyval(sym2poly(S),xQuery(i));
        elseif i>1&&i<xQueryLength
            for j=prev:1:pros-1
                if (xQuery(i)>xInArr(j)&&xQuery(i)<=xInArr(j+1))||xQuery(i)==xInArr(j)
                    pos=j;
                    break;
                end
            end
            S=M(pos-prev+1)*(xInArr(pos+1)-x)^3/(6*h(pos-prev+1))+...
                M(pos-prev+2)*(x-xInArr(pos))^3/(6*h(pos-prev+1))+...
                (yInArr(pos)-(M(pos-prev+1)*h(pos-prev+1)^2)/6)*(xInArr(pos+1)-x)/h(pos-prev+1)+...
                (yInArr(pos+1)-(M(pos-prev+2)*h(pos-prev+1)^2)/6)*(x-xInArr(pos))/h(pos-prev+1);
            yOutArr(i)=polyval(sym2poly(S),xQuery(i));
        elseif i==xQueryLength
            S=M(pros-prev)*(xInArr(pros)-x)^3/(6*h(pros-prev))+...
                M(pros-prev+1)*(x-xInArr(pros-1))^3/(6*h(pros-prev))+...
                (yInArr(pros-1)-(M(pros-prev+1)*h(pros-prev)^2)/6)*(xInArr(pros)-x)/h(pros-prev)+...
                (yInArr(pros)-(M(pros-prev+1)*h(pros-prev)^2)/6)*(x-xInArr(pros-1))/h(pros-prev);
            yOutArr(i)=polyval(sym2poly(S),xQuery(i));
        end
    end
end

2.二阶导数边界条件

  基于二阶导数已知的边界条件:

% 三次样条插值
function [yOutArr]=func_CubicSplineInterpolation2(xInArr,yInArr,ddy1,ddy2,xQuery)
    % ************************************************************
    % 识别误差
    % ************************************************************
    xInLength=length(xInArr);
    yInLength=length(yInArr);
    xQueryLength=length(xQuery);
    % 插值数组不匹配
    if xInLength~=yInLength
        error('Error:The xInArr=%d and the yInArr=%d are not equal in length.',xInLength,yInLength);
    end
    % 插值数组不为递增数组
    for i=1:1:xInLength-1
        if xInArr(i)>xInArr(i+1)
            error('Error:The xInArr should be a incremental array.');
        end
    end
    % 查询数组不为递增数组
    for i=1:1:xQueryLength-1
        if xQuery(i)>xQuery(i+1)
            error('Error:The xQuerry should be a incremental array.');
        end
    end
    % 查询数组中的点超出插值数组的范围——查询数组有为一个点
    if xQueryLength==1
        % 查询数组中的点超出插值数组的范围
        if xQuery(xQueryLength)>xInArr(xInLength)||xQuery(xQueryLength)<xInArr(1)
            error('Error:The num of the query array xQuery should gainer than %d and lesser than %d.',xInArr(1),xInArr(xInLength));
        end
    % 查询数组中的点超出插值数组的范围——查询数组有多个点
    else
        % 查询数组中的点超出插值数组的范围
        if xQuery(xQueryLength)>xInArr(xInLength)||xQuery(1)<xInArr(1)
            error('Error:The num of the xQuery should gainer than %d and lesser than %d.',xInArr(1),xInArr(xInLength));
        end
    end
    % ************************************************************
    % 插值数组只有单个值时不存在区间
    % ************************************************************
    if xInLength==1
        yOutArr=yInArr;
        return;
    end
    % ************************************************************
    % 插值数组只有两个点时直接使用三次Hermit插值
    % ************************************************************
    if xInLength==2
        yOutArr=func_PiecewiseCubeHermitInterpolation(xInArr,yInArr,dyInArr,xQuery);
        return;
    end
    % ************************************************************
    % 插值数组至少有三个点时
    % ************************************************************
    prev=0;pros=0;
    % 识别xQuerry查询数组在插值数组中的范围
    for i=1:1:xInLength-1
        % 查询数组为一个点
        if xQueryLength==1
            if (xQuery(xQueryLength)>xInArr(i)&&xQuery(xQueryLength)<xInArr(i+1))||...
                    (xQuery(xQueryLength)==xInArr(i))
                prev=i;pros=i+1;
                break;
            end
        % 查询数组有多个点
        else
            if(prev~=0&&pros~=0)
                break;
            end
            if(prev==0)
                if (xQuery(1)>xInArr(i)&&xQuery(1)<xInArr(i+1))||(xQuery(1)==xInArr(i))
                    prev=i;
                end
            end
            if(pros==0)
                if (xQuery(xQueryLength)>xInArr(xInLength-i)&&xQuery(xQueryLength)<xInArr(xInLength+1-i))||(xQuery(xQueryLength)==xInArr(xInLength+1-i))
                    pros=xInLength+1-i;
                end
            end
        end
    end
    % ************************************************************
    % 查询数组在插值数组的一个区间中时直接使用三次Hermit插值
    % ************************************************************
    if pros-prev==1
        yOutArr=func_PiecewiseCubeHermitInterpolation(xInArr,yInArr,dyInArr,xQuery);
        return;
    end
    % ************************************************************
    %  查询数组在插值数组的多个区间中时使用三次样条插值
    % ************************************************************
    miu=zeros(1,pros-prev);
    lambda=zeros(1,pros-prev);
    M=zeros(pros-prev+1,1);
    D=zeros(pros-prev,1);
    h=zeros(1,pros-prev);
    K=zeros(pros-prev,pros-prev);
    % 计算λ、μ、d、h
    for i=1:1:pros-prev
        h(i)=xInArr(prev+i)-xInArr(prev-1+i);
        lambda(i)=h(1)/(h(i)+h(1));
        miu(i)=1-lambda(i);
        d(i)=6*(func_DifferenceQuotient1(xInArr(prev:1:prev+1),yInArr(prev:1:prev+1),2)-...
            func_DifferenceQuotient1(xInArr(prev-1+i:1:prev+i),yInArr(prev-1+i:1:prev+i),2))/...
            (h(i)+h(1));
        K(i,i)=2;
        K(i,max(1,mod(i+1,pros-prev+1)))=lambda(i);
        K(i,max(1,mod(pros-prev-1+i,pros-prev+1)))=miu(i);
    end
    % 计算M
    M(2:1:pros-prev+1)=K^(-1)*D;
    M(1)=ddy1;
    M(pros-prev+1)=ddy2;
    % 计算yOutArr
    syms x;
    yOutArr=zeros(1,xQueryLength);
    for i=1:1:xQueryLength
        if i==1
            S=M(1)*(xInArr(prev+1)-x)^3/(6*h(1))+...
                M(2)*(x-xInArr(prev))^3/(6*h(1))+...
                (yInArr(prev)-(M(1)*h(1)^2)/6)*(xInArr(prev+1)-x)/h(1)+...
                (yInArr(prev+1)-(M(2)*h(1)^2)/6)*(x-xInArr(prev))/h(1);
            yOutArr(i)=polyval(sym2poly(S),xQuery(i));
        elseif i>1&&i<xQueryLength
            for j=prev:1:pros-1
                if (xQuery(i)>xInArr(j)&&xQuery(i)<=xInArr(j+1))||xQuery(i)==xInArr(j)
                    pos=j;
                    break;
                end
            end
            S=M(pos-prev+1)*(xInArr(pos+1)-x)^3/(6*h(pos-prev+1))+...
                M(pos-prev+2)*(x-xInArr(pos))^3/(6*h(pos-prev+1))+...
                (yInArr(pos)-(M(pos-prev+1)*h(pos-prev+1)^2)/6)*(xInArr(pos+1)-x)/h(pos-prev+1)+...
                (yInArr(pos+1)-(M(pos-prev+2)*h(pos-prev+1)^2)/6)*(x-xInArr(pos))/h(pos-prev+1);
            yOutArr(i)=polyval(sym2poly(S),xQuery(i));
        elseif i==xQueryLength
            S=M(pros-prev)*(xInArr(pros)-x)^3/(6*h(pros-prev))+...
                M(pros-prev+1)*(x-xInArr(pros-1))^3/(6*h(pros-prev))+...
                (yInArr(pros-1)-(M(pros-prev+1)*h(pros-prev)^2)/6)*(xInArr(pros)-x)/h(pros-prev)+...
                (yInArr(pros)-(M(pros-prev+1)*h(pros-prev)^2)/6)*(x-xInArr(pros-1))/h(pros-prev);
            yOutArr(i)=polyval(sym2poly(S),xQuery(i));
        end
    end
end

3.测试程序

  测试程序如下:

% 测试三次样条插值函数——func_CubicSplineInterpolation1、func_CubicSplineInterpolation2
clc;
clear;
close all;

x=-5.0:0.2:5.0;
y=zeros(1,length(x));
dy=zeros(1,length(x));
ddy=zeros(1,length(x));
for i=1:1:length(x)
    y(i)=1/(1+x(i)^2);
    dy(i)=-(2*x(i))/(x(i)^2 + 1)^2;
    ddy(i)=(8*x(i)^2)/(x(i)^2 + 1)^3 - 2/(x(i)^2 + 1)^2;
end
xq=-5.0:0.05:5.0;

yq1=func_CubicSplineInterpolation1(x,y,dy,xq);
yq2=func_CubicSplineInterpolation2(x,y,ddy(1),ddy(length(x)),xq);
figure(1)
plot(x,y,'k*',xq,yq1,'r-');
xlabel('x');ylabel('y');
legend('原函数','插值函数')
figure(2)
plot(x,y,'k*',xq,yq2,'b-');
xlabel('x');ylabel('y');
legend('原函数','插值函数')

得到结果:
Alt

图2.1 一阶导数边界条件下的三次样条插值结果

Alt

图2.2 二阶导数边界条件下的三次样条插值结果

可以看到的是一阶导数已知的条件下插值函数曲线较为光滑,而二阶导数边界条件下的插值函数曲线稍有曲折,但不能确定是否是普遍现象。


总结

  以上在本文中对三次样条插值插值两种形式的推导进行了简单的介绍,并提供了MATLAB的实现代码与测试用例。

参考文献

[1]李庆扬,王能超,易大义. 数值分析.第5版[M]. 清华大学出版社, 2008.

  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hvp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值