1、理论知识
1)三次样条插值的提法
给定 y = 的函数表( 如表 所示) ,构造函数 满足条件:
①在 上为不超过三次的代数多项式;
② ;
③,其中.
其中称为三次样条插值函数.
x | ||||
y = |
设 在点 处的微商为,则 在每个小
区间上有
2)三次样条插值的解法思路
1)
是不超过3次的插值函数,多段的 组成了
所以总共有n-1个分段,也就是说要求解的参数有 个
由表的信息,我们可以列出:
条件 | 方程个数 |
函数值相等 | n |
函数值连续 | n-2 |
一阶导数连续 | n-2 |
二阶导数连续 | n-2 |
由此还需补充两个边界条件才能凑足4(n-1)个方程去求解4(n-1)个未知数
2)边界条件
而边界条件一般有如下两种情形:
(1) 已 知 被 插 值 函 数 在 两 个 端 点和处 的 微 商 值, 即 给 定
和 ,相应要求 和
(2) 已知被插值函数 在两个端点 x0 和 xn 处的二阶微商值为 0,即给
定 和 ,相应要求 和
3)大致的推导流程
1)先根据“函数值相等”、“函数值连续”,“一阶导连续”,设:
【参考hermite插值】
2)目标——“求解 的表达式"
① 化简 ,代入(2.41)
② 求二阶导
2.4.2直接表现的是 中的二阶导,从中可以获取
③ 将2.4.2中的i替换为i-1,得到的是 中的二阶导,从中可以获取
④ 令 整理得到
⑤ 令,上式变化为
⑥ 令
得到
⑦ 分别令 可以得到n-1个等式组成的方程组
⑧ 补充条件后,有公式
分别整理得到:
2、编程计算
1)矩阵构造
根据大致推导流程,我们需要编写函数计算如下式子:
def calhi(X):
H = []
# len(X) = n+1
for i in range(len(X)-1):
H.append(X[i+1]-X[i])
return H
def calalphai(H):
# len(H) n 0——n-1
# 只当 A0 = 0
A = [0]
for i in range(1,len(H)):
A.append((H[i-1]/(H[i-1]+H[i])))
return A
def calbetai(A,H,Y):
# len(A) = n 实际有效的alpha在[1:]范围内
# Y n+1 0——n
B = [0]
for i in range(1,len(A)):
tmp1 = (1-A[i])/H[i-1]*(Y[i]-Y[i-1])
tmp2 = A[i]/H[i]*(Y[i+1]-Y[i])
B.append(3*(tmp1+tmp2))
return B
根据以下式子,编写函数构造矩阵
# 构造相应的矩阵
# 先构造补充条件的list
import numpy as np
def RowForSupplement(n):
RowsFS = np.zeros(shape=(2,n+1))
RowsFS[0][0] = 2
RowsFS[0][1] = 1
RowsFS[1][n-1] = 1
RowsFS[1][n] = 2
return RowsFS
# 构造原先的n-1个条件的方程
def Row(A):
n = len(A)
Rows = np.zeros(shape=(n-1,n+1))
for i in range(1,n):
Rows[i-1][i-1] = 1-A[i]
Rows[i-1][i] = 2
Rows[i-1][i+1] = A[i]
return Rows
# 合并左侧矩阵
def Concat(Rows,RowsFS,n):
C = np.zeros(shape=(n+1,n+1))
C[:n-1,:] = Rows
C[n-1:,:] = RowsFS
return C
# 构造β矩阵
def Beta(B,Y,H):
n = len(Y)-1
l1 = 3*(Y[1]-Y[0])/H[0]
l2 = 3*(Y[n]-Y[n-1])/H[n-1]
Beta = np.zeros(shape=(n+1,))
for i in range(1,n):
Beta[i-1] = B[i]
Beta[n-1] = l1
Beta[n] = l2
return Beta
# 解线性方程
def solve(X,Y):
H = calhi(X)
A = calalphai(H)
B = calbetai(A,H,Y)
RowsFS = RowForSupplement(len(Y)-1)
Rows = Row(A)
Left = Concat(Rows,RowsFS,len(Y)-1)
beta = Beta(B,Y,H)
M = np.linalg.solve(Left,beta)
print(M)
return M
2)预测函数
到solve为止,只是解出了,我们还可以编写预测函数,直接获取对于x插值函数s(x)的预测值
# 预测代码
def predict(targetX,X,Y):
M = solve(X,Y)
n = len(Y)
# 确定targetX所在的区间
st = 0
for i in range(n-1):
if targetX >= X[i] and targetX <= X[i+1]:
st = i
break
tmp1 = (1+2*(targetX-X[st])/(X[st+1]-X[st]))*pow(((targetX-X[st+1])/(X[st]-X[st+1])),2)
tmp2 = (1+2*(targetX-X[st+1])/(X[st]-X[st+1]))*pow(((targetX-X[st])/(X[st+1]-X[st])),2)
tmp3 = (targetX-X[st])*pow(((targetX-X[st+1])/(X[st]-X[st+1])),2)
tmp4 = (targetX-X[st+1])*pow(((targetX-X[st])/(X[st+1]-X[st])),2)
return tmp1*Y[st]+tmp2*Y[st+1]+tmp3*M[st]+tmp4*M[st+1]
3)验证
1)输入了一个例题的参数,结果预测是正确的
2)对比内置的库函数CubicSpline:
自己的函数:
内置库函数: