Python数值计算(23)——modified akima插值

1. 数学原理

在前面的Akima插值中,计算斜率使用如下公式:

t_i=\frac{|m_{i+1}-m_i|m_{i-1}+|m_{i-1}-m_{i-2}|m_i}{|m_{i+1}-m_i|+|m_{i-1}-m_{i-2}|},i=3,4

如果记:

w_1=\frac{|m_{i+1}-m_i|}{|m_{i+1}-m_i|+|m_{i-1}-m_{i-2}|}

w_2=\frac{|m_{i-1}-m_{i-2}|}{|m_{i+1}-m_i|+|m_{i-1}-m_{i-2}|}

在出现分母分子同时为零的情况时,会出现NaN的计算结果,Akima他自己也意识到这种问题,因此,在原来的算法上做了修订,也就是Modified Akima算法,有时也被简写为makima算法。修订如下:

w_1=|m_{i+1}-m_i|+|m_{i+1}+m_i|/2

w_2=|m_{i+-1}-m_{i-2}|+|m_{i-1}+m_{i-2}|/2

这样的话,就解决了在出现超过2个点具有连续值时的无法被零除问题,此时t_i=0

2. 算法实现

修改原来的算法如下:

import numpy as np
from numpy.polynomial import Polynomial
import matplotlib.pyplot as plt

class makimaIntp:
    __coef:np.ndarray
    __bpx:np.ndarray
    __bpy:np.ndarray
    def __init__(self,x:np.ndarray,y:np.ndarray):
     n,=x.shape
     m=np.zeros(n+3)
     m[2:-2]=np.diff(y)/np.diff(x)
     m[1]=2*m[2]-m[3]
     m[0]=2*m[1]-m[2]
     m[-2]=2*m[-3]-m[-4]
     m[-1]=2*m[-2]-m[-3]
     d=np.zeros(n)
     for i in range(n):
        # 注意此处修改
        w1=np.abs(m[i+3]-m[i+2])+np.abs(m[i+3]+m[i+2])/2
        w2=np.abs(m[i+1]-m[i])+np.abs(m[i+1]+m[i])/2
        if w1==0 and w2==0:
            d[i]=(m[i+1]+m[i+2])/2
        else:
            d[i]=w1/(w1+w2)*m[i+1]+w2/(w1+w2)*m[i+2]

     self.__coef=np.zeros((n-1,4))
     self.__coef[:,0]=y[0:-1]
     self.__coef[:,1]=d[0:-1]
     for i in range(n-1):
       self.__coef[i,2]=(3*(y[i+1]-y[i])/(x[i+1]-x[i])-2*d[i]-d[i+1])/(x[i+1]-x[i])
       self.__coef[i,3]=(d[i+1]+d[i]-2*(y[i+1]-y[i])/(x[i+1]-x[i]))/(x[i+1]-x[i])/(x[i+1]-x[i])
     self.__bpx=x.copy()
     self.__bpy=y.copy()

    def __call__(self,w:np.ndarray):
     n,=w.shape
     ret= np.zeros(n)
     for i in range(n):
       if self.__bpx[0]>=w[i]:
        ret[i]=self.__bpy[0]
        continue
       if self.__bpx[-1]<=w[i]:
        ret[i]=self.__bpy[-1]
        continue
       j=0
       while self.__bpx[j]<w[i]:
        j+=1
       cp=self.__coef[j-1,:]
       p=Polynomial(0)
       for t in range(len(cp)):
        p+=cp[t]*Polynomial([-self.__bpx[j-1],1])**t
       ret[i]=p(w[i])
     return ret

    @property
    def c(self):
     return self.__coef
    @property
    def x(self):
     return self.__bpx

3. 算法测试

依旧以HIROSHI AKIMA当初论文中的一组数据为例,测试如下:

np.set_printoptions(precision=4,linewidth=100)
# test data
x=np.array([0,1,2,3,4,5,6,7,8,9,10])
y=np.array([10,10,10,10,10,10,10.5,15,50,60,85])

aip=makimaIntp(x,y)
print(aip.c)
'''
[[ 10.       0.       0.       0.    ]
 [ 10.       0.       0.       0.    ]
 [ 10.       0.       0.       0.    ]
 [ 10.       0.       0.       0.    ]
 [ 10.       0.       0.       0.    ]
 [ 10.       0.       0.9412  -0.4412]
 [ 10.5      0.5588   4.2111  -0.2699]
 [ 15.       8.1713  68.8387 -42.01  ]
 [ 50.      19.8187 -27.1375  17.3187]
 [ 60.      17.5      9.8684  -2.3684]]
'''

同样的,每一行就是每个分段中的系数p_0,p_1,p_2,p_3

4. 现成算法

Scipy中也有现成的工具包供使用,scipy.interpolate中的Akima1DInterpolator类,可完成该项工作,只需要指定参数method='makima'即可。

akima=Akima1DInterpolator(x,y,method='makima')
print(akima.c)
'''
[[  0.    0.    0.    0.    0.    -0.4412   -0.2699     -42.01      17.3187     -2.3684] 
 [  0.    0.    0.    0.    0.    0.9412    4.2111      68.8387     -27.1375    9.8684] 
 [  0.    0.    0.    0.    0.    0.        0.5588      8.1713      19.8187     17.5   ] 
 [ 10.    10.   10.   10.   10.   10.       10.5        15.         50.         60.    ]]
'''

同样需要注意其系数的组织方式。

最后,对比两者拟合的图形,将其绘制在同一个图表中:

X=np.linspace(x[0],x[-1],100)
Y1=aip(X)
plt.plot(X,Y1,'b-.')
Y2=akima(X)
plt.plot(X,Y2,'r--')
plt.grid()
plt.show()

效果如下:

其中蓝色点画线是我们自己实现的算法,红色虚线是软件包提供的算法,可见两者吻合的非常好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值