牛顿插值是两大多项式插值法interpolation的一种。所谓的多项式插值,直白了讲,就是从一堆 x , y x,y x,y的数据对中,回归出一个多项式函数,这样就可以预测不在数据对中的x的取值。我举个例子:
x 0 = 0 , y 0 = 1 x 1 = 2 , y 1 = 5 x 2 = 4 , y 2 = 17 x_0=0,y_0=1\\ x_1=2,y_1=5\\ x_2=4,y_2=17 x0=0,y0=1x1=2,y1=5x2=4,y2=17
我们经过多项式插值,从这三对数据中推导出这三对数据符合如下方程:
y = x 2 + 1 y=x^2+1 y=x2+1
于是我们就可以预测x等于其他值的时候,y等于多少了。之所以叫插值,是要求x在数据集的范围内,比如上述数据x的范围是 0 ≤ x ≤ 4 0\le x\le 4 0≤x≤4,那么我们只能预测x在这个范围内y的取值了。范围外的取值就是外推法extrapolation研究的内容了。
牛顿插值分为两种,等距与不等距。等距是特殊情况,不等距才是一般情况。
牛顿插值的一般公式如下:
P n ( x ) = ∑ j = 0 n α j e j ( x ) = α ( 0 ) + α 1 ( x − x 0 ) + α 2 ( x − x 0 ) ( x − x 1 ) + ⋯ + α n ( x − x 0 ) ( x − x 1 ) … ( x − x n − 1 ) P_n(x)=\sum_{j=0}^n \alpha_je_j(x)=\alpha(0)+\alpha_1(x-x_0)+\alpha_2(x-x_0)(x-x_1)\\ +\dots+\alpha_n(x-x_0)(x-x_1)\dots(x-x_{n-1}) Pn(x)=j=0∑nαjej(x)=α(0)+α1(x−x0)+α2(x−x0)(x−x1)+⋯+αn(x−x0)(x−x1)…(x−xn−1)
这里的e比较容易得到:
e 0 = 1 e 1 = x − x 0 e 2 = ( x − x 0 ) ( x − x 1 ) e 3 = ( x − x 0 ) ( x − x 1 ) ( x − x 2 ) ⋮ e n = ( x − x 0 ) ( x − x 1 ) … ( x − x n − 1 ) e_0=1\\ e_1=x-x_0\\ e_2=(x-x_0)(x-x_1)\\ e_3=(x-x_0)(x-x_1)(x-x_2)\\ \vdots\\ e_n=(x-x_0)(x-x_1)\dots(x-x_{n-1})\\ e0=1e1=x−x0e2=(x−x0)(x−x1)e3=(x−x0)(x−x1)(x−x2)⋮en=(x−x0)(x−x1)…(x−xn−1)
而难的是α,而α,是根据差商得到的。
α k = f [ x 1 , … , x k ] − f [ x 1 , … , x k − 1 ] x k − x 0 = f [ x 0 , … , x k ] \alpha_k=\frac{f[x_1,\dots,x_k]-f[x_1,\dots,x_{k-1}]}{x_k-x_0}=f[x_0,\dots,x_k] αk=xk−x0f[x1,…,xk]−f[x1,…,xk−1]=f[x0,…,xk]
这是个递归公式:
α 0 = y 0 α 1 = f [ x 1 ] − f [ x 0 ] x 1 − x 0 = f [ x 0 , x 1 ] α k = f [ x 1 , … , x k ] − f [ x 0 , … , x k − 1 ] x k − x 0 = f [ x 0 , … , x k ] f [ i , j ] = f [ j ] − f [ i ] x j − x i α_0=y_0\\ \alpha_1=\frac{f[x_1]-f[x_0]}{x_1-x_0}=f[x_0,x_1]\\ \alpha_k=\frac{f[x_1,\dots,x_k]-f[x_0,\dots,x_{k-1}]}{x_k-x_0}=f[x_0,\dots,x_k]\\ f[i,j]=\frac{f[j]-f[i]}{x_j-x_i} α0=y0α1=x1−x0f[x1]−f[x0]=f[x0,x1]αk=xk−x0f[x1,…,xk]−f[x0,…,xk−1]=f[x0,…,xk]f[i,j]=xj−xif[j]−f[i]
而递归就可以用动态规范的思想,缓存已有的数据来实现。
所以基本算法是
1 计算出e的多项式数组
2 建立一个二维数组,缓存差商,然后全部计算出来
3 将整个多项式建立起来,并且化简
对于e这个多项式数组,n个数据,数组长度就是n。最后一项是n-1次幂。
但是为了对齐,要将其变成同样长度的数组,所以整体是一个n*n的矩阵。
建立差商二维数组时,可以按这样的规则
f [ i j ] = f [ i ] [ j − i ] f[i~j] = f[i][j-i] f[i j]=f[i][j−i]
例如 f [ 2 , 3 , 4 , 5 ] = f [ 2 ] [ 3 ] f[2,3,4,5]=f[2][3] f[2,3,4,5]=f[2][3],所以 f [ 0 ] [ k ] = f [ 1 ] [ k − 1 ] − f [ 0 ] [ k − 1 ] / ( x k − x 0 ) f[0][k]=f[1][k-1]-f[0][k-1]/(x_k-x_0) f[0][k]=f[1][k−1]−f[0][k−1]/(xk−x0)。
在循环时,我们可以利用递归公式设置这个二维数组的值
比如取到 x 3 , y 3 x_3,y_3 x3,y3时,可以设置 f [ 3 ] [ 0 ] = y 3 f[3][0]=y_3 f[3][0]=y3。
那同时 f [ 0 ] [ 3 ] , f [ 1 ] [ 2 ] , f [ 2 ] [ 1 ] f[0][3],f[1][2],f[2][1] f[0][3],f[1][2],f[2][1]的值也可以计算出来。
x 0 → f [ x 0 ] x 1 → f [ x 1 ] , f [ x 0 , x 1 ] x 2 → f [ x 2 ] , f [ x 1 , x 2 ] , f [ x 0 , x 1 , x 2 ] x 3 → f [ x 3 ] , f [ x 2 , x 3 ] , f [ x 1 , x 2 , x 3 ] , f [ x 0 , x 1 , x 2 , x 3 ] x_0 \to f [ x_0 ] \\ x_1 \to f [ x_1] ,f [ x_0 , x_1] \\ x_2\to f [ x_2] , f [ x_1 , x_2 ] , f [x_0 , x_1 , x_2] \\ x_3 \to f [ x_3 ] ,f [ x_2 , x_3 ] , f [x_1 , x_2 , x_3 ] , f [x_0 , x_1 , x_2 , x_3 ] x0→f[x0]x1→f[x1],f[x0,x1]x2→f[x2],f[x1,x2],f[x0,x1,x2]x3→f[x3],f[x2,x3],f[x1,x2,x3],f[x0,x1,x2,x3]
所以:
第一次循环计算出了 f [ 0 ] [ 0 ] f[0][0] f[0][0];
第二次循环计算出了 f [ 1 ] [ 0 ] , f [ 0 ] [ 1 ] f[1][0] ,f[0][1] f[1][0],f[0][1];
第三次循环计算出了 f [ 2 ] [ 0 ] , f [ 1 ] [ 1 ] , f [ 0 ] [ 2 ] f[2][0] ,f[1][1] ,f[0][2] f[2][0],f[1][1],f[0][2];
第四次循环计算出了 f [ 3 ] [ 0 ] , f [ 2 ] [ 1 ] , f [ 1 ] [ 2 ] , f [ 0 ] [ 3 ] f[3][0] ,f[2][1] ,f[1][2] ,f[0][3] f[3][0],f[2][1],f[1][2],f[0][3];
第五次循环计算出了 f [ 4 ] [ 0 ] , f [ 3 ] [ 1 ] , f [ 2 ] [ 2 ] , f [ 1 ] [ 3 ] , f [ 0 ] [ 4 ] f[4][0] ,f[3][1] ,f[2][2] ,f[1][3] ,f[0][4] f[4][0],f[3][1],f[2][2],f[1][3],f[0][4];
记住这个小楼梯,每一个等于左边相邻的减去左上角相邻的
但是小循环要反过来
以第四次循环为例子,先得到:
f [ 3 ] [ 0 ] = y 3 f [ 2 ] [ 1 ] = f [ 2 , 3 ] = ( f [ 3 ] [ 0 ] − f [ 2 ] [ 0 ] ) / ( x 3 − x 2 ) f [ 1 ] [ 2 ] = f [ 1 , 2 , 3 ] = ( f [ 2 , 3 ] − f [ 1 , 2 ] ) / ( x 3 − x 1 ) = ( f [ 2 ] [ 1 ] − f [ 1 ] [ 1 ] ) / ( x 3 − x 1 ) f [ 0 ] [ 3 ] = f [ 0 , 1 , 2 , 3 ] = ( f [ 1 , 2 , 3 ] − f [ 0 , 1 , 2 ] ) / ( x 3 − x 0 ) = ( f [ 1 ] [ 2 ] − f [ 0 ] [ 2 ] ) / ( x 3 − x 0 ) f[3][0]=y_3\\ f[2][1]=f[2,3]=( f[3][0]-f[2][0])/(x_3-x_2)\\ f[1][2]=f[1,2,3]= (f[2,3]-f[1,2])/(x_3-x_1)=(f[2][1]-f[1][1])/(x_3-x_1)\\ f[0][3]=f[0,1,2,3]=(f[1,2,3]-f[0,1,2])/(x_3-x_0)=(f[1][2]-f[0][2])/(x_3-x_0)\\ f[3][0]=y3f[2][1]=f[2,3]=(f[3][0]−f[2][0])/(x3−x2)f[1][2]=f[1,2,3]=(f[2,3]−f[1,2])/(x3−x1)=(f[2][1]−f[1][1])/(x3−x1)f[0][3]=f[0,1,2,3]=(f[1,2,3]−f[0,1,2])/(x3−x0)=(f[1][2]−f[0][2])/(x3−x0)
所以核心python代码如下:
# _*_ coding:utf-8 _*_
# 牛顿多项式
from com.youngthing.mathalgorithm.polynomial import polynomial_simplify
from com.youngthing.mathalgorithm.interpolation.horner_polynominal import HornerPolynomial
def get_polynomial(data, n):
arr = [0 for _ in data]
if n == 0:
arr[len(data) - 1] = 1
return arr
matrix = [[1, -data[i][0]] for i in range(0, n)]
# 造一个多项式矩阵
p = polynomial_simplify(matrix)
# 不足的在前面补0
r = [0] * (len(data) - len(p))
r.extend(p)
return r
def interpolate(data: list):
# 首先定义数组e
e = [get_polynomial(data, i) for i, datum in enumerate(data)]
# 再次是定义两个差商数组
# 差商应该是一个二维数组吧
size = len(data)
f = [[0] * (size - index) for index, datum in enumerate(data)]
for i in range(0, size):
for j in range(0, i + 1):
yi = data[i][1]
xi = data[i][0]
f[i - j][j] = yi if j == 0 else (f[i - j + 1][j - 1] - f[i - j][j - 1]) / (xi - data[i - j][0])
# 差商数组完成
# 最后把多项式求和 如果数组长度为s,那么多项式最高s-1次,有s项
polynomial = [0] * size
print(e)
for i in range(0, size):
# i代表每个相加项
# 每一项都是同幂项系数相加
# j代表多项式从高到低的幂次
for j in range(0, size):
polynomial[j] += f[0][i] * e[i][j]
return HornerPolynomial(polynomial)
而霍纳多项式的代码,我就不写出来了
测试代码:
from com.youngthing.mathalgorithm.interpolation import newton
if __name__ == '__main__':
#测试数据
data=[[0,1],[2,5],[4,17]]
polynomial = newton.interpolate(data)
print(polynomial(0),polynomial(2),polynomial(4))
print(polynomial(1),polynomial(3),polynomial(5))
print(polynomial)
print(polynomial.polynomial)
测试结果
1.0 5.0 17.0
2.0 10.0 26.0
x**2+1
[1.0, 0.0, 1.0]