三次样条插值(附上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(x−xi)+ci(x−xi)2+di(x−xi)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+1S′i−1(xi)=S′i(xi)Si−1′′(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(x−xi)+ci(x−xi)2+di(x−xi)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+1−yiδi=xi+1−xi
此时则有式子一:
Δ
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)
S′i−1(xi)=S′i(xi)
由于式子中具有x-xi项,对于右边求导后代入不难得到:
S
′
i
(
x
i
)
=
b
i
\mathrm{S}\prime_{\mathrm{i}}\left( \mathrm{x}_{\mathrm{i}} \right) =\mathrm{b}_{\mathrm{i}}
S′i(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
S′i−1(x)=bi−1+2ci−1(x−xi−1)+3di−1(x−xi−1)2S′i−1(xi)=bi−1+2ci−1δi−1+3di−1δi−12=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}}
Si−1′′(xi)=2ci−1+6δi−1di−1=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}
{di−1=3δi−11(ci−ci−1)→di=3δi1(ci+1−ci)bi=δiΔi−32δici−3δ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)
δi−1ci−1+2(δi−1+δi)ci+δici+1=3(δiΔi−δi−1Δi−1)
注意,此时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⋱δn−2⋱2(δn−2+δn−1)δn−1
coc1⋮cn
=3
δ1Δ1−δ0Δ0δ2Δ2−δ1Δ1⋮δn−1Δn−1−δn−2Δn−2
此时方程左边为n-1个条件,有n+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;Sn−1′′(xn)=0
评价为简单粗暴的直接赋值
-
夹紧的三次样条(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 S′0(x0)=v0;S′n−1(xn)=vn
需要引入额外的条件进行赋值,对自然样条的一些补充,但基本思路一致。 -
非结三次样条(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;Sn−2′′′(xn−1)=6dn−1
这里只编写前两个(懒,作业只要求前两个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Δ0−v0)δn−1cn−1+2δn−1cn=3(vn−δn−1Δn−1)
补充进方程即可求解。求解出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⋱δn−2⋱2(δn−2+δn−1)δn−1δn−12δn−1
coc1⋮cn
=3
δ0Δ0−V0δ1Δ1−δ0Δ0δ2Δ2−δ1Δ1⋮δn−1Δn−1−δn−2Δn−2vn−δn−1Δn−1
标准三对角形式,函数一套结束。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