本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
\quad 这居然才是我第二次写数学专题的博客……特别是在中考数学没考好之后,我越来越觉得数学要加把劲了 (似乎与中考并没有什么关系?但是不管怎样数学都是要补的)。
\quad 以下进入正文内容。
我们来回忆初中数学课上常见的一类题目:
- 给定一个二次函数上一些点的坐标,请描出这个二次函数的图像,然后求出某些横坐标 x i x_i xi 在函数上所对应的值 y i y_i yi 。
然后给出了这么一张表格:
x i x_i xi | 1 1 1 | 2 2 2 | 3 3 3 | 4 4 4 | 5 5 5 |
---|---|---|---|---|---|
y i y_i yi | 0 0 0 | 1 1 1 | 4 4 4 | 9 9 9 |
这个时候,你可能就会从里面随便选出了三个点: ( 1 , 0 ) , ( 2 , 1 ) , ( 3 , 4 ) (1,0), (2,1), (3,4) (1,0),(2,1),(3,4).
接着设出三个三元一次方程并联立得到:
{ x + y + z = 0 4 x + 2 y + z = 1 9 x + 3 y + z = 4 \begin{cases}x+y+z&=&0\\4x+2y+z&=&1\\9x+3y+z&=&4\end{cases} ⎩⎪⎨⎪⎧x+y+z4x+2y+z9x+3y+z===014
这种简单的式子当然难不倒你,你很快算出了解:
{ x = 1 y = − 2 z = 1 \begin{cases}x=1\\y=-2\\z=1\end{cases} ⎩⎪⎨⎪⎧x=1y=−2z=1
从而得到了二次函数:
f ( x ) = x 2 − 2 x + 1 f(x)=x^2-2x+1 f(x)=x2−2x+1
假设你不会因式分解简化运算,那么你最后会把 x = 5 x=5 x=5 带入函数式:
f ( 5 ) = x 2 − 2 x + 1 = 5 2 − 2 × 5 + 1 = 16 \begin{matrix}f(5)&=& x^2-2x+1\\&=&5^2-2\times 5+1\\&=&16\end{matrix} f(5)===x2−2x+152−2×5+116
就这样,我们完成了一次 把一个自变量带入函数中,求出其对应函数值的过程 ,这个过程,我们可以把它认为是一次插值。
简而言之,你可以把插值简单理解成把新的 x x x 丢进函数中,取出函数值的过程。
但是,我们还有办法。
既然我们上面是解方程,那么我们就可以直接按照数学的方法列出方程,并采用矩阵的形式存储下来,就像这样:
[ x 1 n x 1 n − 1 ⋯ x 1 2 x 1 y 1 x 2 n x 2 n − 1 ⋯ x 2 2 x 2 y 2 ⋮ ⋮ ⋱ ⋮ ⋮ ⋮ x n + 1 n x n + 1 n − 1 ⋯ x n + 1 2 x n + 1 y n + 1 ] \begin{bmatrix}x_1^n & x_1^{n-1} & \cdots& x_1^2 & x_1 & y_1\\x_2^n & x_2^{n-1} & \cdots& x_2^2 & x_2 & y_2\\\vdots & \vdots & \ddots& \vdots & \vdots & \vdots\\x_{n+1}^n & x_{n+1}^{n-1} & \cdots& x_{n+1}^2 & x_{n+1} & y_{n+1}\end{bmatrix} ⎣⎢⎢⎢⎡x1nx2n⋮xn+1nx1n−1x2n−1⋮xn+1n−1⋯⋯⋱⋯x12x22⋮xn+12x1x2⋮xn+1y1y2⋮yn+1⎦⎥⎥⎥⎤
这种矩阵我们可以通过高斯消元来计算出所有未知数(即函数各项的系数),然后将横坐标回代入函数解析式求出答案。由于不是我们讨论的重点,因此不作详细介绍。
学过高斯消元的同学都知道,高斯消元是一种暴力的解方程的办法, 其时间复杂度是 O ( n 3 ) O(n^3) O(n3) 的。虽说确实可以解决了 100 100 100 次函数插值的问题,但是面对更高次的方程,我们有没有更快的方法呢?
这就是为什么需要使用拉格朗日插值法。
我们再回到刚开始的二次函数。我们先是使用了一般式,然后勉勉强x强用高斯消元做到了 O ( n 3 ) \mathcal{O}(n^3) O(n3) 的复杂度,不太优秀——但是二次函数还有顶点式和交点式啊!
对于一般的多次函数,顶点式并不适用,因此我们从交点式的角度考虑。
一般的, n n n 次函数的交点式可以写成如下形式:
f ( x ) = a ( x − x 1 ) ( x − x 2 ) ⋯ ( x − x n ) = a ∏ i n ( x − x i ) \begin{matrix}f(x)&=&a(x-x_1)(x-x_2)\cdots(x-x_n)\\&=&a\ \displaystyle\prod_i^n (x-x_i)\end{matrix} f(x)==a(x−x1)(x−x2)⋯(x−xn)a i∏n(x−xi)
接下来,我们考虑怎么把给定的这些点带入上式中。
考虑对于每一个点定义如下函数 l i ( α ) = { β y i , if x i = α , 0 , if x i ≠ α . \begin{matrix}l_i(\alpha)=\begin{cases}\beta y_i,\ \text{if}\ \ x_i=\alpha,\\0 ,\ \text{if}\ \ x_i\neq \alpha.\end{cases}\end{matrix} li(α)={βyi, if xi=α,0, if xi̸=α.,其中 β \beta β 是一个任意的非 0 0 0 常数(即保证函数值是与 y i y_i yi成线性关系的且当 y i y_i yi 非 0 0 0 时 l i ( α ) l_i(\alpha) li(α) 也非 0 0 0)。
我们称这个函数为拉格朗日函数。它的定义用语文表述就是:只有在 x i x_i xi 处取到与 y i y_i yi 相关的值,其余位置均为 0 0 0.
另一方面,如果拉格朗日函数的定义域为所有 x i x_i xi 组成的集合,那么第 i i i 个点的拉格朗日函数 l ( i ) l(i) l(i) 也可以表示为:
l i ( x ) = a ∏ i ≠ j ( x − x j ) l_i(x)=a\ \displaystyle\prod_{i\neq j} (x-x_j) li(x)=a i̸=j∏(x−xj)
显然这个式子满足拉格朗日函数的性质。
我们考虑这样定义的拉格朗日函数有什么优点。当 x ≠ x i x\neq x_i x̸=xi 时,因为定义域是所有 x i x_i xi 组成的集合,所以总有一项为 x j − x j = 0 x_j-x_j=0 xj−xj=0,即 l i = 0 l_i=0 li=0;
当 x = x i x=x_i x=xi 时,移项得到 a = l i ( x i ) ∏ i ≠ j ( x i − x j ) = y i ∏ i ≠ j ( x i − x j ) a=\frac{l_i(x_i)}{\ \displaystyle\prod_{i\neq j} (x_i-x_j)}=\frac{y_i}{\ \displaystyle\prod_{i\neq j} (x_i-x_j)} a= i̸=j∏(xi−xj)li(xi)= i̸=j∏(xi−xj)yi
我们发现,我们对于所有的点都能用坐标 ( x i , y i ) (x_i,y_i) (xi,yi) 表示出了交点式中的 a a a .
这样问题就好办了。我们把这个 a a a 回代,就有了
f i ( x i ) = y i ∏ i ≠ j ( x i − x i ) ∏ i ≠ j ( x i − x j ) = y i f_i(x_i)=y_i\frac{\displaystyle\prod_{i\neq j} (x_i-x_i)}{\ \displaystyle\prod_{i\neq j} (x_i-x_j)}=y_i fi(xi)=yi i̸=j∏(xi−xj)i̸=j∏(xi−xi)=yi
满足要求!我们就愉快的把拉格朗日函数的 l l l 记号给扔掉了。
同时,我们还注意到,由于拉格朗日函数具有“独立性”,即对于每个 l i ( x ) l_i(x) li(x) 他们之间不会互相干涉(只要 i ≠ j i\neq j i̸=j 那就都是 0 0 0 了;对于每个 x i x_i xi 取到非 0 0 0 函数值的情况有且只有一种),所以我们把所有的 f i ( x ) f_i(x) fi(x) 加起来对于一个单独的 x k x_k xk 来说结果也是不会变的(因为在其它 l i l_i li 处取值都是 0 0 0 ),即对于任意的 x k x_k xk 均有
f ( x k ) = ∑ i = 1 n y i ∏ i ≠ j x k − x j x i − x j f(x_k)=\displaystyle\sum_{i=1}^n y_i\displaystyle\prod_{i\neq j}\frac{x_k-x_j}{x_i-x_j} f(xk)=i=1∑nyii̸=j∏xi−xjxk−xj
这样,我们就推出了一个唯一的多项式能经过所有的点。
拉格朗日公式推完了!!!
等下,你的定义域似乎是 所有 x i x_i xi 构成的集合 啊?
那我怎么对于任意数值插入多项式呢?
注意到 n + 1 n+1 n+1 个点只能唯一确定一个 n n n 次多项式,因此我们上面推出的这个函数是唯一确定的。对于一个唯一确定的函数,当然把任意的 x x x 带入都能得到唯一且正确的答案啦!
复杂度优化到了 O ( n 2 ) \mathcal{O}(n^2) O(n2),可以通过 n ≤ 5000 n\leq 5000 n≤5000 的数据。
代码还是自己实现的好~其实这个代码真的没什么细节就是对着公式写。
配套练习:洛谷 P4781 【模板】拉格朗日插值
#include
#include
const int MAXN=2010;
const int MOD=998244353;
struct node{
int x,y;
node(int x=0,int y=0):x(x),y(y){}
bool operator<(const node &rhs)const{
return x<rhs.x||(x==rhs.x&&y<rhs.y);
}
}p[MAXN];
inline long long inv(long long x){
long long res=1;
int b=MOD-2;
while(b){
if(b&1)
res=res*x%MOD;
x=x*x%MOD;b>>=1;
}
return res;
}
int main(){
int n,k;scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
int x,y;scanf("%d%d",&x,&y);
p[i]=node(x,y);
}
long long res=0;
for(int i=1;i<=n;i++){
long long prod1=1,prod2=1;
for(int j=1;j<=n;j++){
if(i==j)
continue;
prod1=prod1*(k-p[j].x+MOD)%MOD;
prod2=prod2*(p[i].x-p[j].x+MOD)%MOD;
}
res=(res+prod1*inv(prod2)%MOD*p[i].y%MOD)%MOD;
}
printf("%lld\n",res);
return 0;
}
[Warning]
请不要使用拉格朗日插值法完成数学课本上函数相关章节的练习。
[Extra Exercises]
请独立思考如何在给定的点的横坐标为 { x ∈ N ∗ ∣ x ≤ n } \{x\in \mathbb{N}^{*}|x\leq n\} {x∈N∗∣x≤n} 时将拉格朗日插值的复杂度优化到 O ( n ) \mathcal{O}(n) O(n).