关键字:Matalb;逐次生成;曲线拟合
系列文章目录
数值分析——拉格朗日插值
数值分析——埃尔米特(Hermit)插值
数值分析——分段低次插值
数值分析——三次样条插值
文章目录
前言
在之前的文章中简单介绍了线性插值、抛物线插值、拉格朗日插值,以上的插值方法推导简单、公式结构紧凑是许多曲线拟合的首选方式。但是如果遇到增减插值点的情况时,需要全部进行重新计算。为了计算的方便,逐次生成插值多项式的插值函数——牛顿插值多项式便应运而生。
本文对一些复杂的流程与概念进行了简化与省略,如果想详细了解的小伙伴可以参考李庆扬老师的《数值分析 -第5版》的第2章(需要电子版请私戳)
一、理论推导
1.插值多项式的逐次生成
基于拉格朗日插值的推导思路,由一阶的线性插值函数来逐次深入到高阶的牛顿插值多项式。首先需要了解到线性插值的另一种表现形式——点斜式:
P
1
(
x
)
=
L
1
(
x
)
=
y
0
+
y
1
−
y
0
x
1
−
x
0
(
x
−
x
0
)
=
P
0
(
x
)
+
a
1
(
x
−
x
0
)
(1)
P_1\left( x \right)=L_1\left( x \right)=y_0+\frac{y_1-y_0}{x_1-x_0}\left( x - x_0 \right)=P_0\left( x \right)+a_1\left( x-x_0 \right) \tag{1}
P1(x)=L1(x)=y0+x1−x0y1−y0(x−x0)=P0(x)+a1(x−x0)(1)其中
P
0
(
x
)
=
y
0
P_0\left( x \right)=y_0
P0(x)=y0,且
a
1
=
y
1
−
y
0
x
1
−
x
0
=
f
(
x
1
)
−
f
(
x
0
)
x
1
−
x
0
a_1=\frac{y_1-y_0}{x_1-x_0}=\frac{f\left( x_1 \right)-f\left( x_0 \right)}{x_1-x_0}
a1=x1−x0y1−y0=x1−x0f(x1)−f(x0)被定义为函数的差商。点斜式的插值函数也满足:
P
1
(
x
0
)
=
y
0
,
P
1
(
x
1
)
=
y
1
(2)
P_1\left( x_0 \right)=y_0,P_1\left( x_1 \right)=y_1 \tag{2}
P1(x0)=y0,P1(x1)=y1(2)考虑三个点的抛物线插值函数
L
2
(
x
)
L_2\left( x \right)
L2(x)满足:
P
2
(
x
0
)
=
y
0
,
P
2
(
x
1
)
=
y
1
,
P
2
(
x
2
)
=
y
2
(3)
P_2\left( x_0 \right)=y_0,P_2\left( x_1 \right)=y_1,P_2\left( x_2 \right)=y_2 \tag{3}
P2(x0)=y0,P2(x1)=y1,P2(x2)=y2(3)基于此条件可以发现线性插值函数
P
1
(
x
)
P_1\left( x \right)
P1(x),其实就已经满足公式(3)的前两个条件,只需要在其基础上增加一个多项式使
P
2
(
x
)
P_2\left( x \right)
P2(x)满足公式(3)的第三个条件即可:
P
2
(
x
)
=
P
1
(
x
)
+
a
2
(
x
−
x
0
)
(
x
−
x
1
)
(4)
P_2\left( x \right)=P_1\left( x \right)+a_2\left( x-x_0 \right)\left( x-x_1 \right) \tag{4}
P2(x)=P1(x)+a2(x−x0)(x−x1)(4)其中
a
2
a_2
a2为待定系数可通过
P
2
(
x
2
)
=
y
2
P_2\left( x_2 \right)=y_2
P2(x2)=y2求得 :
a
2
=
P
2
(
x
2
)
−
P
1
(
x
2
)
(
x
−
x
0
)
(
x
−
x
1
)
=
f
(
x
2
)
−
f
(
x
1
)
x
2
−
x
1
−
f
(
x
1
)
−
f
(
x
0
)
x
1
−
x
0
x
2
−
x
1
(5)
a_2=\frac{P_2\left( x_2 \right)-P_1\left( x_2 \right)}{\left( x-x_0 \right)\left( x-x_1 \right)}=\frac{\frac{f\left( x_2 \right)-f\left( x_1 \right)}{x_2-x_1}-\frac{f\left( x_1 \right)-f\left( x_0 \right)}{x_1-x_0}}{x_2-x_1} \tag{5}
a2=(x−x0)(x−x1)P2(x2)−P1(x2)=x2−x1x2−x1f(x2)−f(x1)−x1−x0f(x1)−f(x0)(5)
2.均差
可以看到系数的表达式十分复杂,为了推导与理解,这里引入均差(差商)的定义:
f
[
x
0
,
x
1
,
⋯
,
x
k
−
1
,
x
k
]
=
f
[
x
0
,
x
1
,
⋯
,
x
k
−
2
,
x
k
]
−
f
[
x
0
,
x
1
,
⋯
,
x
k
−
2
,
x
k
−
1
]
x
k
−
x
k
−
1
=
f
[
x
1
,
⋯
,
x
k
]
−
f
[
x
0
,
⋯
,
x
k
−
1
]
x
k
−
x
0
(6)
\begin{align} f\left[x_0,x_1,\cdots,x_{k-1},x_k\right]&= \frac {f\left[x_0,x_1,\cdots,x_{k-2},x_k\right]- f\left[x_0,x_1,\cdots,x_{k-2},x_{k-1}\right]} {x_k-x_{k-1}} \\ &=\frac {f\left[x_1,\cdots,x_k\right]- f\left[x_0,\cdots,x_{k-1}\right]} {x_k-x_0} \end{align} \tag{6}
f[x0,x1,⋯,xk−1,xk]=xk−xk−1f[x0,x1,⋯,xk−2,xk]−f[x0,x1,⋯,xk−2,xk−1]=xk−x0f[x1,⋯,xk]−f[x0,⋯,xk−1](6)因此
f
[
x
0
,
x
k
]
=
f
(
x
k
)
−
f
(
x
0
)
x
k
−
x
0
f\left[x_0,x_k\right]=\frac{f\left( x_k \right)-f\left( x_0 \right)}{x_k-x_0}
f[x0,xk]=xk−x0f(xk)−f(x0)被称为函数
f
(
x
)
f\left( x \right)
f(x)关于点
x
0
x_0
x0、
x
k
x_k
xk的一阶均差,
f
[
x
0
,
x
1
,
x
k
]
=
f
[
x
0
,
x
k
]
−
f
[
x
0
,
x
1
]
x
k
−
x
1
f\left[x_0,x_1,x_k\right]=\frac{f\left[x_0,x_k\right]-f\left[x_0,x_1\right]}{x_k-x_1}
f[x0,x1,xk]=xk−x1f[x0,xk]−f[x0,x1]被称为函数
f
(
x
)
f\left( x \right)
f(x)关于点
x
0
x_0
x0、
x
1
x_1
x1、
x
k
x_k
xk的二阶均差,公式(6)被称为函数
f
(
x
)
f\left( x \right)
f(x)关于点
x
0
、
x
1
、
⋯
、
x
k
−
1
、
x
k
x_0、x_1、\cdots、x_{k-1}、x_k
x0、x1、⋯、xk−1、xk的k阶均差。公式(6)的另外一个表达形式为:
f
[
x
0
,
x
1
,
⋯
,
x
k
−
1
,
x
k
]
=
∑
j
=
0
k
f
(
x
j
)
(
x
j
−
x
0
)
⋯
(
x
j
−
x
j
−
1
)
(
x
j
−
x
j
+
1
)
⋯
(
x
j
−
x
k
)
(7)
f\left[x_0,x_1,\cdots,x_{k-1},x_k\right]= \sum_{j=0}^k \frac {f\left( x_j \right)} {\left( x_j-x_0 \right) \cdots \left( x_j-x_{j-1} \right) \left( x_j-x_{j+1} \right) \cdots \left( x_j-x_k \right)} \tag{7}
f[x0,x1,⋯,xk−1,xk]=j=0∑k(xj−x0)⋯(xj−xj−1)(xj−xj+1)⋯(xj−xk)f(xj)(7)根据文献,公式(6)到公式(7)的推导可以通过归纳法证明,当然不是不会😉 这里博主就不赘述了。对于在区间内存在
n
n
n阶导数的函数,
n
n
n阶均差可以表示为:
f
[
x
0
,
x
1
,
⋯
,
x
k
−
1
,
x
k
]
=
f
(
n
)
(
ξ
)
n
!
,
ξ
∈
[
a
,
b
]
(8)
f\left[x_0,x_1,\cdots,x_{k-1},x_k\right]=\frac{f^{\left( n \right)}\left( \xi \right)}{n!}, \quad \xi \in \left[a,b\right] \tag{8}
f[x0,x1,⋯,xk−1,xk]=n!f(n)(ξ),ξ∈[a,b](8)
3.牛顿插值多项式
引入均差的定义后线性插值函数表达式就可以推导为:
P
1
(
x
)
=
f
(
x
0
)
+
f
[
x
0
,
x
1
]
(
x
−
x
0
)
(9)
P_1\left( x \right) =f\left(x_0\right)+f\left[ x_0,x_1 \right]\left( x-x_0 \right) \tag{9}
P1(x)=f(x0)+f[x0,x1](x−x0)(9)抛物线插值函数表达式就可以推导为:
P
2
(
x
)
=
P
1
(
x
)
+
f
[
x
0
,
x
1
,
x
2
]
(
x
−
x
0
)
(
x
−
x
1
)
(10)
P_2\left( x \right) =P_1\left( x \right)+f\left[ x_0,x_1,x_2 \right]\left( x-x_0 \right)\left( x-x_1 \right) \tag{10}
P2(x)=P1(x)+f[x0,x1,x2](x−x0)(x−x1)(10)
n
n
n阶插值函数可以表示为:
P
n
(
x
)
=
P
n
−
1
(
x
)
+
f
[
x
0
,
x
1
,
⋯
,
x
n
−
1
,
x
n
]
(
x
−
x
0
)
(
x
−
x
1
)
⋯
(
x
−
x
n
−
1
)
=
P
n
−
2
(
x
)
+
f
[
x
0
,
x
1
,
⋯
,
x
n
−
2
,
x
n
−
1
]
(
x
−
x
0
)
(
x
−
x
1
)
⋯
(
x
−
x
n
−
2
)
+
f
[
x
0
,
x
1
,
⋯
,
x
n
−
1
,
x
n
]
(
x
−
x
0
)
(
x
−
x
1
)
⋯
(
x
−
x
n
−
1
)
=
⋯
=
f
(
x
0
)
+
f
[
x
0
,
x
1
]
(
x
−
x
0
)
+
⋯
+
f
[
x
0
,
x
1
,
⋯
,
x
n
−
1
,
x
n
]
(
x
−
x
0
)
(
x
−
x
1
)
⋯
(
x
−
x
n
−
1
)
(11)
\begin{align} P_n\left( x \right) &=P_{n-1}\left( x \right)+f\left[ x_0,x_1,\cdots,x_{n-1},x_n \right]\left( x-x_0 \right)\left( x-x_1 \right)\cdots\left( x-x_{n-1} \right) \\ &=P_{n-2}\left( x \right)+f\left[ x_0,x_1,\cdots,x_{n-2},x_{n-1} \right]\left( x-x_0 \right)\left( x-x_1 \right)\cdots\left( x-x_{n-2} \right)+f\left[ x_0,x_1,\cdots,x_{n-1},x_n \right]\left( x-x_0 \right)\left( x-x_1 \right)\cdots\left( x-x_{n-1} \right) \\ &=\cdots \\ &=f\left(x_0\right)+f\left[ x_0,x_1 \right]\left( x-x_0 \right)+\cdots+f\left[ x_0,x_1,\cdots,x_{n-1},x_n \right]\left( x-x_0 \right)\left( x-x_1 \right)\cdots\left( x-x_{n-1} \right) \end{align} \tag{11}
Pn(x)=Pn−1(x)+f[x0,x1,⋯,xn−1,xn](x−x0)(x−x1)⋯(x−xn−1)=Pn−2(x)+f[x0,x1,⋯,xn−2,xn−1](x−x0)(x−x1)⋯(x−xn−2)+f[x0,x1,⋯,xn−1,xn](x−x0)(x−x1)⋯(x−xn−1)=⋯=f(x0)+f[x0,x1](x−x0)+⋯+f[x0,x1,⋯,xn−1,xn](x−x0)(x−x1)⋯(x−xn−1)(11)该式就被称为牛顿插值多项式。
4.牛顿前插公式
在上文中的情况中,插值点都是随机分布。在一些特定的场合下,插值点的间隔是固定的,此时牛顿插值多项式就可以转化为即将介绍的牛顿前插公式。
由于插值点之间是等距的,故设:
x
k
=
x
0
+
k
h
k
=
0
,
1
,
⋯
,
n
−
1
,
n
(12)
x_k=x_0+kh \quad k=0,1,\cdots,n-1,n \tag{12}
xk=x0+khk=0,1,⋯,n−1,n(12)其中
h
h
h、
x
0
x_0
x0分别是步长和初始点。设
x
k
x_k
xk点的函数值为
f
k
=
f
(
x
k
)
(
k
=
0
,
1
,
⋯
,
n
−
1
,
n
)
f_k=f\left( x_k \right)\left( k=0,1,\cdots,n-1,n \right)
fk=f(xk)(k=0,1,⋯,n−1,n),称
Δ
f
k
=
f
k
+
1
−
f
k
\Delta f_k=f_{k+1}-f_{k}
Δfk=fk+1−fk为
x
k
x_k
xk处以
h
h
h为步长的一阶前向差分,称
Δ
2
f
k
=
Δ
f
k
+
1
−
Δ
f
k
\Delta^2 f_k=\Delta f_{k+1}-\Delta f_{k}
Δ2fk=Δfk+1−Δfk为
x
k
x_k
xk处以
h
h
h为步长的二阶前向差分,进而
Δ
n
f
k
=
Δ
n
−
1
f
k
+
1
−
Δ
n
−
1
f
k
(13)
\Delta^n f_k=\Delta^{n-1} f_{k+1}-\Delta^{n-1} f_{k} \tag{13}
Δnfk=Δn−1fk+1−Δn−1fk(13)称为
x
k
x_k
xk处以
h
h
h为步长的
n
n
n阶前向差分,各阶差分的计算路径如图:
导出均差与差分的关系:
f
[
x
k
,
⋯
,
x
k
+
m
]
=
1
m
!
1
h
m
Δ
m
f
k
,
m
=
1
,
2
,
⋯
,
n
(14)
f\left[ x_k,\cdots,x_{k+m} \right]=\frac{1}{m!}\frac{1}{h^m}\Delta^mf_k, \quad m=1,2,\cdots,n \tag{14}
f[xk,⋯,xk+m]=m!1hm1Δmfk,m=1,2,⋯,n(14)由此可得牛顿前插公式:
P
n
(
x
0
+
t
h
)
=
f
0
+
t
Δ
f
0
+
t
(
t
−
1
)
2
!
Δ
2
f
0
+
⋯
+
t
(
t
−
1
)
⋯
(
t
−
n
+
1
)
n
!
Δ
n
f
0
P_n\left( x_0+th \right)= f_0+t\Delta f_0+\frac{t\left( t-1 \right)}{2!}\Delta^2f_0+\cdots+\frac{t\left( t-1 \right) \cdots \left( t-n+1 \right)}{n!}\Delta^nf_0
Pn(x0+th)=f0+tΔf0+2!t(t−1)Δ2f0+⋯+n!t(t−1)⋯(t−n+1)Δnf0
二、MATLAB实现
1.利用暴力递归法实现牛顿插值多项式
由于牛顿多项式的计算每一步都可以使用前一步的计算结果,可以使用循环或递归的方式进行编程:
% 牛顿多项式插值
function [aArr]=func_NewtonInterpolation(xArr,yArr,n)
% ************************************************************
% 识别误差
% ************************************************************
xLength=length(xArr);
yLength=length(yArr);
% 插值数组不匹配
if xLength~=yLength
error('Error:The length of xArr=%d and yArr=%d are not equal in length.',xLength,yLength);
elseif xLength~=n
error('Error:The length of xArr=%d and n=%d are not equal.',xLength,n);
elseif yLength~=n
error('Error:The length of yArr=%d and n=%d are not equal.',yLength,n);
elseif n==0
error('Error:The n should gainer than %d.',n);
end
% ************************************************************
% 递归计算牛顿插值多项式
% ************************************************************
% 底层返回
if n==1
aArr=yArr(n);
return;
end
% 计算新增多项式
syms x;
newPoly=func_DifferenceQuotient1(xArr,yArr,n);
for i=1:1:n-1
newPoly=newPoly*(x-xArr(i));
end
% 递归
aArr=sym2poly(poly2sym(func_NewtonInterpolation(xArr(1:1:n-1),yArr(1:1:n-1),n-1),x)+newPoly);
end
2.牛顿插值多项式测试
针对已有函数表:
设计测试程序:
% 测试牛顿插值多项式函数——func_NewtonInterpolation
clc;
clear;
close all;
xArr=[0.40,0.55,0.65,0.80,0.90,1.05];
yArr=[0.41075,0.57815,0.69675,0.88811,1.02652,1.25382];
polyval(func_NewtonInterpolation(xArr(1:1:5),yArr(1:1:5),5),0.596)
测试结果:
3.利用暴力递归法实现牛顿前插多项式
实现代码如下:
% 差分形式的牛顿多项式插值
function [aArr]=func_DifferenceNewtonInterpolation(yArr,n)
% ************************************************************
% 识别误差
% ************************************************************
yLength=length(yArr);
if yLength~=n
error('Error:The length of yArr=%d and n=%d are not equal.',yLength,n);
elseif n==0
error('Error:The n should gainer than %d.',n);
end
% ************************************************************
% 递归计算牛顿插值多项式
% ************************************************************
% 底层返回
if n==1
aArr=yArr;
return;
end
% 计算新增多项式
syms t;
newpoly=func_Difference(yArr,n,n-1)/factorial(n-1);
for i=1:1:n-1
newpoly=newpoly*(t-i+1);
end
% 递归
aArr=sym2poly(poly2sym(func_DifferenceNewtonInterpolation(yArr(1:1:n-1),n-1),t)+...
newpoly);
end
4.牛顿前插多项式测试
针对
c
o
s
(
x
)
cos\left( x \right)
cos(x)已有函数表:
设计测试程序:
% 测试前插牛顿插值函数——func_DifferenceNewtonInterpolation
clc;
clear;
close all;
xArr=0.00:0.10:0.50;
yArr=[1,0.995,0.98007,0.95534,0.92106,0.87758];
polyval(func_DifferenceNewtonInterpolation(yArr(1:1:5),5),0.048)
cos(0.048)
测试结果:
总结
以上在本文中对牛顿插值多项式与牛顿前插多项式的推导进行了简单的介绍,并提供了MATLAB的实现代码与测试用例。
参考文献
[1]李庆扬,王能超,易大义. 数值分析.第5版[M]. 清华大学出版社, 2008.