栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > Cocos2d-x

插值与拟合

Cocos2d-x 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

插值与拟合

目录
  • 导入:什么是插值?什么是拟合?
  • 插值
    • 拉格朗日插值法
    • Hermite插值法
    • MATLAB插值函数
  • 拟合
    • 最小二乘法
    • MATLAB多项式拟合
    • 基本拟合窗口
  • 总结

导入:什么是插值?什么是拟合?

导语:假设我们现在通过做实验得到了一批数据点,但光有点我们还无法知道这些数据代表了什么,到底数据点之间存在线性关系还是指数关系,对这些我们一概不知。因此我们需要找到一些方法来对散点进行处理得到变量之间的关系曲线,这样我们才好分析。(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=a0​x2+a1​x+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​=a0​x12​+a1​x1​+a2​y2​=a0​x22​+a1​x2​+a2​y3​=a0​x32​+a1​x3​+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)=y1​f1​(x)+y2​f2​(x)+y3​f3​(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, 1neq 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_{jneq i}^{1le jle 3} frac{x-x_j}{x_i-x_j} fi​(x)=Πj​=i1≤j≤3​xi​−xj​x−xj​​。因此,最终我们得到, f ( x ) = ∑ i = 1 3 y i f i ( x ) f(x)=sum_{i=1}^3 y_if_i(x) f(x)=∑i=13​yi​fi​(x)。

我们对三点的情况进行推广,就得到了更一般的拉格朗日插值法公式: f ( x ) = ∑ i = 1 N y i f i ( x ) f(x)=sum_{i=1}^N y_if_i(x) f(x)=∑i=1N​yi​fi​(x), f i ( x ) = Π j = 1 ,   i ≠ j N x − x j x i − x j f_i(x)=Pi_{j=1, ineq j}^{N} frac{x-x_j}{x_i-x_j} fi​(x)=Πj=1, i​=jN​xi​−xj​x−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插值法为例,讨论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)=y0​h0​(x)+y1​h1​(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)=Aleft(frac{x-x_{1}}{x_{0}-x_{1}}right)^{2} h0​(x)=A(x0​−x1​x−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​−x1​x−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​−x0​x−x0​​)(x0​−x1​x−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)=aleft(x-x_{0}right)left(frac{x-x_{1}}{x_{0}-x_{1}}right)^{2} H0​(x)=a(x−x0​)(x0​−x1​x−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​−x1​x−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_1left[ 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​−x0​x−x0​​)(x0​−x1​x−x1​​)2]+y1​[(1+2x0​−x1​x−x1​​)(x1​−x1​x−x0​​)2]+...+y0′​[(x−x0​)(x0​−x1​x−x1​​)2]+y1′​[(x−x1​)(x1​−x0​x−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​−xi​x−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) cdotsleft(x-x_{i-1}right)left(x-x_{i+1}right) cdotsleft(x-x_{n}right)}{left(x_{i}-x_{0}right) cdotsleft(x_{i}-x_{i-1}right)left(x_{i}-x_{i+1}right) cdotsleft(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_0rightarrow x_k, x_1 rightarrow x_{k+1}, y_0rightarrow y_k, y'_0rightarrow 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_kfrac{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​)+...+dk​h2s(s−h)2​+dk+1​h2s2(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​=hk​yk+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}} dk​w1​+w2​​=δk−1​w1​​+δk​w2​​,其中 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) dk​1​=21​(δk−1​1​+δk​1​)。

然后我们再来看 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 + xrightarrow 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+​)=hk​6δk​−2dk+1​−4dk​​;当 x → x k + 1 − xrightarrow 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插值函数。

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_iquad (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

由于偏差平方和 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=0m​ak​xik​]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=0m​ak​(∑i=1n​xik+j​)=∑i=1n​yi​xij​,

{ 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=1n​xi​+a2​∑i=1n​xi2​+⋯+am​∑i=1n​xim​=∑i=1n​yi​a0​∑i=1n​xi​+a1​∑i=1n​xi2​+a2​∑i=1n​xi3​+⋯+am​∑i=1n​xim+1​=∑i=1n​yi​xi​⋯⋯⋯a0​∑i=1n​xim​+a1​∑i=1n​xim+1​+a2​∑i=1n​xim+2​+⋯+am​∑i=1n​xi2m​=∑i=1n​yi​xim​​

当最高幂次为 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=1n​xi​=∑i=1n​yi​a0​∑i=1n​xi​+a1​∑i=1n​xi2​=∑i=1n​yi​xi​​

当最高幂次为 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​=∑yi​a0​∑xi​+a1​∑xi2​+a2​∑xi3​=∑xi​yi​a0​∑xi2​+a1​∑xi3​+a2​∑xi4​=∑xi2​yi​​

MATLAB多项式拟合

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=I0​e−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知识学习见:计算物理入门

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/967829.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号