1. 函数插值
函数插值问题: 用形式简单的插值函数
f^(x) 近似原函数
(1) 设函数
y=f(x) 在某个区间上有定义,并且已知该区间上的一些数据点
{xi,yi} 严格满足
yi=f(xi),i=1,⋯,N,这些数据点称为“控制节点”或“插值节点”
(2) 如果存在一个形式上比较简单(比如
n 次多项式)的函数
f^(x),使得
f^(xi)=yi,i=1,⋯,N 都成立,就称
f^(x) 为
f(x) 的插值函数。
典型的函数插值方法:拉格朗日插值、牛顿插值、
Hermite插值、样条插值等。
与“函数逼近”的主要区别:
插值函数
f^(x) 必须经过“插值节点”,也就是要满足
f^(xi)=yi,i=1,⋯,N
2. RBF函数插值
与拉格朗日插值之类的常规函数插值不同,基于核函数的函数插值“通过引入核函数”来刻画数据的局部化特征。
径向基函数
(Radial Basis Function,RBF) 就是一类特殊的基函数,最常用的就是“高斯基函数”,定义为:
φ(x)=e−2σ2x2 (以一维情况为例)
RBF函数插值:
f^(x)=i=1∑Nwiφ(∥x−xi∥)
假设有
N 个插值节点,也就是已知
{xj,yj}∣∣j=1N,其中
f^(xj)=yj=f(xj),如下图所示。
图中,红色实线为真实函数曲线,绿色空心圆圈代表插值节点
(xj,yj),蓝色实心点为RBF插值所求得的权值
wj
将
{xj,yj}∣∣j=1N 带入方程
f^(x)=i=1∑Nwiφ(∥x−xi∥),可得到:
Φ
⎣⎢⎢⎢⎡φ11φ21⋮φ11φ12φ22⋮φ12⋯⋯⋯φ1Nφ2N⋮φ1N⎦⎥⎥⎥⎤W
⎣⎢⎢⎢⎡w1w2⋮wN⎦⎥⎥⎥⎤=y
⎣⎢⎢⎢⎡y1y2⋮yN⎦⎥⎥⎥⎤,其中
φji=φ(∥xj−xi∥)
其中,
Φ=[φji] 为插值矩阵。因为
φji=φ(∥xj−xi∥)=φij,因此插值矩阵是对称的。对于高斯核函数而言,插值矩阵的对角线元素的值为
1。
将线性方程组记为
ΦW=y,该方程组的第
j 行为:
f^(xj)=yj=w1φ(∥xj−x1∥)+w2φ(∥xj−x2∥)+⋯+wNφ(∥xj−xN∥)
因此,可求出
RBF 插值的系数为:
W=Φ−1y,其示意图如下图所示。
Micchelli定理可以保证采用高斯函数时,插值矩阵
Φ 是可逆的(只要插值节点互不相同)。
代码实现
import numpy as np
import matplotlib.pyplot as plt
def gen_data(x1,x2):
y_sample = np.sin(np.pi*x1/2)+np.cos(np.pi*x1/3)
y_all = np.sin(np.pi*x2/2)+np.cos(np.pi*x2/3)
return y_sample, y_all
def kernel_interpolation(y_sample,x1,sig):
gaussian_kernel = lambda x,c,h: np.exp(-(x-x[c])**2/(2*(h**2)))
num = len(y_sample)
w = np.zeros(num)
int_matrix = np.asmatrix(np.zeros((num,num)))
for i in range(num):
int_matrix[i,:] = gaussian_kernel(x1,i,sig)
w = int_matrix.I * np.asmatrix(y_sample).T
return w
def kernel_interpolation_rec(w,x1,x2,sig):
gkernel = lambda x,xc,h: np.exp(-(x-xc)**2/(2*(h**2)))
num = len(x2)
y_rec = np.zeros(num)
for i in range(num):
for k in range(len(w)):
y_rec[i] = y_rec[i] + w[k]*gkernel(x2[i],x1[k],sig)
return y_rec
if __name__ == '__main__':
snum = 20
ratio = 20
sig = 1
xs = -8
xe = 8
x1 = np.linspace(xs,xe,snum)
x2 = np.linspace(xs,xe,(snum-1)*ratio+1)
y_sample, y_all = gen_data(x1,x2)
plt.figure(1)
w = kernel_interpolation(y_sample,x1,sig)
y_rec = kernel_interpolation_rec(w,x1,x2,sig)
plt.plot(x2,y_rec,'k')
plt.plot(x2,y_all,'r:')
plt.ylabel('y')
plt.xlabel('x')
for i in range(len(x1)):
plt.plot(x1[i],y_sample[i],'go',markerfacecolor='none')
plt.legend(labels=['reconstruction','original','control point'],loc='lower left')
plt.title('kernel interpolation:$y=sin(\pi x/2)+cos(\pi x/3)$')
plt.show()
运行结果:
在相同区间、分别采用
8,12,16,20 个控制节点
(control point) 进行函数插值的结果
显然,插值节点过少,无法体现整个函数的特征;插值节点越多,函数插值的结果越精确
扩大插值区间范围,控制节点
(control point) 也需要增加数量,才能保持函数插值的准确性