目录
导语:假设我们现在通过做实验得到了一批数据点,但光有点我们还无法知道这些数据代表了什么,到底数据点之间存在线性关系还是指数关系,对这些我们一概不知。因此我们需要找到一些方法来对散点进行处理得到变量之间的关系曲线,这样我们才好分析。(PS:已经了解插值与拟合概念的读者可以在目录选择感兴趣的内容阅读)
那就以小明同学为例吧,小明同学正在做实验,他得到的实验原始数据为:x=1:1:17; y=[3.5 4 4.3 4.6 4.7 4.8 4.8 4.7 4.6 4 3.9 3.3 2.8 2.5 2.2 1.8 1.3],而他想知道数据x与y之间有什么关系,但遗憾的是,这位同学并没有什么特别的天赋能从数据中直接看出x和y之间的关系,那么怎样才能拥有这种“天赋”呢。
他很快想到了,虽然我们不能直接看出数据点之间的关系,但可以把这些点画在图上再观察啊!说干就干,小明立刻把数据点标在了图上。

好像还不是很直观,如果我们能根据数据点做出一条连续的线来那效果一定会更好,那么我们想办法将这些数据用线连起来吧。
既然是基于这些数据点来画图,那么我们来试试连点成线,先用直线将这些数据点连起来。

啊这,看上去虽然直观了一点,但如果小明敢在实验报告上这样搞肯定会被老师骂死的。于是小明立刻换了一种方法,他决定使用某种方法将线条做得弯曲一点。
看上去舒服多了。不过仔细观察可以发现,虽然曲线更好看了,但还是因必须通过所有数据点这一规定,而出现了一些不必要的弯折。
小明想着,反正实验数据有点波动正常,那非要连点成线呢,因此他决定更大胆一点!不再让曲线非得穿过所有数据点,而是打算根据所有数据点的位置去画一条尽可能靠近所有数据点的绝对光滑的曲线。
有前面的连线结果看上去好像数据线条好像对应着二次函数对应的曲线,那我们就试着拿这样的曲线去靠近数据点吧。

怎么样,是不是感觉舒服了不少。虽然小明同学实验做得不怎么样导致数据点产生了较多偏移,但我们依然可以判断出横纵轴变量之间应该符合二次函数的关系。
至此我们就理解了插值和拟合的概念。
插值就是我们为了稳妥起见,在保证曲线通过所有数据点时,使数据点间的线条尽可能光滑、尽可能靠近原始图像的方法;
拟合则是我们在发现了数据的函数关系后,大胆决定使用这种函数关系的图像去靠近所有数据点的方法。
> 拟合也是广义上的插值
好了,下课。
诶~,不对,同学们都回来,我们再拖个堂😆,把插值中对数据点中光滑曲线的操作和拟合时确保偏差最小的方法讲一讲。👨🏫 👨🏫 👨🏫
插值其实就是一个解压缩的过程,它其实是函数求值的逆过程——由值求函数。我们希望通过插值方法解压缩,来看到压缩之前的那个未知函数长什么样子。
有了上面的例子我们自然知道,直接把数据点连起来的方法不太聪明,我们要想办法用曲线通过所有数据点,而这曲线我们自然也不能凭感觉随便画一条,还是需要特定的数学方法给出。这样的根据已知数据点绘制曲线的特别方法就是插值的重点~
假设我们现在有三点,要找到一条可以穿过它的曲线,那么我们可以考虑求解多项式,使得其对应函数图像通过这三点。通过三点的曲线可以用二次多项式 y = a 0 x 2 + a 1 x + a 2 y=a_0x^2+a_1x+a_2 y=a0x2+a1x+a2 表示,将三点坐标带入则有方程组 { y 1 = a 0 x 1 2 + a 1 x 1 + a 2 y 2 = a 0 x 2 2 + a 1 x 2 + a 2 y 3 = a 0 x 3 2 + a 1 x 3 + a 2 \left\{\begin{array}{l} y_{1}=a_{0}x_1^2+a_{1} x_{1}+a_{2} \\ y_{2}=a_{0}x_2^2+a_{1} x_{2}+a_{2} \\ y_{3}=a_{0}x_3^2+a_{1} x_{3}+a_{2} \end{array}\right. ⎩⎨⎧y1=a0x12+a1x1+a2y2=a0x22+a1x2+a2y3=a0x32+a1x3+a2

虽然我们学过,两点确定一条直线,但三点可并不能确定一条二次曲线,因此,确定这样一条过三点的二次曲线的方法是多样的。然后呢,看到章节名就该想到,拉格朗日大神对这个问题又有着独特的看法,那么拉格朗日得到二次曲线的思路如下:
既然我无法直接得到这条二次曲线,那我就通过累加的方法,将三条二次曲线叠加起来得到这条曲线。既然在该过程中唯一要保证的就是通过三个点,那么我们就让三条曲线分别代表一个点。例如,第一根曲线 f 1 ( x ) f_1(x) f1(x),在 x 1 x_1 x1处取值为1,在其余两点取值为0;

再令第二根曲线
f
2
(
x
)
f_2(x)
f2(x),在
x
2
x_2
x2处取值为1,在其余两点取值为0;令第三根曲线
f
3
(
x
)
f_3(x)
f3(x),在
x
3
x_3
x3处取值为1,在其余两点取值为0;这样操作之后再令函数分别乘以对应的系数
y
1
,
y
2
,
y
3
y_1,\ y_2,\ y_3
y1, y2, y3 并求和。因此,最后得到
f
(
x
)
=
y
1
f
1
(
x
)
+
y
2
f
2
(
x
)
+
y
3
f
3
(
x
)
f(x)=y_1f_1(x)+y_2f_2(x)+y_3f_3(x)
f(x)=y1f1(x)+y2f2(x)+y3f3(x),这样的函数
f
(
x
)
f(x)
f(x) 就满足了通过三点的条件。

以上就是拉格朗日插值法的核心思想。接下来,我们对过三点的拉格朗日插值法进行严格的推导。
以第1条曲线为例,应当满足 f 1 ( x j ) = { 1 , 1 = j 0 , 1 ≠ j f_1(x_j)=\begin{cases} 1,\ 1=j \\ 0,\ 1\neq j \end{cases} f1(xj)={1, 1=j0, 1=j,那么显然, f 1 ( x ) = ( x − x 2 ) ( x − x 3 ) ( x 1 − x 2 ) ( x 1 − x 3 ) f_1(x)=\frac{(x-x_2)(x-x_3)}{(x_1-x_2)(x_1-x_3)} f1(x)=(x1−x2)(x1−x3)(x−x2)(x−x3)。
对其他曲线也有 f i ( x ) = Π j ≠ i 1 ≤ j ≤ 3 x − x j x i − x j f_i(x)= \Pi_{j\neq i}^{1\le j\le 3} \frac{x-x_j}{x_i-x_j} fi(x)=Πj=i1≤j≤3xi−xjx−xj。因此,最终我们得到, f ( x ) = ∑ i = 1 3 y i f i ( x ) f(x)=\sum_{i=1}^3 y_if_i(x) f(x)=∑i=13yifi(x)。
我们对三点的情况进行推广,就得到了更一般的拉格朗日插值法公式: f ( x ) = ∑ i = 1 N y i f i ( x ) f(x)=\sum_{i=1}^N y_if_i(x) f(x)=∑i=1Nyifi(x), f i ( x ) = Π j = 1 , i ≠ j N x − x j x i − x j f_i(x)=\Pi_{j=1,\ i\neq j}^{N} \frac{x-x_j}{x_i-x_j} fi(x)=Πj=1, i=jNxi−xjx−xj。
以数据点 (1,16), (2,18), (3,21), (4,17), (5,15), (6,12) 为例,使用拉格朗日插值法得到结果如下。

其对应代码为
%% 函数文件
function v=insert1(x,y,u)
n=length(x);
v=zeros(size(u)); % 创建与u尺寸一致的全0数组
for k=1:n
w=ones(size(u));
for j=1:n % 或写为 [1:k-1 k+1:n] 跳过k,这样在写一步便不需做判断
if j~=k w=(u-x(j))./(x(k)-x(j)).*w; end % 累乘求得f_i(x)
end
v=v+w*y(k); % 累和求得f(x)
end
end
%% 脚本文件
clear all; clc; close all
x=[1 2 3 4 5 6];
y=[16 18 21 17 15 12];
u=0.75:0.05:6.25;
v=insert1(x,y,u);
plot(x,y,'--',u,v,'-')
axis([0 7 6 22])
例题:已知 x = [-2 -1 1 2], y = [-3 -2 1 3],采用拉格朗日插值法,求 xi=0 处的函数值。读者动手计算此例,进一步掌握拉格朗日插值法的思想。[参考答案见文末]
Hermite插值也叫做带指定微商值的插值,由此已经能看出它的特点:拉格朗日插值的插值条件都是在给定节点取已知函数值,而Hermite插值还要求插值函数在给定节点上取已知导数值。
我们先以两点的Hermite插值即三次Hermite插值法为例,讨论Hermite插值法的思路。
给定函数 f ( x ) f(x) f(x) 上两点 ( x 0 , y 0 ) , ( x 1 , y 1 ) (x_0,\ y_0),\ (x_1,\ y_1) (x0, y0), (x1, y1) 及在 x 0 , x 1 x_0,x_1 x0,x1 处函数的的微商值 y 0 ′ , y 1 ′ y_0',\ y_1' y0′, y1′。
同拉格朗日插值法的处理,我们将函数
H
(
x
)
H(x)
H(x) 拆分成4个不同的函数,使得它们分别对应两点的函数值与微商值。因此,四个基函数如下

乘以对应系数,则有
H
(
x
)
=
y
0
h
0
(
x
)
+
y
1
h
1
(
x
)
+
y
0
′
H
0
(
x
)
+
y
1
′
H
1
(
x
)
H(x)=y_0 h_0(x)+y_1h_1(x)+y_0'H_0(x)+y_1'H_1(x)
H(x)=y0h0(x)+y1h1(x)+y0′H0(x)+y1′H1(x)。接下来的问题就是如何确定函数
h
i
(
x
)
h_i(x)
hi(x) 及
H
i
(
x
)
H_i(x)
Hi(x)。
以
h
0
(
x
)
h_0(x)
h0(x) 为例,可以看出
h
0
(
x
)
h_0(x)
h0(x) 有一个二阶零点
x
1
x_1
x1,因此,可以设
h
0
(
x
)
=
A
(
x
−
x
1
x
0
−
x
1
)
2
h_{0}(x)=A\left(\frac{x-x_{1}}{x_{0}-x_{1}}\right)^{2}
h0(x)=A(x0−x1x−x1)2 。另外,
h
0
(
x
)
h_{0}(x)
h0(x) 是一个不超过三次的多项式 (四个条件构造的函数不超过3次),故
A
A
A 可设为一次多项式,便得到
h
0
(
x
)
=
(
a
+
b
(
x
−
x
0
)
)
(
x
−
x
1
x
0
−
x
1
)
2
h_{0}(x)=(a+b(x-x 0))\left(\frac{x-x_{1}}{x_{0}-x_{1}}\right)^{2}
h0(x)=(a+b(x−x0))(x0−x1x−x1)2 ,再使用点
x
0
x_{0}
x0 的两条信息,就能分别解出
a
a
a 和
b
b
b 。最终解得:
h
0
(
x
)
=
(
1
+
2
x
−
x
0
x
1
−
x
0
)
(
x
−
x
1
x
0
−
x
1
)
2
h_{0}(x)=\left(1+2 \frac{x-x_{0}}{x_{1}-x_{0}}\right)\left(\frac{x-x_{1}}{x_{0}-x_{1}}\right)^{2}
h0(x)=(1+2x1−x0x−x0)(x0−x1x−x1)2
再以
H
0
H_{0}
H0 为例。
H
0
(
x
)
H_{0}(x)
H0(x) 有一个二阶零点
x
1
x_{1}
x1 和一个一阶零点
x
0
x_{0}
x0 ,于是可得
H
0
(
x
)
=
a
(
x
−
x
0
)
(
x
−
x
1
x
0
−
x
1
)
2
H_{0}(x)=a\left(x-x_{0}\right)\left(\frac{x-x_{1}}{x_{0}-x_{1}}\right)^{2}
H0(x)=a(x−x0)(x0−x1x−x1)2 。再用最后一个条件:
y
0
′
=
1
y_{0}^{\prime}=1
y0′=1 ,便可解得:
H
0
(
x
)
=
(
x
−
x
0
)
(
x
−
x
1
x
0
−
x
1
)
2
H_{0}(x)=\left(x-x_{0}\right)\left(\frac{x-x_{1}}{x_{0}-x_{1}}\right)^{2}
H0(x)=(x−x0)(x0−x1x−x1)2
这便求出了所有的
h
i
h_{i}
hi 和
H
i
H_{i}
Hi 。
因此
H
(
x
)
=
y
0
[
(
1
+
2
x
−
x
0
x
1
−
x
0
)
(
x
−
x
1
x
0
−
x
1
)
2
]
+
y
1
[
(
1
+
2
x
−
x
1
x
0
−
x
1
)
(
x
−
x
0
x
1
−
x
1
)
2
]
+
.
.
.
+
y
0
′
[
(
x
−
x
0
)
(
x
−
x
1
x
0
−
x
1
)
2
]
+
y
1
′
[
(
x
−
x
1
)
(
x
−
x
0
x
1
−
x
0
)
2
]
\begin{aligned} H(x)&=y_0 \left[ \left(1+2 \frac{x-x_{0}}{x_{1}-x_{0}}\right)\left(\frac{x-x_{1}}{x_{0}-x_{1}}\right)^{2}\right]+y_1\left[ \left(1+2 \frac{x-x_{1}}{x_{0}-x_{1}}\right)\left(\frac{x-x_{0}}{x_{1}-x_{1}}\right)^{2}\right]+...\\ &\quad +y_0'\left[\left(x-x_{0}\right)\left(\frac{x-x_{1}}{x_{0}-x_{1}}\right)^{2}\right]+y_1'\left[\left(x-x_{1}\right)\left(\frac{x-x_{0}}{x_{1}-x_{0}}\right)^{2}\right] \end{aligned}
H(x)=y0[(1+2x1−x0x−x0)(x0−x1x−x1)2]+y1[(1+2x0−x1x−x1)(x1−x1x−x0)2]+...+y0′[(x−x0)(x0−x1x−x1)2]+y1′[(x−x1)(x1−x0x−x0)2]
那么这就是Hermite插值法的思路。
接下来我们对其推广,得到n个点的 h i h_{i} hi 和 H i H_{i} Hi :
h i ( x ) = ( 1 − ω n ′ ′ ( x i ) ω n ′ ( x i ) ( x − x i ) ) l i 2 ( x ) = ( 1 + 2 ∑ j ≠ i x − x i x j − x i ) l i 2 ( x ) H i ( x ) = l i 2 ( x ) ( x − x i ) , i = 0 , 1 , ⋯ , n \begin{gathered} h_{i}(x)=\left(1-\frac{\omega_{n}^{\prime \prime}\left(x_{i}\right)}{\omega_{n}^{\prime}\left(x_{i}\right)}\left(x-x_{i}\right)\right) l_{i}^{2}(x)=\left(1+2 \sum_{j \neq i} \frac{x-x_{i}}{x_{j}-x_{i}}\right) l_{i}^{2}(x) \\ H_{i}(x)=l_{i}^{2}(x)\left(x-x_{i}\right), i=0,1, \cdots, n \end{gathered} hi(x)=(1−ωn′(xi)ωn′′(xi)(x−xi))li2(x)=⎝⎛1+2j=i∑xj−xix−xi⎠⎞li2(x)Hi(x)=li2(x)(x−xi),i=0,1,⋯,n
其中
l i ( x ) = ( x − x 0 ) ⋯ ( x − x i − 1 ) ( x − x i + 1 ) ⋯ ( x − x n ) ( x i − x 0 ) ⋯ ( x i − x i − 1 ) ( x i − x i + 1 ) ⋯ ( x i − x n ) , i = 0 , 1 , 2 , ⋯ , n l_{i}(x)=\frac{\left(x-x_{0}\right) \cdots\left(x-x_{i-1}\right)\left(x-x_{i+1}\right) \cdots\left(x-x_{n}\right)}{\left(x_{i}-x_{0}\right) \cdots\left(x_{i}-x_{i-1}\right)\left(x_{i}-x_{i+1}\right) \cdots\left(x_{i}-x_{n}\right)}, i=0,1,2, \cdots, n li(x)=(xi−x0)⋯(xi−xi−1)(xi−xi+1)⋯(xi−xn)(x−x0)⋯(x−xi−1)(x−xi+1)⋯(x−xn),i=0,1,2,⋯,n
不过,我们一般并不直接对大量点同时使用Hermite插值公式进行处理,这会导致龙格现象 (龙格现象介绍见插值章节末尾)。更常见的做法是将带处理数据分为多段,对于每段都使用Hermite三次插值法。
对于分段后的某两个端点,我们重新定义符号,令
x
0
→
x
k
,
x
1
→
x
k
+
1
,
y
0
→
y
k
,
y
0
′
→
d
k
x_0\rightarrow x_k,\ x_1 \rightarrow x_{k+1},\ y_0\rightarrow y_k,\ y'_0\rightarrow d_k
x0→xk, x1→xk+1, y0→yk, y0′→dk,同时为了方便表示,我们使
h
=
x
k
+
1
−
x
k
,
s
=
x
−
x
k
,
s
−
h
=
x
−
x
k
+
1
h=x_{k+1}-x_k,\ s=x-x_{k},\ s-h=x-x_{k+1}
h=xk+1−xk, s=x−xk, s−h=x−xk+1,因此Hermite三次插值法公式变为:
H
(
x
)
=
y
k
(
h
3
−
3
h
s
2
+
2
s
3
h
3
)
+
y
k
+
1
(
3
h
s
2
−
2
s
3
h
3
)
+
.
.
.
+
d
k
s
(
s
−
h
)
2
h
2
+
d
k
+
1
s
2
(
s
−
h
)
h
2
\begin{aligned} H(x)&=y_k \left( \frac{h^3-3hs^2+2s^3}{h^3} \right)+y_{k+1}\left(\frac{3hs^2-2s^3}{h^3}\right)+...\\ &\quad +d_k\frac{s(s-h)^2}{h^2}+d_{k+1}\frac{s^2(s-h)}{h^2} \end{aligned}
H(x)=yk(h3h3−3hs2+2s3)+yk+1(h33hs2−2s3)+...+dkh2s(s−h)2+dk+1h2s2(s−h)
看上去很顺利,我们可以很容易的得到两个节点之间的Hermite插值函数,但事实是,我们还不能确定导函数,我们无法根据待插值的数据求出 d k , d k + 1 d_k,\ d_{k+1} dk, dk+1。
对于导函数未知的问题,我们可以人为地添加一些条件凑出导函数。那么我们的方法有,保证一阶导数相等的分段三次Hermite插值多项式(pchip [piecewise cubic Hermite interpolating polynomial]),以及一阶、二阶导数都相等的三次样条(cubic spline)插值函数法。
我们先来看pchip方法,即保证函数
H
(
x
)
H(x)
H(x)的一阶导数连续。根据向前差分公式 [ 前差公式介绍:数值计算与MATLAB微积分 ],令
δ
k
=
y
k
+
1
−
y
k
h
k
\delta_k=\frac{y_{k+1}-y_k}{h_k}
δk=hkyk+1−yk,这是导函数
d
k
d_k
dk的过渡函数,同
δ
k
\delta_k
δk是向前差分,误差比较大,因此我们引入
d
k
d_k
dk的向后差分项
δ
k
−
1
\delta_{k-1}
δk−1。
接下来我们根据 δ k − 1 \delta_{k-1} δk−1和 δ k \delta_k δk合成 d k d_k dk,令 w 1 + w 2 d k = w 1 δ k − 1 + w 2 δ k \frac{w_1+w_2}{d_k}=\frac{w_1}{\delta_{k-1}}+\frac{w_2}{\delta_{k}} dkw1+w2=δk−1w1+δkw2,其中 w 1 = 2 h k + h k − 1 , w 2 = h k + 2 h k − 1 w_1=2h_k+h_{k-1},\ w_2=h_k+2h_{k-1} w1=2hk+hk−1, w2=hk+2hk−1, w 1 , w 2 w_1,w_2 w1,w2是前差公式与后插公式合成 d k d_k dk时的权重。有时也为了计算方便,将其简化为 1 d k = 1 2 ( 1 δ k − 1 + 1 δ k ) \frac{1}{d_k}=\frac{1}{2}\left( \frac{1}{\delta_{k-1}}+\frac{1}{\delta_k}\right) dk1=21(δk−11+δk1)。
然后我们再来看 cubic spline 方法,根据
H
(
x
)
H(x)
H(x) 的公式可得,
P
′
′
(
x
)
=
(
6
h
−
12
s
)
δ
k
+
(
6
s
−
2
h
)
d
k
+
1
+
(
6
s
−
4
h
)
d
k
h
2
P^{\prime \prime}(x)=\frac{(6 h-12 s) \delta_{k}+(6 s-2 h) d_{k+1}+(6 s-4 h) d_{k}}{h^{2}}
P′′(x)=h2(6h−12s)δk+(6s−2h)dk+1+(6s−4h)dk 。同时当
x
→
x
k
+
x\rightarrow x_k^+
x→xk+时,
s
=
0
s=0
s=0,因此
P
′
′
(
x
k
+
)
=
6
δ
k
−
2
d
k
+
1
−
4
d
k
h
k
P^{\prime \prime}\left(x_{k}^+\right)=\frac{6 \delta_{k}-2 d_{k+1}-4 d_{k}}{h_{k}}
P′′(xk+)=hk6δk−2dk+1−4dk;当
x
→
x
k
+
1
−
x\rightarrow x_{k+1}^-
x→xk+1−时,
s
=
h
s=h
s=h,因此
P
′
′
(
x
k
+
1
−
)
=
−
6
δ
k
+
4
d
k
+
1
+
2
d
k
h
k
P^{\prime \prime}\left(x_{k+1}^-\right)=\frac{-6 \delta_{k}+4 d_{k+1}+2 d_{k}}{h_{k}}
P′′(xk+1−)=hk−6δk+4dk+1+2dk,将下标都减一得到
P
′
′
(
x
k
−
)
=
−
6
δ
k
−
1
+
4
d
k
+
2
d
k
−
1
h
k
−
1
P^{\prime \prime}\left(x_{k}^-\right)=\frac{-6 \delta_{k-1}+4 d_{k}+2 d_{k-1}}{h_{k-1}}
P′′(xk−)=hk−1−6δk−1+4dk+2dk−1。由于cubic spline二阶导数连续,因此,当
h
k
=
h
k
+
1
h_k=h_{k+1}
hk=hk+1时,
d
k
−
1
+
4
d
k
+
d
k
+
1
=
3
δ
k
−
1
+
3
δ
k
d_{k-1}+4 d_{k}+d_{k+1}=3 \delta_{k-1}+3 \delta_{k}
dk−1+4dk+dk+1=3δk−1+3δk。
至此,我们就得到了 d k , d k + 1 d_k,\ d_{k+1} dk, dk+1,可以顺利的使用Hermite插值法对数据点进行插值。
上面介绍的都是插值函数的思路,接下来我们正式介绍MATLAB插值函数。
MATLAB插值的指令为 interp1(x,y,xi,'method'),其中
x
,
y
x,y
x,y为已知数据点,·xi·为插入的数据点,即为绘制插值结果所需的点的个数 (插值结果仅由数据点和插值方法确定而与xi取点密度无关,xi仅决定插值结果在绘图时的取点密度),·method·为插值方法。
除了我们上面介绍的插值方法外,相当然地,有一些更加简单的差值方法,比如 最近邻插值nearest,nearest插值函数的值等于距离其最近的数据点的值,
以三角函数sin(x)的拟合为例,图中画o处为已知数据点,画×处为插值结果,使用nearest 插值函数我们可以得到一个十分“朴素”的结果。

clear all; clc; close all
x=0:5; y=sin(x); % 已知数据点
xi=-1:0.1:6; % 插入的数据点个数
x0=-2:0.01:6; y0=sin(x0); % 绘制正弦曲线的数据点
yi1=interp1(x,y,xi,'nearest'); % 插值函数
plot(x,y,'o',xi,yi1,'x',x0,y0,'--')
同时还有线性插值linear,即将数据点直接用线段连接得到插值结果。以及之前提到的保形分段三次插值pchip,和分段三次样条插值spline。
这里就不继续介绍下去了,这里放出所有插值法的插值结果及局部图,只需要注释或反注释相应的绘图函数即可实现不同的插值结果。
四种插值函数效果
使用sin函数为例,四种插值函数拟合结果如下。

clc; clear all; close all
x=0:17; y=sin(x);
xi=0:0.3:10;
f1=interp1(x,y,xi,'nearest'); % 最近邻插值
f2=interp1(x,y,xi,'linear'); % 线性插值
f3=interp1(x,y,xi,'pchip'); % 立方插值
f4=interp1(x,y,xi,'spline'); % 样条插值
subplot(2,2,1)
plot(xi,f1,xi,f1,'r.'); title('nearest')
subplot(2,2,2)
plot(xi,f2,xi,f2,'r.'); title('linear')
subplot(2,2,3)
plot(xi,f3,xi,f3,'r.'); title('pchip')
subplot(2,2,4)
plot(xi,f4,xi,f4,'r.'); title('spline')
接下来我们对pchip及spline插值结果进行对比。

可以发现在该组数据中spline内插效果好于pchip,而在外插(外推)即在数据点之外的范围内,pchip插值效果更好一些。
该规律仅在部分情况使用,笔者在此对比只是为说明,插值函数选取对插值结果至关重要。
clc; clear all; close all
x=4:1:10; y=[4.3 4.6 5 5.3 5.3 5 4.6 ];
xi=2:0.3:12;
f1=interp1(x,y,xi,'nearest'); % 最近邻插值
f2=interp1(x,y,xi,'linear'); % 线性插值
f3=interp1(x,y,xi,'pchip'); % 保形分段三次插值
f4=interp1(x,y,xi,'spline'); % 分段三次样条插值
plot(x,y,'o')
hold on
%plot(xi,f1,'k--') % 取消该行注释即可绘制nearest插值结果
%plot(xi,f2,'.-')
plot(xi,f3,'b--')
plot(xi,f4,'r-.')
legend('原始数据','pchip','spline')
%legend('原始数据','最近区域插值 nearest','线性插值 linear','保形分段三次插值 pchip','分段三次样条插值 spline')
grid on
龙格现象
在计算过程中,利用多项式对某一函数近似逼近,计算相应的函数值。一般情况下,多项式的次数越多,需要的数据就越多,而预测也就越准确。但有时会出现插值次数越高,插值结果越偏离原函数的现象,称为龙格现象。
简单点说就是:插值多项式的震荡在数据点两端波动极大,产生明显的震荡,导致结果偏离原函数。
朗格朗日插值法例题参考答案:-2/3
曲线拟合也叫函数逼近,是一类特殊的插值方法。它只要求插值函数能合理地反映数据的基本趋势,并不要求函数能通过数据点。所以在插值处理时,函数与数据点的偏差为零,而在曲线似合时,函数与数据点则可能存在偏差 δ i = φ ( x i ) − y i ( i = 0 , 1 , 2 , … , n ) \delta_i=\varphi(x_i)-y_i\quad (i=0,1,2,\dots,n) δi=φ(xi)−yi(i=0,1,2,…,n),因此可以通过偏差来反映函数的逼近程度。
有一种判别标准为,使偏差的平方和 ∑ i = 0 n δ i 2 \sum_{i=0}^{n} \delta_{i}^{2} ∑i=0nδi2 为最小。这样的方法称最佳平方逼近,也叫曲线拟合的最小二乘法,是最常用的方法。
如果考虑到数据在精度上的差异, 可以加上一定的权重因子, 即要求加 权的偏差的平方和 ∑ i = 0 n λ i δ i 2 \sum_{i=0}^{n} \lambda_{i} \delta_{i}^{2} ∑i=0nλiδi2 为最小。
设所求的拟合函数 φ ( x ) \varphi(x) φ(x) 是一个 m m m 次多项式 P m ( x ) ( m < n ) P_{m}(x)(m<n) Pm(x)(m<n), P m ( x ) = a 0 + a 1 x + ⋯ + a m x m = ∑ j = 0 m a j x j P_{m}(x)=a_{0}+a_{1} x+\cdots+a_{m} x^{m}=\sum_{j=0}^{m} a_{j} x^{j} Pm(x)=a0+a1x+⋯+amxm=∑j=0majxj,通过选取多项式的系数 a j a_{j} aj,使得数据点 ( x i , y i ) (x_i,y_i) (xi,yi)与拟合函数的偏差平方和 ∑ i = 1 n δ i 2 = ∑ i = 1 n [ y i − P m ( x i ) ] 2 = F ( a 0 , a 1 , ⋯ , a m ) \sum_{i=1}^{n} \delta_{i}^{2}=\sum_{i=1}^{n}\left[y_{i}-P_{m}\left(x_{i}\right)\right]^{2}=F\left(a_{0}, a_{1}, \cdots, a_{m}\right) ∑i=1nδi2=∑i=1n[yi−Pm(xi)]2=F(a0,a1,⋯,am) 最小。
由于偏差平方和
F
F
F 与所有
a
i
a_i
ai有关,那么我们可以使
F
F
F依次对
a
i
a_i
ai求偏导,偏导为0处
F
F
F 便取得极值,因此由多元函数取极值的条件得方程组
∂
F
∂
a
j
=
−
2
∑
i
=
1
n
[
y
i
−
∑
k
=
0
m
a
k
x
i
k
]
x
i
j
=
0
\frac{\partial F}{\partial a_{j}}=-2 \sum_{i=1}^{n}\left[y_{i}-\sum_{k=0}^{m} a_{k} x_{i}^{k}\right] x_{i}^{j}=0
∂aj∂F=−2∑i=1n[yi−∑k=0makxik]xij=0,移项得
∑
k
=
0
m
a
k
(
∑
i
=
1
n
x
i
k
+
j
)
=
∑
i
=
1
n
y
i
x
i
j
\sum_{k=0}^{m} a_{k}\left(\sum_{i=1}^{n} x_{i}^{k+j}\right)=\sum_{i=1}^{n} y_{i} x_{i}^{j}
∑k=0mak(∑i=1nxik+j)=∑i=1nyixij,
即
{
n
a
0
+
a
1
∑
i
=
1
n
x
i
+
a
2
∑
i
=
1
n
x
i
2
+
⋯
+
a
m
∑
i
=
1
n
x
i
m
=
∑
i
=
1
n
y
i
a
0
∑
i
=
1
n
x
i
+
a
1
∑
i
=
1
n
x
i
2
+
a
2
∑
i
=
1
n
x
i
3
+
⋯
+
a
m
∑
i
=
1
n
x
i
m
+
1
=
∑
i
=
1
n
y
i
x
i
⋯
⋯
⋯
a
0
∑
i
=
1
n
x
i
m
+
a
1
∑
i
=
1
n
x
i
m
+
1
+
a
2
∑
i
=
1
n
x
i
m
+
2
+
⋯
+
a
m
∑
i
=
1
n
x
i
2
m
=
∑
i
=
1
n
y
i
x
i
m
\left\{\begin{array}{l} n a_{0}+a_{1} \sum_{i=1}^{n} x_{i}+a_{2} \sum_{i=1}^{n} x_{i}^{2}+\cdots+a_{m} \sum_{i=1}^{n} x_{i}^{m}=\sum_{i=1}^{n} y_{i} \\ a_{0} \sum_{i=1}^{n} x_{i}+a_{1} \sum_{i=1}^{n} x_{i}^{2}+a_{2} \sum_{i=1}^{n} x_{i}^{3}+\cdots+a_{m} \sum_{i=1}^{n} x_{i}^{m+1}=\sum_{i=1}^{n} y_{i} x_{i} \\ \quad \cdots \cdots \cdots \\ a_{0} \sum_{i=1}^{n} x_{i}^{m}+a_{1} \sum_{i=1}^{n} x_{i}^{m+1}+a_{2} \sum_{i=1}^{n} x_{i}^{m+2}+\cdots+a_{m} \sum_{i=1}^{n} x_{i}^{2 m}=\sum_{i=1}^{n} y_{i} x_{i}^{m} \end{array}\right.
⎩⎪⎪⎨⎪⎪⎧na0+a1∑i=1nxi+a2∑i=1nxi2+⋯+am∑i=1nxim=∑i=1nyia0∑i=1nxi+a1∑i=1nxi2+a2∑i=1nxi3+⋯+am∑i=1nxim+1=∑i=1nyixi⋯⋯⋯a0∑i=1nxim+a1∑i=1nxim+1+a2∑i=1nxim+2+⋯+am∑i=1nxi2m=∑i=1nyixim
当最高幂次为 1 时就是最小二乘法的拟合直线: { n a 0 + a 1 ∑ i = 1 n x i = ∑ i = 1 n y i a 0 ∑ i = 1 n x i + a 1 ∑ i = 1 n x i 2 = ∑ i = 1 n y i x i \left\{\begin{array}{l} n a_{0}+a_{1} \sum_{i=1}^{n} x_{i}=\sum_{i=1}^{n} y_{i} \\ a_{0} \sum_{i=1}^{n} x_{i}+a_{1} \sum_{i=1}^{n} x_{i}^{2}=\sum_{i=1}^{n} y_{i} x_{i} \end{array}\right. {na0+a1∑i=1nxi=∑i=1nyia0∑i=1nxi+a1∑i=1nxi2=∑i=1nyixi
当最高幂次为 2 时就是最小二乘法的抛物线拟合: { n a 0 + a 1 ∑ x i + a 2 ∑ x i 2 = ∑ y i a 0 ∑ x i + a 1 ∑ x i 2 + a 2 ∑ x i 3 = ∑ x i y i a 0 ∑ x i 2 + a 1 ∑ x i 3 + a 2 ∑ x i 4 = ∑ x i 2 y i \left\{\begin{array}{c} n a_{0}+a_{1} \sum x_{i}+a_{2} \sum x_{i}^{2}=\sum y_{i} \\ a_{0} \sum x_{i}+a_{1} \sum x_{i}^{2}+a_{2} \sum x_{i}^{3}=\sum x_{i} y_{i} \\ a_{0} \sum x_{i}^{2}+a_{1} \sum x_{i}^{3}+a_{2} \sum x_{i}^{4}=\sum x_{i}^{2} y_{i} \end{array}\right. ⎩⎨⎧na0+a1∑xi+a2∑xi2=∑yia0∑xi+a1∑xi2+a2∑xi3=∑xiyia0∑xi2+a1∑xi3+a2∑xi4=∑xi2yi
MATLAB使用polyfit(x,y,n)对数据作最小二乘法的多项式拟合,使用polyval(P,x)计算多项式的值。其中x,y为数据点,n为拟合多项式的最高幂次,P为被求值的多项式系数,多项式系数按降序排列。
polynomial 多项式,fitting 拟合,value 值
以二次曲线拟合为例,对二次函数添加误差后拟合。
clc; clear all; close all
x=0:0.5:5;
y0=1*x+0.2.*x.*x; % 给定二次曲线
y=[0.5 -0.18 -0.01 0.13 0.1 0.31 -0.22 -0.31 0.2 0.4 -0.14]; % 加入误差
yy=y0+y;
P=polyfit(x,yy,2) % 二次曲线拟合,此处可以输出拟合结果,为显示结果故不加分号
% 输出:P = [0.2261 0.8433 0.2345],即拟合结果为 yy=0.2261*x^2+0.8433*x+0.2345,系数按降序排列
z=polyval(P,x); % 由拟合函数P计算得到的函数值,可以用来计算偏差
plot(x,z,x,yy,'r*')

上面介绍的是多项式拟合,而我们还可能遇到指数函数、幂函数的拟合,这种情况应当怎样处理呢?
以指数函数为例, I = I 0 e − a x I=I_0 e^{-ax} I=I0e−ax,对其两边取对数则, ln I 0 − a x \ln I_0\ -ax lnI0 −ax,拟合得到的常数项即为 ln I 0 \ln I_0 lnI0,一次项系数即为 − a -a −a。
clc; clear all; close all
x=[0.2 0.3 0.4 0.5 0.6 0.7 0.8];
I=[3.16 2.38 1.75 1.34 1.0 0.74 0.56];
y=log(I);
polyfit(x,y,1) % ans=[-2.8883 1.7283], I0=exp(1.7283)=5.6311, a=-(-2.8883)=2.8883
% 也可以使用非线性拟合函数 lsqcurvefit
x0=[1 1]; % 起点拟合模型
fun=@(x0,x)x0(1)*exp(x0(2)*x);
lsqcurvefit(fun,x0,x,I) % ans=[5.6361 -2.8906]
可以在MATLAB图形窗口Figure中找到工具内的基本拟合,具体位置如下

在基本拟合内有多种拟合类型,且可以选择是否显示方程、R^2等,极其方便。

此外,MATLAB还有 Curve Fitting 这样的APP供选择,Curve Fitting位置如下。

那么MATLAB插值与拟合篇基本上就结束了,文章可能有些长,但还是希望读者可以耐心看完。文章包括了对插值、拟合思路的推导以及如何使用MATLAB进行插值与拟合,文章风格相较于以往的文章更加像科普文而非知识总结,希望读者能有比较良好的阅读体验。如果您喜欢我的文章,欢迎关注我,也欢迎与我讨论。
参考文献
[1] 长尾科技 如何直观地理解拉格朗日插值法?
更多MATLAB知识学习见:计算物理入门
大家好!我对我的:username字段进行了一个小的验证,它应该是4到30个字符。我写了一个验证::length=>{:within=>4..30,:message=>I18n.t('activerecord.errors.range')-我想显示一个错误各种错误的消息(不像,太长或太短),但这里有一个问题-我可以将最小值和最大值都传递给翻译,以便有类似的东西:用户名应该在4到30个字符之间。目前我有:range:"shouldbebetween%{count}and%{count}characters",这显然不起作用(只是为了检查)。是否可以从范围中获取这些值?谢谢大家的指教!
给定这段代码:has_many:foos,:finder_sql=#{id}部分被过早插入。我如何逃脱它? 最佳答案 在定界符两边加上单引号:has_many:foos,:finder_sql= 关于ruby-如何转义Ruby字符串插值?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/2052724/
我正在尝试使用Rails在ActionMailer中设置电子邮件地址。在它被硬编码之前,但我们现在想让它们成为ENV变量,这样我们就不需要在每次电子邮件更改时都修改代码。这是它目前的定义方式:from='"NameofPerson"'我已经尝试使用ENV['EMAIL']将电子邮件设置为环境变量,但即使使用#{ENV['EMAIL'}我也没有运气。谁能指出我正确的方向? 最佳答案 您不能在Ruby中对单引号字符串使用字符串插值。但是双引号字符串可以!from="'NameofPerson'"但是如果你想用双引号将NameofPers
有没有办法在AppleScript中实现Ruby风格的字符串插值?我需要一种灵活的方式来创建字符串模式。ruby:fn="john"=>"john"ln="doe"=>"doe"addresses=[]=>[]addresses["jdoe@company.com"]addresses["jdoe@company.com","john.doe@company.com"]苹果脚本:setfnto"john"setlnto"doe"settheAddressesto{}#jdoe@company.comcopy[somethinghere]&"@company.com"totheendof
我正在寻找进行对数回归(对数方程的曲线拟合)的Rubygem或库。我试过statsample(http://ruby-statsample.rubyforge.org/),但它似乎没有我要找的东西。有人有什么建议吗? 最佳答案 尝试使用“statsample”gem。您可以使用类似的方法执行指数、对数、幂、正弦或任何其他变换。我希望这有帮助。require'statsample'#IndependentVariablex_data=[Math.exp(1),Math.exp(2),Math.exp(3),Math.exp(4),Ma
我的目标是用散列中的值替换字符串中的键。我是这样做的:"hello%{name},todayis%{day}"%{name:"Tim",day:"Monday"}如果字符串中的键在散列中丢失:"hello%{name},todayis%{day}"%{name:"Tim",city:"Lahore"}然后它会抛出一个错误。KeyError:key{day}notfound预期结果应该是:"helloTim,todayis%{day}"or"helloTim,todayis"有人可以指导我只替换匹配的键而不抛出任何错误吗? 最佳答案
我不明白,为什么eval会这样工作:"123#{456.to_s}789"#=>"123456789"eval('123#{456.to_s}789')#=>123如何在eval中插入字符串?更新:谢谢friend们。有效。因此,如果您有一个带有#{}的字符串变量,您希望稍后对其进行评估,您应该按照以下说明进行操作:string='123#{456}789'eval("\""+string+"\"")#=>123456789或string='123#{456}789'eval('"'+string+'"')#=>123456789 最佳答案
我认为ruby只是调用方法to_s但我无法解释它是如何工作的:classFakedefto_sselfendend"#{Fake.new}"根据逻辑,由于无限递归,这应该将堆栈级别提升得太深。但它工作正常,似乎从对象调用#to_s。=>"#"但为什么呢?已添加:classFakedefto_sFake2.newendendclassFake2defto_s"Fake2#to_s"endend此代码在两种情况下的工作方式不同:puts"#{Fake.new}"=>"#"但是:putsFake.new.to_s=>"Fake2#to_s"我觉得不正常。有人可以建议在ruby解释器中
我有一个heredoc,我正在使用#{}插入一些其他字符串,但有一个实例我也想写实际文本#{some_ruby_stuff}在我的heredoc中,没有对其进行插值。有没有办法逃避#{.我试过“\”,但没有成功。虽然它逃脱了#{},它还包括“\”:>>"development\#{RAILS_ENV}\n" 最佳答案 对于无需手动转义所有潜在插值的heredoc,您可以使用单引号样式heredoc。它是这样工作的:item= 关于ruby-on-rails-如何从字符串插值中逃脱#{,我
如何在单引号内进行插值?我试过类似的方法,但有两个问题。string='textcontains"#{search.query}"'没用我需要最终字符串将动态内容用双引号括起来,如下所示:'textcontains"candy"'可能看起来很奇怪,但是gem我正在使用的需要这个。 最佳答案 如果您不想转义双引号,可以使用%{textcontains"#{search.query}"}"textcontains\"#{search.query}\"". 关于ruby-单引号内的插值,我们在