1.拉格朗日的推导
在数值分析中,拉格朗日插值法是以法国十八世纪数学家约瑟夫·拉格朗日命名的一种多项式插值方法。许多实际问题中都用函数来表示某种内在联系或规律,而不少函数都只能通过实验和观测来了解。如对实践中的某个物理量进行观测,在若干个不同的地方得到相应的观测值,拉格朗日插值法可以找到一个多项式,其恰好在各个观测的点取到观测到的值。这样的多项式称为拉格朗日(插值)多项式。
1.1 推导流程如下:
1.1.1主要思路:
通过简单的一次插值以及复杂一点的二次插值(就是我们所说的拉格朗日插值)最后推导到n次插值。
1.1.2对流程图的解释
- 基函数:
-
在数学中,基函数是函数空间一组特殊的基的元素。对于函数空间中的连续函数都可以将所要插的y值表示成一系列基函数的线性组合;表示如下:
-
对比在向量空间中,每个向量都可以表示成基向量的线性组合一样;
-
所以总结来看讲到基函数你就应该想到以下几点:
- 最终由基映射的结果是什么?——a.在基函数就是要插入的值;b.最终合成的向量;
- 基函数对应的变量是什么?——a.在基函数就是已知的各个x值;b.基本的单位向量;
-
基向量图示:
-
1.1.3 详细推导过程
1、线性插值(一次插值)
- 直线可以表现为下面两种形式:
y = L 1 ( x ) = y k + y k + 1 − y k x k + 1 − x k ( x − x k ) 点 斜 式 y = L 1 ( x ) = x − x k + 1 x k − x k + 1 y k + x − x k x k + 1 − x k y k + 1 两 点 式 \begin{array}{c} y=L_{1}(x)=y_{k}+\frac{y_{k+1}-y_{k}}{x_{k+1}-x_{k}}\left(x-x_{k}\right) {点斜式} \\ y=L_{1}(x)=\frac{x-x_{k+1}}{x_{k}-x_{k+1}} y_{k}+\frac{x-x_{k}}{x_{k+1}-x_{k}} y_{k+1}{两点式} \end{array} y=L1(x)=yk+xk+1−xkyk+1−yk(x−xk)点斜式y=L1(x)=xk−xk+1x−xk+1yk+xk+1−xkx−xkyk+1两点式
-
通过初高中知识去回忆上面的两个式子,首先计算斜率: k = y − y k x − x k = y k + 1 − y k x k + 1 − x k k = \frac{{y - {y_k}}}{{x - {x_k}}} = \frac{{{y_{k + 1}} - {y_k}}}{{{x_{k + 1}} - {x_k}}} k=x−xky−yk=xk+1−xkyk+1−yk,再回忆常用的公式: y = k ( x − x k ) + y k y = k(x - {x_k}) + {y_k} y=k(x−xk)+yk;这时候就与上面的式子一对应了;
-
图示:
2、拉格朗日插值(二次插值)
我们把两点式作为一个通用的推理式子,从意义上来说就是我们把两个点作为两个基,需要做的就是找出所插入的式子每个基各占多少。再结合到上面的两点式可以得到最终的通用式:
y
=
l
1
x
1
+
l
2
x
2
+
.
.
.
+
l
n
x
n
y = {l_1}{x_1} + {l_2}{x_2} + ... + {l_n}{x_n}
y=l1x1+l2x2+...+lnxn
那么我们现在来思考一下,如果插入点是
x
1
x_1
x1本身,那么其他项肯定就是0不然结果就不对了,并且结合已知的零点(可以肯定已知的点都是函数的零点),但是本身值肯定不能算算进去就变成0了,所以有以下式子:
l
k
−
1
(
x
)
=
A
(
x
−
x
k
)
(
x
−
x
k
+
1
)
l_{k-1}(x)=A\left(x-x_{k}\right)\left(x-x_{k+1}\right)
lk−1(x)=A(x−xk)(x−xk+1)
又因为
l
k
−
1
(
x
)
l_{k-1}(x)
lk−1(x)的取值为0或者1,而这时只能取1,所以最终的
A
A
A结果是:
l
k
−
1
(
x
)
=
(
x
−
x
k
)
(
x
−
x
k
+
1
)
(
x
k
−
1
−
x
k
)
(
x
k
−
1
−
x
k
+
1
)
l_{k-1}(x)=\frac{\left(x-x_{k}\right)\left(x-x_{k+1}\right)}{\left(x_{k-1}-x_{k}\right)\left(x_{k-1}-x_{k+1}\right)}
lk−1(x)=(xk−1−xk)(xk−1−xk+1)(x−xk)(x−xk+1)
将其带入通用式就是最终的结果。
3、n次拉格朗日插值
一次插值是将两个点进行计算,而2次插值是将3个点进行插值,n个插值就是将n+1个节点进行插值。
2.两种拉格朗日差值形式
2.1 朴素的拉格朗日插值
假如有
n
+
1
n+1
n+1个点,每个点形如
(
x
0
,
y
0
)
,
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
.
.
.
(x_0,y_0),(x_1,y_1),(x_2,y_2),...
(x0,y0),(x1,y1),(x2,y2),...,假设任意两个x都不相同,那么最后得到的多项式肯定是:
L
(
x
)
=
∑
j
=
0
n
(
y
i
∏
i
=
0
,
i
≠
j
n
x
−
x
i
x
j
−
x
i
)
L(x)=\sum_{j=0}^{n}\left(y_{i} \prod_{i=0, i \ne j}^{n} \frac{x-x_{i}}{x_{j}-x_{i}}\right)
L(x)=j=0∑n⎝⎛yii=0,i=j∏nxj−xix−xi⎠⎞
2.2 重心的拉格朗日插值
从朴素转换为中心的:
L
(
x
)
=
∑
j
=
0
n
(
y
i
∏
i
=
0
,
i
≠
j
n
x
−
x
i
x
j
−
x
i
)
→
L
(
x
)
=
l
(
x
)
∑
j
=
0
n
x
−
x
j
w
j
y
j
L(x) = \sum\limits_{j = 0}^n {\left( {{y_i}\prod\limits_{i = 0,i \ne j}^n {\frac{{x - {x_i}}}{{{x_j} - {x_i}}}} } \right)} \to L(x) = l(x)\sum\limits_{j = 0}^n {\frac{{x - {x_j}}}{{{w_j}}}} {y_j}
L(x)=j=0∑n⎝⎛yii=0,i=j∏nxj−xix−xi⎠⎞→L(x)=l(x)j=0∑nwjx−xjyj
其中:
l
(
x
)
=
∏
i
=
0
n
x
−
x
i
l(x) = \prod\limits_{i = 0}^n {x - {x_i}}
l(x)=i=0∏nx−xi
w = ∏ i = 0 , i ≠ j n x j − x i w = \prod\limits_{i = 0,i \ne j}^n {{x_j} - {x_i}} w=i=0,i=j∏nxj−xi
在这里我们需要把 l ( x ) l(x) l(x)和 w w w看做是一个整体(那么在计算的时候就相当于每次计算这两项都进行了保存),所以在进行从n个点到n+1个点过渡时就只需要对这两个式子多出得第n个点相应的操作。
2.3 优化技巧
发现每一个连乘的分子乘上一个二项式能变成一个统一的多项式,所以处理的时候直接用不需要的那个二项式去除那个多项式就行(下面的常数计算)
3.不使用的情况
为什么不用拉格朗日插值法来拟合大量的一维数据(比如机器学习上面)?
-
答:容易产生过拟合,且处理过程时间比较长。
-
例子:如果有n个样本 ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯ , ( x n , y n ) (x_1,y_1),(x_2,y_2),\cdots,(x_n,y_n) (x1,y1),(x2,y2),⋯,(xn,yn),拉格朗日多项式的次数就是 n − 1 n-1 n−1,表达式为: y = a 0 + a 1 x + a 2 x 2 + a 3 x 3 + ⋯ + a n x n y=a_0+a_1x+a_2x^2+a_3x^3+\cdots+a_{n}x^{n} y=a0+a1x+a2x2+a3x3+⋯+anxn
-
n个点我们需要计算出 a 0 , a 1 , . . . , a n a_0,a_1,...,a_n a0,a1,...,an这n个系数,并且在数据高低起伏过大的地方容易产生过拟合;
-
4.二次的拉格朗日插值代码实现
# 实现拉格朗日差值
# -*-coding:utf-8 -*-
import numpy as np
from scipy import interpolate
import pylab as pl #pylab是matplotlib的一个模块
# 自定义函数实现拉格朗日差值
# a:变量x的插入值,x、y代表已知的值
def h(x,y,a):
ans=0.0
# 实现累加,得到最终的结果y
for i in range(len(y)):
t=y[i]
# 实现累乘,得到系数l(x)
for j in range(len(y)):
if i !=j:
t*=(a-x[j])/(x[i]-x[j])
ans +=t
return ans
x=[1,0]
y=[0,2]
print(h(x,y,1.1))
a=interpolate.lagrange(x,y)
print(a)
print('insert 1.1 is:')
print(a(1.1))
# 调用库函数实现拉格朗日差值
x=np.linspace(0,10,11)
#x=[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
y=np.sin(x)
xnew=np.linspace(0,10,101)
pl.plot(x,y,"ro")
for kind in ["nearest","zero","slinear","quadratic","cubic"]:#插值方式
#"nearest","zero"为阶梯插值
#slinear 线性插值
#"quadratic","cubic" 为2阶、3阶B样条曲线插值
f=interpolate.interp1d(x,y,kind=kind)
# ‘slinear’, ‘quadratic’ and ‘cubic’ refer to a spline interpolation of first, second or third order)
ynew=f(xnew)
pl.plot(xnew,ynew,label=str(kind))
pl.legend(loc="lower right")
pl.show()
参考文章