数值分析——分段低次插值

分段低次插值

为什么需要分段低次插值

之前拉格朗日插值时,可以发现,当插值节点很多时,会发生龙格现象,即在数据点的两端边缘出,多项式值与被插值的原函数差值很大。

解决这种现象可以通过所谓的分段低次插值来逼近原函数,所谓分段低次插值,就是分段低次多项式,对每两个数据点之间的小区间进行低次多项式插值。

常用的分段低次插值有:分段线性插值分段三次Hermite插值三次样条插值

分段线性插值

分段线性插值很简单,实际上就是将所有数据点用折线连起来,得到的分段函数就是分段线性插值。但很明显,这样插值得到的函数在数据点处一阶导数不存在。

分段三次Hermite插值

同样很简单,只要在任意一个小区间 [ x k , x k + 1 ] [x_k,x_{k+1}] [xk,xk+1]进行两点三次Hermite插值即可。

分段三次Hermite插值虽然更复杂,但保证了插值函数一阶连续可导,更光滑。

三次样条插值

三次样条插值对插值函数光滑性要求更高:

记插值函数 S ( x ) S(x) S(x),要求:

  • S ( x ) S(x) S(x)整体二阶连续可导。
  • 每个小区间上都是三次多项式。

现在来看看如何求三次样条插值,首先,假定数据点 a = x 0 < x 1 < . . . < x n = b a=x_0\lt x_1\lt ...\lt x_n=b a=x0<x1<...<xn=b,插值数据点的原函数为 f ( x ) f(x) f(x),则必有以下条件:

S ( x k ) = f ( x k ) , k = 0 , 1 , 2 , . . . , n S(x_k)=f(x_k),k=0,1,2,...,n S(xk)=f(xk),k=0,1,2,...,n

S ′ ( x k − ) = S ′ ( x k + ) , k = 1 , 2 , . . . , n − 1 S'(x_k^-)=S'(x_k^+),k=1,2,...,n-1 S(xk)=S(xk+),k=1,2,...,n1

S ′ ′ ( x k − ) = S ′ ′ ( x k + ) , k = 1 , 2 , . . . , n − 1 S''(x_k^-)=S''(x_k^+),k=1,2,...,n-1 S(xk)=S(xk+),k=1,2,...,n1

实际上,如果先将区间分段,共分为 n n n段,每段有 4 4 4个待定系数,需要 4 n 4n 4n个方程。而上面三个方程实际上是 4 n − 2 4n-2 4n2个方程。费解方程个数的话只需要注意到:第一个式子实际上是 S ( x k + ) = f ( x k ) , k = 0 , 1 , . . . , n − 1 S(x_k^+)=f(x_k),k=0,1,...,n-1 S(xk+)=f(xk),k=0,1,...,n1 S ( x k − ) = f ( x k ) , k = 1 , . . . , n S(x_k^-)=f(x_k),k=1,...,n S(xk)=f(xk),k=1,...,n。所以总方程数目是 2 n + ( n − 1 ) + ( n − 1 ) = 4 n − 2 2n+(n-1)+(n-1)=4n-2 2n+(n1)+(n1)=4n2

现在还缺少两个方程,因为上面后两个方程里, k k k不能取 0 0 0 n − 1 n-1 n1

想要确定插值函数,需要额外两个方程才行,这引出了边界条件。

第一类边界条件

给定两端一阶导数值:

S ′ ( x 0 + ) = f 0 ′ S'(x_0^+)=f_0' S(x0+)=f0

S ′ ( x n − ) = f n ′ S'(x_n^-)=f_n' S(xn)=fn

第二类边界条件

给定两端二阶导数值

S ′ ′ ( x 0 + ) = f 0 ′ ′ S''(x_0^+)=f_0'' S(x0+)=f0

S ′ ′ ( x n − ) = f n ′ ′ S''(x_n^-)=f_n'' S(xn)=fn

其中, f 0 ′ ′ = f n ′ ′ = 0 f_0''=f_n''=0 f0=fn=0称为自然边界条件。

第三类边界条件

周期边界条件,即 x n − x 0 x_n-x_0 xnx0是一个周期,那么就有:

S ( x 0 ) = S ( x n ) , S ′ ( x 0 + ) = S ′ ( x n − ) , S ′ ′ ( x 0 + ) = S ′ ′ ( x n − ) S(x_0)=S(x_n),S'(x_0^+)=S'(x_n^-),S''(x_0^+)=S''(x_n^-) S(x0)=S(xn),S(x0+)=S(xn),S(x0+)=S(xn)$

推导样条插值的方法

首先,由于 S ( x ) S(x) S(x)二阶连续可导,且是三次多项式,那么 S ′ ′ ( x ) S''(x) S(x)实际上是分段线性函数。可以假设每一段的斜率是 M k , k = 0 , 1 , . . . , n M_k,k=0,1,...,n Mk,k=0,1,...,n

在区间 [ x k , x k + 1 ] [x_k,x_{k+1}] [xk,xk+1]里,必有: S k ′ ′ ( x ) = x k + 1 − x x k + 1 − x k M k + x − x k x k + 1 − x k M k + 1 S_k''(x)=\dfrac{x_{k+1}-x}{x_{k+1}-x_k}M_k+\dfrac{x-x_k}{x_{k+1}-x_k}M_{k+1} Sk(x)=xk+1xkxk+1xMk+xk+1xkxxkMk+1

积分两次得: S k ( x ) = ( x k + 1 − x ) 3 6 ( x k + 1 − x k ) M k + ( x − x k ) 3 6 ( x k + 1 − x k ) M k + 1 + c 1 x + c 2 S_k(x)=\dfrac{{(x_{k+1}-x)}^3}{6(x_{k+1-x_k})}M_k+\dfrac{{(x-x_k)}^3}{6(x_{k+1-x_k})}M_{k+1}+c_1x+c_2 Sk(x)=6(xk+1xk)(xk+1x)3Mk+6(xk+1xk)(xxk)3Mk+1+c1x+c2

积分两次,得到两个待定常数 c 1 , c 2 c_1,c_2 c1,c2

这两个未知数只需要两个方程就可以确定,那么直接带入 S k ( x k ) = y k , S k ( x k + 1 ) = y k + 1 S_k(x_k)=y_k,S_{k}(x_{k+1})=y_{k+1} Sk(xk)=yk,Sk(xk+1)=yk+1就可以得出结果。

接着只需要确定所有的 M k M_k Mk就可以。

这里用Mathematica计算 x 0 , x 1 x_0,x_1 x0,x1上的结果,表达式还是相当复杂的。

{ { c1 → − − 1 6 M0 ( x1 − x0 ) 2 + 1 6 M1 ( x1 − x0 ) 2 + y0 − y1 x1 − x0 , c2 → − − M0 x0 2 x1 + 2 M0x0 x1 2 − M0 x1 3 + M1 x0 3 − 2 M1 x0 2 x1 + M1x0 x1 2 − 6 x0y1 + 6 x1y0 6 ( x0 − x1 ) } } \left\{\left\{\text{c1}\to -\dfrac{-\frac{1}{6} \text{M0} (\text{x1}-\text{x0})^2+\frac{1}{6} \text{M1} (\text{x1}-\text{x0})^2+\text{y0}-\text{y1}}{\text{x1}-\text{x0}},\\\text{c2}\to -\dfrac{-\text{M0} \text{x0}^2 \text{x1}+2 \text{M0} \text{x0} \text{x1}^2-\text{M0} \text{x1}^3+\text{M1} \text{x0}^3-2 \text{M1} \text{x0}^2 \text{x1}+\text{M1} \text{x0} \text{x1}^2-6 \text{x0} \text{y1}+6 \text{x1} \text{y0}}{6 (\text{x0}-\text{x1})}\right\}\right\} {{c1x1x061M0(x1x0)2+61M1(x1x0)2+y0y1,c26(x0x1)M0x02x1+2M0x0x12M0x13+M1x032M1x02x1+M1x0x126x0y1+6x1y0}}

不过课件里提供了一种化简,直接抄过来:

S k ( x ) = S k ( x ) = ( x k + 1 − x ) 3 6 ( x k + 1 − x k ) M k + ( x − x k ) 3 6 ( x k + 1 − x k ) M k + 1 + ( y k − M k ( x k + 1 − x k ) 2 6 x k + 1 − x x k + 1 − x k ) + ( y k + 1 − M k + 1 ( x k + 1 − x k ) 2 6 x − x k x k + 1 − x k ) S_k(x)=S_k(x)=\dfrac{{(x_{k+1}-x)}^3}{6(x_{k+1-x_k})}M_k+\dfrac{{(x-x_k)}^3}{6(x_{k+1-x_k})}M_{k+1}+(y_k-\dfrac{M_k{(x_{k+1}-x_k)}^2}{6}\dfrac{x_{k+1}-x}{x_{k+1}-x_k})+\\(y_{k+1}-\dfrac{M_{k+1}{(x_{k+1}-x_k)}^2}{6}\dfrac{x-x_k}{x_{k+1}-x_k}) Sk(x)=Sk(x)=6(xk+1xk)(xk+1x)3Mk+6(xk+1xk)(xxk)3Mk+1+(yk6Mk(xk+1xk)2xk+1xkxk+1x)+(yk+16Mk+1(xk+1xk)2xk+1xkxxk)

对这个表达式应用方程: S k − 1 ′ ( x k − ) = S k ′ ( x k + ) S_{k-1}'(x_k^-)=S_k'(x_k^+) Sk1(xk)=Sk(xk+)可得到方程:

μ k M k − 1 + 2 M k + λ k M k + 1 = d k \mu_kM_{k-1}+2M_k+\lambda_kM_{k+1}=d_k μkMk1+2Mk+λkMk+1=dk

其中: μ k = h k − 1 h k − 1 + h k , h k = x k + 1 − x k , λ k = h k h k − 1 + h k , d k = 6 f [ x k − 1 , x k , x k + 1 ] \mu_k=\dfrac{h_{k-1}}{h_{k-1}+h_k},h_k=x_{k+1}-x_k,\lambda_k=\dfrac{h_k}{h_{k-1}+h_k},\\d_k=6f[x_{k-1},x_k,x_{k+1}] μk=hk1+hkhk1,hk=xk+1xk,λk=hk1+hkhk,dk=6f[xk1,xk,xk+1]

这里有 n − 1 n-1 n1个方程,加上两个边界条件即可以确定所有的 M k M_k Mk

第一类边界条件解法

带入边界条件 S ′ ( x 0 + ) = f 0 ′ , S ′ ( x n − ) = f n ′ S'(x_0^+)=f_0',S'(x_n^-)=f_n' S(x0+)=f0,S(xn)=fn到原始的表达式里,可以得到, 2 M 0 + M 1 = d 0 , M n − 1 + 2 M n = d n 2M_0+M_1=d_0,M_{n-1}+2M_n=d_n 2M0+M1=d0,Mn1+2Mn=dn

注意这里的 d n , d 0 d_n,d_0 dn,d0需要从原始表达式里直接求得,不能用 d k = 6 f [ x k − 1 , x k , x k + 1 ] d_k=6f[x_{k-1},x_k,x_{k+1}] dk=6f[xk1,xk,xk+1]

由此,可以得到方程:

[ 2 1 μ 1 2 λ 1 μ 2 2 λ 2 ⋱ ⋱ ⋱ μ n − 1 2 λ n − 1 1 2 ] [ M 0 M 1 M 2 ⋮ M n − 1 M n ] = [ d 0 d 1 d 2 ⋮ d n − 1 d n ] \begin{bmatrix}2& 1& & &\\\mu_1&2&\lambda_1&&\\&\mu_2&2&\lambda_2&\\&&\ddots&\ddots&\ddots&\\&&&\mu_{n-1}&2&\lambda_{n-1}\\&&&&1&2\end{bmatrix}\begin{bmatrix}M_0\\M_1\\M_2\\\vdots\\M_{n-1}\\M_n\end{bmatrix}=\begin{bmatrix}d_0\\d_1\\d_2\\\vdots\\d_{n-1}\\d_n\end{bmatrix} 2μ112μ2λ12λ2μn121λn12M0M1M2Mn1Mn=d0d1d2dn1dn

第二类边界条件解法

第二类边界条件相当于直接给出了 M 0 , M n M_0,M_n M0,Mn,那便直接用边界条件带入 μ k M k − 1 + 2 M k + λ k M k + 1 = d k \mu_kM_{k-1}+2M_k+\lambda_kM_{k+1}=d_k μkMk1+2Mk+λkMk+1=dk确定 d 1 , d n − 1 d_1,d_{n-1} d1,dn1这两个方程,可以解得,第二类边界条件的方程为:

[ 2 λ 1 μ 2 2 λ 2 ⋱ ⋱ ⋱ μ n − 2 2 λ n − 2 μ n − 1 2 ] [ M 1 M 2 ⋮ M n − 2 M n − 1 ] = [ d 1 − μ 1 M 0 d 2 ⋮ d n − 2 d n − 1 − λ n − 1 M n ] \begin{bmatrix}2&\lambda_1&&\\\mu_2&2&\lambda_2&\\&\ddots&\ddots&\ddots&\\&&\mu_{n-2}&2&\lambda_{n-2}\\&&&\mu_{n-1}&2\end{bmatrix}\begin{bmatrix}M_1\\M_2\\\vdots\\M_{n-2}\\M_{n-1}\end{bmatrix}=\begin{bmatrix}d_1-\mu_1M_0\\d_2\\\vdots\\d_{n-2}\\d_{n-1}-\lambda_{n-1}M_n\end{bmatrix} 2μ2λ12λ2μn22μn1λn22M1M2Mn2Mn1=d1μ1M0d2dn2dn1λn1Mn

第三类边界条件解法

第三类边界条件,即周期性边界条件,可以理解为 0 , 1 , . . . , n − 1 , 0 / n , 1 0,1,...,n-1,0/n,1 0,1,...,n1,0/n,1构成了一个闭环,相当于指定了 M 0 = M n M_0=M_n M0=Mn。那么同样只需要看看 0 / n 0/n 0/n附近的方程形式就好。

选择 k = n k=n k=n,那么 μ n M n − 1 + 2 M n + λ n M n + 1 = d n \mu_nM_{n-1}+2M_{n}+\lambda_nM_{n+1}=d_n μnMn1+2Mn+λnMn+1=dn,可以将 M n + 1 M_{n+1} Mn+1视为 M 1 M_1 M1

选择 k = 1 k=1 k=1,那么 μ 1 M 0 + 2 M 1 + λ 1 M 2 = d 1 \mu_1M_{0}+2M_{1}+\lambda_1M_{2}=d_1 μ1M0+2M1+λ1M2=d1,可以将 M 0 M_0 M0视为 M n M_n Mn

那么方程为:

[ 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 ] \begin{bmatrix}2&\lambda_1&&&\mu_1\\\mu_2&2&\lambda_2&\\&\ddots&\ddots&\ddots&\\&&\mu_{n-1}&2&\lambda_{n-1}\\\lambda_n&&&\mu_{n}&2\end{bmatrix}\begin{bmatrix}M_1\\M_2\\\vdots\\M_{n-1}\\M_{n}\end{bmatrix}=\begin{bmatrix}d_1\\d_2\\\vdots\\d_{n-1}\\d_{n}\end{bmatrix} 2μ2λnλ12λ2μn12μnμ1λn12M1M2Mn1Mn=d1d2dn1dn

解这三类边界条件的方程叫做三弯矩方程,它们都有唯一解。

三次样条插值示例

对函数 f ( x ) = 1 1 + x 2 f(x)=\dfrac{1}{1+x^2} f(x)=1+x21进行插值,区间 [ − 5 , 5 ] [-5,5] [5,5],取11个等距节点,比较三次样条插值和10次拉格朗日插值的函数。

为了方便,直接导入了前面博客里的拉格朗日插值的程序。https://blog.csdn.net/lan_777/article/details/111146113

import numpy as np
import matplotlib.pyplot as plt

# 对函数1/(1+x^2)插值,区域[-5,5],取11个数据点
# 使用第二类边界条件来解
n = 11 - 1
xdata = np.linspace(-5, 5, n + 1)
ydata = 1 / (1 + xdata ** 2)
M0 = 37 / 4396
Mn = 37 / 4396

# 计算三弯矩方程所需的系数矩阵
# hk的k从0到n-1
hk = np.diff(xdata)
# uk=h_{k-1}/(h_{k-1}+h_k),k从1到n-1
uk = hk[1:] / (hk[1:] + hk[:-1])
lambdak = hk[:-1] / (hk[1:] + hk[:-1])
# 计算dk,k同样从1到n-1
dk = 6 * np.diff(np.diff(ydata) / np.diff(xdata)) / np.diff(xdata)[:-1]

# 计算M
# 创建系数矩阵
factor_mat = 2 * np.eye(n - 1) + np.diag(lambdak[:-1], k=1) + np.diag(uk[1:], k=-1)
dk[0] = dk[0] - uk[0] * M0
dk[-1] = dk[-1] - lambdak[-1] * Mn
# 计算二阶导数
Mk = np.linalg.inv(factor_mat) @ dk
# print(type(Mk))
Mk = np.insert(Mk, 0, M0)
# print((Mk))
Mk = np.insert(Mk, len(Mk), Mn)


# print((Mk))


# 分段求函数

def sx(mk, hk, xdata, ydata, plot_x):
    if np.max(plot_x) > np.max(xdata) or np.min(plot_x) < np.min(xdata):
        print("输出范围需要在节点内。")
        return False
    plot_y = np.array([])
    for i in range(n):
        # print((plot_x < xdata[i+1]) & (plot_x >= xdata[i]))
        segment_x = plot_x[((plot_x < xdata[i + 1]) & (plot_x >= xdata[i]))]
        if i == n-1:
            segment_x = plot_x[((plot_x <= xdata[i + 1]) & (plot_x >= xdata[i]))]
        segment_y = (mk[i + 1] - mk[i]) / (6 * hk[i]) * (segment_x - xdata[i]) ** 3 + \
                    mk[i] / 2 * (segment_x - xdata[i]) ** 2 + \
                    ((ydata[i + 1] - ydata[i]) / hk[i] - (hk[i] * (mk[i + 1] + 2 * mk[i])) / 6) * \
                    (segment_x - xdata[i]) + ydata[i]
        plot_y = np.insert(plot_y, len(plot_y), segment_y)
    return plot_y

# 导入之前的拉格朗日插值程序,在下面直接绘制
import lagrange

plot_x = np.linspace(-5, 5, 400)
plot_y = sx(Mk, hk, xdata, ydata, plot_x)
# print(plot_y)
p1, = plt.plot(plot_x, plot_y)
p2, = plt.plot(plot_x, 1 / (1 + plot_x ** 2))
p4, = plt.plot(lagrange.x_plot,lagrange.l_matrix @ lagrange.ydata)
p3 = plt.scatter(xdata, ydata)
plt.legend([p1, p2, p4], ["Spline", "f(x)", "Lagrange"])
plt.show()

运行结果:
在这里插入图片描述
这里可以看出,三次样条插值的结果要明显优秀于高次拉格朗日插值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值