一.插值的定义.
插值:给定若干个点 ( x i , y i ) (x_i,y_i) (xi,yi),确定一条函数曲线 f ( x ) f(x) f(x)满足对于所有 i i i,有 f ( x i ) = y i f(x_i)=y_i f(xi)=yi的过程称为插值.
多项式插值:给定 n + 1 n+1 n+1个 x i x_i xi两两不同的点 ( x i , y i ) (x_i,y_i) (xi,yi),确定一个多项式 f ( x ) = ∑ i = 0 n a i x i f(x)=\sum_{i=0}^{n}a_ix^{i} f(x)=∑i=0naixi满足对于所有 i i i,有 f ( x i ) = y i f(x_i)=y_i f(xi)=yi的过程称为插值.
方便起见,接下来若没有说明,则下文所说的所有插值均指多项式插值.
二.朴素插值方法.
根据插值的定义,很容易列出如下方程组:
{ a 0 + a 1 x 0 + a 2 x 0 2 + ⋯ + a n x 0 n = y 0 a 0 + a 1 x 1 + a 2 x 1 2 + ⋯ + a n x 1 n = y 1 ⋮ a 0 + a 1 x n + a 2 x n 2 + ⋯ + a n x n n = y n \left\{\begin{matrix} a_0+a_1x_0+a_2x_0^2+\cdots+a_nx_0^n=y_0\\ a_0+a_1x_1+a_2x_1^2+\cdots+a_nx_1^n=y_1\\ \vdots\\ a_0+a_1x_n+a_2x_n^2+\cdots+a_nx_n^n=y_n \end{matrix}\right. ⎩⎪⎪⎪⎨⎪⎪⎪⎧a0+a1x0+a2x02+⋯+anx0n=y0a0+a1x1+a2x12+⋯+anx1n=y1⋮a0+a1xn+a2xn2+⋯+anxnn=yn
接下来只需要高斯消元,就可以在 O ( n 3 ) O(n^3) O(n3)的时间复杂度内求出这个多项式了.
三.拉格朗日插值法.
显然, O ( n 3 ) O(n^3) O(n3)的时间复杂度并不令人满意,考虑有没有其他更加优秀的算法.
拉格朗日插值法的思路是构造 n + 1 n+1 n+1个基础多项式 l i ( x ) l_i(x) li(x)满足:
l i ( x i ) = 1 j ≠ i ⇒ l i ( x j ) = 0 l_i(x_i)=1\\ j\neq i\Rightarrow l_i(x_j)=0 li(xi)=1j=i⇒li(xj)=0
然后拉格朗日构造出了这样的基础多项式:
l i ( x ) = ∏ j ≠ i x − x j x i − x j l_i(x)=\prod_{j\neq i}\frac{x-x_j}{x_i-x_j} li(x)=j=i∏xi−xjx−xj
有了这 n + 1 n+1 n+1个基础多项式,我们就可以通过它们的线性组合得到 f ( x ) f(x) f(x):
f ( x ) = ∑ i = 0 n y i l i ( x ) = ∑ i = 0 n y i ∏ j ≠ i x − x j x i − x j f(x)=\sum_{i=0}^{n}y_il_i(x)=\sum_{i=0}^{n}y_i\prod_{j\neq i}\frac{x-x_j}{x_i-x_j} f(x)=i=0∑nyili(x)=i=0∑nyij=i∏xi−xjx−xj
也就是说若我们直接将多项式用 n + 1 n+1 n+1个点存下来,就可以做到 O ( n 2 ) O(n^2) O(n2)计算带入某个 x x x的值.
四.求出多项式的系数表示.
乍一看,如果要计算出这个多项式的系数表示,怎么着复杂度也是 O ( n 3 ) O(n^3) O(n3)的…
其实不然,我们来转化一下:
f ( x ) = ∑ i = 0 n y i ∏ j ≠ i x − x j x i − x j = ∑ i = 0 n y i ∏ j = 0 n ( x − x j ) x − x i 1 ∏ j ≠ i ( x i − x j ) f(x)=\sum_{i=0}^{n}y_i\prod_{j\neq i}\frac{x-x_j}{x_i-x_j}\\ =\sum_{i=0}^{n}y_i\frac{\prod_{j=0}^{n}(x-x_j)}{x-x_i}\frac{1}{\prod_{j\neq i}(x_i-x_j)} f(x)=i=0∑nyij=i∏xi−xjx−xj=i=0∑nyix−xi∏j=0n(x−xj)∏j=i(xi−xj)1
令 t i = y i ∏ j ≠ i ( x i − x j ) t_i=\frac{y_i}{\prod_{j\neq i}(x_i-x_j)} ti=∏j=i(xi−xj)yi,容易发现 t i t_i ti可以暴力计算,那么剩下的式子就是:
f ( x ) = ∑ i = 0 n t i ∏ j = 0 n ( x − x j ) x − x i f(x)=\sum_{i=0}^{n}t_i\frac{\prod_{j=0}^{n}(x-x_j)}{x-x_i} f(x)=i=0∑ntix−xi∏j=0n(x−xj)
令 g ( x ) = ∏ i = 0 n ( x − x i ) g(x)=\prod_{i=0}^{n}(x-x_i) g(x)=∏i=0n(x−xi),容易发现 g ( x ) g(x) g(x)也可以暴力算,式子变为:
f ( x ) = ∑ i = 0 n t i g ( x ) x − x i f(x)=\sum_{i=0}^{n}t_i\frac{g(x)}{x-x_i} f(x)=i=0∑ntix−xig(x)
接下来的问题就是 O ( n ) O(n) O(n)计算 g ( x ) x − x i \frac{g(x)}{x-x_i} x−xig(x),这可以用背包方案数的技巧做到.
时间复杂度 O ( n 2 ) O(n^2) O(n2).
五.利用拉格朗日插值法计算自然数幂和.
首先我们需要推一下自然数幂和的式子.记:
S k ( n ) = ∑ i = 1 n i k S_{k}(n)=\sum_{i=1}^{n}i^{k} Sk(n)=i=1∑nik
那么有:
S k ( n ) = S k ( n + 1 ) − ( n + 1 ) k = ∑ i = 1 n ( i + 1 ) k − ( n + 1 ) k + 1 = ∑ i = 1 n ∑ j = 0 k ( k j ) i j − ( n + 1 ) k + 1 = ∑ i = 0 k ( k i ) ∑ j = 1 n j i − ( n + 1 ) k + 1 = ∑ i = 0 k ( k i ) S i ( n ) − ( n + 1 ) k + 1 S_k(n)=S_k(n+1)-(n+1)^{k}\\ =\sum_{i=1}^{n}(i+1)^{k}-(n+1)^{k}+1\\ =\sum_{i=1}^{n}\sum_{j=0}^{k}\binom{k}{j}i^{j}-(n+1)^{k}+1\\ =\sum_{i=0}^{k}\binom{k}{i}\sum_{j=1}^{n}j^{i}-(n+1)^{k}+1\\ =\sum_{i=0}^{k}\binom{k}{i}S_{i}(n)-(n+1)^{k}+1 Sk(n)=Sk(n+1)−(n+1)k=i=1∑n(i+1)k−(n+1)k+1=i=1∑nj=0∑k(jk)ij−(n+1)k+1=i=0∑k(ik)j=1∑nji−(n+1)k+1=i=0∑k(ik)Si(n)−(n+1)k+1
推到这一步,我们来尝试一下推出 S k − 1 ( n ) S_{k-1}(n) Sk−1(n):
S k − 1 ( n ) = 1 k ( ( n + 1 ) k − 1 − ∑ i = 0 k − 2 ( k i ) S i ( n ) ) S_{k-1}(n)=\frac{1}{k}\left((n+1)^{k}-1-\sum_{i=0}^{k-2}\binom{k}{i}S_i(n)\right) Sk−1(n)=k1((n+1)k−1−i=0∑k−2(ik)Si(n))
也就是:
S k ( n ) = 1 k + 1 ( ( n + 1 ) k + 1 − 1 − ∑ i = 0 k − 1 ( k + 1 i ) S i ( n ) ) S_{k}(n)=\frac{1}{k+1}\left((n+1)^{k+1}-1-\sum_{i=0}^{k-1}\binom{k+1}{i}S_{i}(n)\right) Sk(n)=k+11((n+1)k+1−1−i=0∑k−1(ik+1)Si(n))
经过一波操作之后,我们确定了自然数幂和的式子,但是直接计算是 O ( k 2 ) O(k^2) O(k2)的,并不够优秀.
经过观察之后,我们发现这个式子必然是关于 n n n的 k + 1 k+1 k+1次多项式,那么接下来就是计算点和拉格朗日插值了.
计算点非常容易,对于所有 i ∈ [ 0 , k + 1 ] i\in [0,k+1] i∈[0,k+1],只需要计算出 i k i^{k} ik然后前缀和一下就是 S k ( i ) S_k(i) Sk(i)了,这一部分的时间复杂度为 O ( k log k ) O(k\log k) O(klogk).
然后是拉格朗日插值,然而是 O ( k 2 ) O(k^2) O(k2)的,复杂度并没有变…
但是这不是大问题,我们观察所取的点,发现是连续的 k + 2 k+2 k+2个点,这会有什么用呢?
代入拉格朗日插值公式:
f ( n ) = ∑ i = 0 k + 1 S k ( i ) ∏ j ≠ i n − j i − j = ∑ i = 0 k + 1 S k ( i ) 1 i ! ( − 1 ) k + 1 − i ( k + 1 − i ) ! ∏ j ≠ i ( n − j ) f(n)=\sum_{i=0}^{k+1}S_k(i)\prod_{j\neq i}\frac{n-j}{i-j}\\ =\sum_{i=0}^{k+1}S_k(i)\frac{1}{i!(-1)^{k+1-i}(k+1-i)!}\prod_{j\neq i}(n-j) f(n)=i=0∑k+1Sk(i)j=i∏i−jn−j=i=0∑k+1Sk(i)i!(−1)k+1−i(k+1−i)!1j=i∏(n−j)
对于最后一个连乘式,我们令:
P ( i ) = ∏ j = 0 i ( n − j ) S ( i ) = ∏ j = i k ( n − j ) P(i)=\prod_{j=0}^{i}(n-j)\\ S(i)=\prod_{j=i}^{k}(n-j) P(i)=j=0∏i(n−j)S(i)=j=i∏k(n−j)
那么有:
f ( n ) = ∑ i = 0 k + 1 S k ( i ) P ( i − 1 ) ∗ S ( i + 1 ) i ! ( − 1 ) k + 1 − i ( k + 1 − i ) ! f(n)=\sum_{i=0}^{k+1}S_k(i)\frac{P(i-1)*S(i+1)}{i!(-1)^{k+1-i}(k+1-i)!} f(n)=i=0∑k+1Sk(i)i!(−1)k+1−i(k+1−i)!P(i−1)∗S(i+1)
这就可以做到 O ( k ) O(k) O(k)拉格朗日插值了.
总时间复杂度即为 O ( k log k ) O(k\log k) O(klogk).通过一个线性筛 i k i^{k} ik的操作,可以做到近似 O ( k ) O(k) O(k)(实际上是 O ( k log 2 k ln k ) O\left(\frac{k\log_2 k}{\ln k}\right) O(lnkklog2k)).