拉格朗日插值
朴素的拉格朗日插值
插值就是有 n n n 个已知的平面直角坐标系中的点,然后求出一个 n − 1 n-1 n−1 次多项式,这个多项式穿过这些点。
{ x } : x 0 , x 1 , x 2 , ⋯ , x n { y } : y 0 , y 1 , y 2 , ⋯ , y n \{ x \} : x_0, x_1, x_2, \cdots, x_n \\ \{ y \} : y_0, y_1, y_2, \cdots, y_n {x}:x0,x1,x2,⋯,xn{y}:y0,y1,y2,⋯,yn
然后我们要找到一个 f ( x ) f(x) f(x) 穿过这些点。拉格朗日插值给出了一种显然的 O ( n 2 ) O(n^2) O(n2) 的构造方法,也就是:
f ( x ) = ∑ k = 0 n ∏ i = 0 , i ≠ k n ( x − x i ) ∏ i = 0 , i ≠ k n ( x k − x i ) y k f(x) = \sum_{k=0}^n \frac{\prod\limits_{i=0, i \neq k}^n (x-x_i)}{\prod\limits_{i=0, i \neq k}^n (x_k - x_i)}y_k f(x)=k=0∑ni=0,i=k∏n(xk−xi)i=0,i=k∏n(x−xi)yk
正确性非常显然qwq。
重心拉格朗日插值
我们观察一下上面的式子,我们发现我们每次求解一个 f ( x ) f(x) f(x) 的时间复杂度就是 O ( n 2 ) O(n^2) O(n2),但是我们如果要求出这个 n n n 次多项式的各个项的系数的话那么时间复杂度就上升到了 O ( n 3 ) O(n^3) O(n3)。这个时间复杂度就和普通的高斯消元没有区别了。所以,我们要考虑如何优化朴素的拉格朗日插值,这时候就要引出重心拉格朗日插值了。
第一个小小的优化
首先我们来看看这个朴素法的式子:
f ( k ) = ∑ i = 0 n y i ∏ j = 0 , j ≠ i n k − x j x i − x j f(k) = \sum_{i=0}^n y_i \prod_{j = 0,j \neq i}^n \frac{k - x_j}{x_i - x_j} f(k)=i=0∑nyij=0,j=i∏nxi−xjk−xj
我们 很难 发现在
x
x
x 的取值是连续(也就是说我们知道
(
1
,
f
(
1
)
)
,
(
2
,
f
(
2
)
)
,
(
3
,
f
(
3
)
)
,
⋯
(1, f(1)), (2, f(2)), (3, f(3)), \cdots
(1,f(1)),(2,f(2)),(3,f(3)),⋯ 的时候)的时候有一种优化时间复杂度的方法。在这种情况下显然有
x
i
=
1
,
x
j
=
j
x_i = 1, x_j = j
xi=1,xj=j。所以这个式子就可以变成这样:
f ( k ) = ∑ i = 0 n y i ∏ j = 0 , j ≠ i n k − j i − j f(k) = \sum_{i = 0}^ny_i \prod_{j = 0, j \neq i}^n \frac{k - j}{i - j} f(k)=i=0∑nyij=0,j=i∏ni−jk−j
这样之后我们就可以用一些简单的预处理来做到 O ( n ) O(n) O(n) 预处理 O ( 1 ) O(1) O(1) 查询 ∏ j = 0 , j ≠ i n k − j i − j \prod\limits_{j=0, j\neq i}^n \frac{k-j}{i-j} j=0,j=i∏ni−jk−j 这一坨东西。具体可以这样:我破门预处理
p r e i = ∏ j = 0 i ( k − j ) s u f i = ∏ j = i n ( k − j ) f a c i = ∏ p = 1 i p = i ! \begin{aligned} & pre_i = \prod_{j=0}^i (k-j) \\ & suf_i = \prod_{j = i}^n (k-j) \\ & fac_i = \prod_{p=1}^i p = i! \end{aligned} prei=j=0∏i(k−j)sufi=j=i∏n(k−j)faci=p=1∏ip=i!
之后我们就有:
∏ j = 0 , j ≠ i n k − j i − j = p r e i − 1 × s u f i + 1 f a c i × f a c n − i × ( − 1 ) n − i \prod_{j = 0, j \neq i}^n \frac{k-j}{i-j} = \frac{pre_{i-1} \times suf_{i+1}}{fac_{i} \times fac_{n-i} \times (-1)^{n-i}} j=0,j=i∏ni−jk−j=faci×facn−i×(−1)n−iprei−1×sufi+1
那么整个式子就变成了:
f ( k ) = ∑ i = 0 n y i p r e i − 1 × s u f i + 1 f a c i × f a c n − i × ( − 1 ) n − i f(k) = \sum_{i=0}^n y_i \frac{pre_{i-1} \times suf_{i+1}}{fac_{i} \times fac_{n-i} \times (-1)^{n-i}} f(k)=i=0∑nyifaci×facn−i×(−1)n−iprei−1×sufi+1
这样之后我们就可以在 O ( n ) O(n) O(n) 的时间内算出某一个具体的 f ( a ) f(a) f(a)。但是要知道这个表达式各项的系数还是需要 O ( n 3 ) O(n^3) O(n3) 的时间才能算出来。
正题:重心拉格朗日插值
重心拉格朗日插值能做到比上面的 O ( n 3 ) O(n^3) O(n3) 快很多的求出一个式子的表达式各项系数,而且还能做到让你新添加一个点然后快速算出新的多项式的表达式,这比前面的东西都要优秀。现在我们来看看这是怎么做到的。
首先我们发现了一件事,就是说在 ∏ j = 0 , j ≠ i n k − x j x i − x j \prod\limits_{j=0, j \neq i}^n\frac{k - x_j}{x_i - x_j} j=0,j=i∏nxi−xjk−xj 这一坨式子里面每个分子都是 k − x j k - x_j k−xj 然后呢我们就这样:
令:
g ( k ) = ∏ i = 0 n ( k − x i ) g(k) = \prod_{i=0}^n (k - x_i) g(k)=i=0∏n(k−xi)
然后:
f ( k ) = g ( k ) ∑ i = 0 n y i k − x i ∏ j = 0 , j ≠ i n 1 x i − x j f(k) = g(k) \sum_{i = 0}^n \frac{y_i}{k - x_i} \prod_{j=0, j\neq i}^n \frac{1}{x_i - x_j} f(k)=g(k)i=0∑nk−xiyij=0,j=i∏nxi−xj1
再令:
t i = ∏ j = 0 , j ≠ i n 1 x i − x j t_i = \prod_{j = 0, j \neq i}^n \frac{1}{x_i - x_j} ti=j=0,j=i∏nxi−xj1
那么:
f ( k ) = g ( k ) ∑ i = 0 n y i t i k − x i f(k) = g(k)\sum_{i=0}^n \frac{y_it_i}{k-x_i} f(k)=g(k)i=0∑nk−xiyiti
可以很明显的看出,如果我们多家进去一个点 ( x n + 1 , y n + 1 ) (x_{n+1}, y_{n+1}) (xn+1,yn+1),我们只需要将每一个 t i t_i ti 都除以一个 x i − x n + 1 x_i - x_{n+1} xi−xn+1,这样就可以 O ( n ) O(n) O(n) 的得到新的 ∑ i = 0 n y i t i k − x i \sum\limits_{i=0}^n\frac{y_it_i}{k - x_i} i=0∑nk−xiyiti。然后再与新的 g ( k ) g(k) g(k) 乘起来就得到了新的多项式。总的时间复杂度就是 O ( n ) O(n) O(n),非常的快。