三次样条函数(cubic spline functions)的插值求解(python,数值积分)

第三十七篇 三次样条函数的插值求解

利用三次样条函数进行插值

到目前为止,描述的两种插值方法拉格朗日多项式和正向差分会形成高阶的多项式,但一般情况下,选择阶数的值等于小于数据点的数目更合适。除了考虑计算的复杂程度外,高阶多项式还会导致给定数据点之间出现不需要的极大值和极小值。所以另一种类型的插值方法以“分段”的方式从点到点拟合低阶多项式。多项式的阶数可以由我们自己决定。如果多项式被选择为线性,则数据点只是由直线连接,在每个直线相互连接的点上都有“角”。在工程应用中,如果有许多数据点紧密地聚集在一起,那么线性函数的不连续性质可能不是问题。
最常用的分段多项式方法是在相邻点之间拟合三次函数,这种方法能够保持结点处的二阶导数连续性。这种三次多项式通常被称为“样条函数”,因为插值函数可以被视为一个柔性弹性梁(或样条),最初是直的,通过所需的点(xi, yi), i = 0,1,…,产生变形。
一般来说,如果有np点,则需要n个三次样条函数(其中n = np−1),可以写成这样的形式
在这里插入图片描述
4n个未知系数Aji可以由以下4n个条件确定:

  1. 三次方必须在保证相同点的数据一致性,从而得到2n方程
    在这里插入图片描述
  2. 一阶导数必须在所有内点上是连续的,从而得到n - 1方程。
    . 在这里插入图片描述
    3 二阶导数也必须在所有内部点上连续,导致进一步的n - 1方程。
    在这里插入图片描述
  3. 最后两个条件指的是样条的两端,其中二阶导数被设为零,因此
    在这里插入图片描述
    .最终边界条件类比“结构”的形式,表明了“梁”两端的弯矩为零。
    虽然4n个未知数的4n个方程可以求解得到所需的系数,但结合了条件1和3。可以得到的原三次函数的拓展形式为
    三次样条函数(cubic spline functions)的插值求解(python,数值积分)_第1张图片
    式中I = 1,2,…,n, xi−1≤x≤xi, Δxi−1 = xi−xi−1,这与之前使用的“正向差法”表示法相同。
    上面方程的微分和条件2关于一阶导数连续性的使用,得到
    在这里插入图片描述
    式中I = 1,2,…,n−1,Δyi−1 = yi−yi−1。
    它等价于n - 1个线性方程组在已知点的二阶导数未知,如下所示
    三次样条函数(cubic spline functions)的插值求解(python,数值积分)_第2张图片
    系数矩阵是对称的三对角的。一旦二阶导数f(xi), i = 1,2,…,n−1被计算出来,可以结合边界条件f(x0) = f(xn) = 0,求出方程中的所有三次函数。
    为了得到对应于特定值x的y的估计值,首先必须通过观察x相对于原始数据点xi, i = 0,1,…,n的位置来确定使用哪个三次函数。一旦找到使用哪个函数,f‘’(xi)和f‘’(xi−1)的计算值可以代入方程,计算所需点的函数值。

计算实例

给与下面四个点,使用三次样条函数去计算x=1.3时的y值
三次样条函数(cubic spline functions)的插值求解(python,数值积分)_第3张图片
在这个例子中,将有三个三次样条分布在四个坐标上。我们假设在x = 0.0和x = 2.3处的二阶导数为零,因此在x = 1.0和x = 1.5处还有两个二阶导数。
从上面方程可以写成下面的形式
在这里插入图片描述
需要的正向差分项求出在下表中
三次样条函数(cubic spline functions)的插值求解(python,数值积分)_第4张图片
替换之后,得到方程为
在这里插入图片描述
很容易解出得到
在这里插入图片描述
这样,全部的二阶导就都知道了,分别为f‘’(0) = 0, f’’(1) = −3.2364, f’’(1.5) = 6.2185 and f’’(2.3) = 0,
因此从上面的式子可以求得三次样条函数,展示在下图中
三次样条函数(cubic spline functions)的插值求解(python,数值积分)_第5张图片
在x = 1.3处进行插值,它位于三次样条函数f2(x)的1.0到1.5范围之间,设置i=2,由上面的方程得到
三次样条函数(cubic spline functions)的插值求解(python,数值积分)_第6张图片
因此
三次样条函数(cubic spline functions)的插值求解(python,数值积分)_第7张图片
得到
在这里插入图片描述

程序如下

其中有一个主程序,和两个子程序,分别为天际线矩阵的乔列斯基分解子程序sparin,和一个逆向迭代求解的子程序spabac。详情可见以天际线存储矩阵的乔列斯基分解

#三次样条函数的插值求解
import B
import numpy as np1
np=5;xi=6.5
diffx=np1.zeros((np-1),dtype=np1.float)
diffy=np1.zeros((np-1),dtype=np1.float)
kdiag=np1.zeros((np-2),dtype=np1.int64)
kv=np1.zeros((2*(np-2)-1))
rhs=np1.zeros((np),dtype=np1.float)
x=np1.zeros((np))
y=np1.zeros((np))
x[0:np]=(0.0,2.5,5.0,8.0,10.0)
y[0:np]=(0.0,-0.004538,-0.005,0.0,0.004352)
print('三次样条函数的插值求解')
print('数据点','  x    y')
for i in range(1,np+1):
    print('{:13.4e}'.format(x[i-1]),end='')
    print('{:13.4e}'.format(y[i-1]))
for i in range(1,np):
    diffx[i-1]=x[i]-x[i-1]
    diffy[i-1]=y[i]-y[i-1]
for i in range(1,np-1):
    kdiag[i-1]=2*i-1
    kv[kdiag[i-1]-1]=2.0*(diffx[i-1]+diffx[i])
    rhs[i-1]=6.0*(diffy[i]/diffx[i]-diffy[i-1]/diffx[i-1])
for i in range(1,np-2):
    kv[2*i-1]=diffx[i]
B.sparin(kv,kdiag)
B.spabac(kv,rhs,kdiag)
print('插值点','   x   y')
for i in range(1,np):
    if xi<x[i]:
        yi=(rhs[i-2]*(x[i]-xi)**3+rhs[i-1]*(xi-x[i-1])**3)/(6.0*diffx[i-1])+(y[i-1]/diffx[i-1]-\
            rhs[i-2]*diffx[i-1]/6.0)*(x[i]-xi)+(y[i]/diffx[i-1]-rhs[i-1]*diffx[i-1]/6.0)*(xi-x[i-1])
        break
print('{:13.4e}'.format(xi),end='')
print('{:13.4e}'.format(yi))
sparin
def sparin(kv,kdiag):
#对称天际线矩阵的乔列斯基分解
  n=kdiag.shape[0] 
  kv[0]=kv[0]**0.5
  for i in range(2,n+1):
    ki=kdiag[i-1]-i
    l=kdiag[i-2]-ki+1
    for j in range(int(l),i+1):
      x=np.float64(kv[ki+j-1])
      kj=kdiag[j-1]-j
      if j!=1:
        ll=kdiag[j-2]-kj+1
        ll=max(l,ll)
        if ll!=j:
          m=j-1
          for k in range(int(ll),m+1):
            x=x-np.float64(kv[ki+k-1]*kv[kj+k-1])
      kv[ki+j-1]=x/kv[kj+j-1]
    kv[ki+i-1]=x**0.5
spabac
def spabac(kv,loads,kdiag):
#天际线矩阵的乔列斯基前后迭代
  n=kdiag.shape[0]
  loads[0]=loads[0]/kv[0]
  for i in range(2,n+1):
    ki=kdiag[i-1]-i
    l=kdiag[i-2]-ki+1 
    x=loads[i-1]
    if l!=i:
      m=i-1
      for j in range(int(l),m+1): 
        x=x-kv[ki+j-1]*loads[j-1]
    loads[i-1]=x/kv[ki+i-1]
  for it in range(2,n+1):
    i=n+2-it
    ki=kdiag[i-1]-i
    x=loads[i-1]/kv[ki+i-1]
    loads[i-1]=x
    l=kdiag[i-2]-ki+1
    if l!=i:
      m=i-1
      for k in range(int(l),int(m+1)):
        loads[k-1]=loads[k-1]-x*kv[ki+k-1]
  loads[0]=loads[0]/kv[0]

终端输出结果如下
三次样条函数(cubic spline functions)的插值求解(python,数值积分)_第8张图片

你可能感兴趣的:(有限元,数值分析,python,线性代数)