所谓拉格朗日插值,就是在“拉格朗日”进行的一项民俗活动。拉格朗日通常在每年2月的第82个星期三。
(逃)
前言
非常强大的算法。
当可以证明某个函数是一个
k
k
k 次多项式时,我们就可以插入
k
+
1
k+1
k+1 个函数值并快速的求出我们要求的函数值。
拉格朗日插值
情境:
对于一个 n − 1 n-1 n−1 次的多项式,若给出了 n n n 个形如 f ( x i ) = y y f({x_i})=y_y f(xi)=yy 的条件( x i x_i xi 互不相同),请你对于给出的 k k k,求出对应的函数值 f ( k ) f(k) f(k)。
首先,可以证明,这个函数是存在且唯一的。
我们写出一个函数,形如:
f
(
x
)
=
∑
i
=
1
n
y
i
∏
j
≠
i
x
−
x
j
x
i
−
x
j
f(x)=\sum_{i=1}^ny_i\prod_{j\ne i}\frac{x-x_j}{x_i-x_j}
f(x)=i=1∑nyij=i∏xi−xjx−xj
对于第
i
i
i 项,当
x
=
x
j
(
j
≠
i
)
x=x_j(j\ne i)
x=xj(j=i) 时,会得到0,
x
=
x
i
x=x_i
x=xi 时,会得到
y
i
y_i
yi。
所以,这个函数时满足给出的
n
n
n 个条件的,又由于这个东西显然是
n
−
1
n-1
n−1 次的,所以它就是我们要找的那个唯一确定的函数。
那么我们直接把
k
k
k 往里代就好了,时间复杂度
O
(
n
2
)
O(n^2)
O(n2)
ll lagrange(int n,ll *x,ll *y,ll k){
k%=mod;
ll res(0);
for(int i=1;i<=n;i++){
ll s1=1,s2=1;
for(int j=1;j<=n;j++){
if(i==j) continue;
(s1*=(m-x[j]+mod))%=mod;
(s2*=(mod+x[i]-x[j]))%=mod;
}
(res+=y[i]*s1%mod*ksm(s2,mod-2)%mod)%=mod;
}
return res;
}
连续函数值的快速插值
很多时候,我们插入的
n
n
n 个值可以是
f
(
1
)
,
f
(
2
)
,
.
.
.
,
f
(
n
)
f(1),f(2),...,f(n)
f(1),f(2),...,f(n)。此时可以在
O
(
n
)
O(n)
O(n) 的复杂度内进行插值。
把原来的式子的
x
i
x_i
xi 全都换成
i
i
i,就变成:
f
(
x
)
=
∑
i
=
1
n
y
i
∏
j
≠
i
x
−
j
i
−
j
f(x)=\sum_{i=1}^ny_i\prod_{j\ne i}\frac{x-j}{i-j}
f(x)=i=1∑nyij=i∏i−jx−j
这个东西就非常好看,我们随便预处理出 一些前缀和和逆元就可以
O
(
1
)
O(1)
O(1) 求出
∏
\prod
∏ 里的结果。
总复杂度
O
(
n
)
O(n)
O(n)
ll lagrange(int n,ll *y,ll k){//consecutive
k%=mod;
jc[0]=1;
for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod;
ni[n]=ksm(jc[n],mod-2);
for(int i=n-1;i>=0;i--) ni[i]=ni[i+1]*(i+1)%mod;
pre[0]=1;
for(int i=1;i<=n;i++) pre[i]=pre[i-1]*(k-i)%mod;
suf[n+1]=1;
for(int i=n;i>=1;i--) suf[i]=suf[i+1]*(k-i)%mod;
ll res(0);
for(int i=1;i<=n;i++){
ll add=y[i]*pre[i-1]%mod*suf[i+1]%mod*ni[i-1]%mod*ni[n-i]%mod;
if((n-i)&1) add=mod-add;
(res+=add)%=mod;
}
return res;
}
重心拉格朗日插值
有的时候我们需要动态的插点,每一次都
O
(
n
2
)
O(n^2)
O(n2) 的重新计算是我们不能接受的。
看原式:
f
(
k
)
=
∑
i
=
1
n
+
1
y
i
∏
j
≠
i
k
−
x
j
x
i
−
x
j
f(k)=\sum_{i=1}^{n+1}y_i\prod_{j\ne i}\frac{k-x_j}{x_i-x_j}
f(k)=i=1∑n+1yij=i∏xi−xjk−xj
设:
g
(
k
)
=
∏
i
=
1
n
+
1
(
k
−
x
i
)
g(k)=\prod_{i=1}^{n+1}(k-x_i)
g(k)=i=1∏n+1(k−xi)
t
i
=
∏
j
≠
i
1
x
i
−
x
j
t_i=\prod_{j\ne i}\frac{1}{x_i-x_j}
ti=j=i∏xi−xj1
那么原式可以写成:
f
(
k
)
=
g
(
k
)
∑
i
=
1
n
+
1
y
i
t
i
k
−
x
i
f(k)=g(k)\sum_{i=1}^{n+1}\frac{y_it_i}{k-x_i}
f(k)=g(k)i=1∑n+1k−xiyiti
每次加入一个点时,暴力计算其重心
t
t
t 并更新其他点的重心即可。
单次复杂度达到
O
(
k
n
)
O(kn)
O(kn)(
k
k
k 为求逆元复杂度)。
应用:对函数为多项式形式的证明和对多项式次数的求解
我们要用拉格朗日插值,前提当然得是这个东西得是一个多项式,并且得求出它的次数。(这也往往是拉格朗日插值的难点)
并不是所有的函数都是多项式的! 比如,最简单的正弦函数就是一个无穷项数的函数(它的零点有无穷个)