所谓拉格朗日插值,就是在“拉格朗日”进行的一项民俗活动。拉格朗日通常在每年2月的第82个星期三。
(逃)
前言
非常强大的算法。
当可以证明某个函数是一个 kkk 次多项式时,我们就可以插入 k+1k+1k+1 个函数值并快速的求出我们要求的函数值。
拉格朗日插值
情境:
对于一个 n−1n-1n−1 次的多项式,若给出了 nnn 个形如 f(xi)=yyf({x_i})=y_yf(xi)=yy 的条件(xix_ixi 互不相同),请你对于给出的 kkk,求出对应的函数值 f(k)f(k)f(k)。
首先,可以证明,这个函数是存在且唯一的。
我们写出一个函数,形如:
f(x)=∑i=1nyi∏j≠ix−xjxi−xjf(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
对于第 iii 项,当 x=xj(j≠i)x=x_j(j\ne i)x=xj(j=i) 时,会得到0,x=xix=x_ix=xi 时,会得到 yiy_iyi。
所以,这个函数时满足给出的 nnn 个条件的,又由于这个东西显然是 n−1n-1n−1 次的,所以它就是我们要找的那个唯一确定的函数。
那么我们直接把 kkk 往里代就好了,时间复杂度 O(n2)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;
}
连续函数值的快速插值
很多时候,我们插入的 nnn 个值可以是 f(1),f(2),...,f(n)f(1),f(2),...,f(n)f(1),f(2),...,f(n)。此时可以在 O(n)O(n)O(n) 的复杂度内进行插值。
把原来的式子的 xix_ixi 全都换成 iii,就变成:
f(x)=∑i=1nyi∏j≠ix−ji−jf(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){//consecutivek%=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(n2)O(n^2)O(n2) 的重新计算是我们不能接受的。
看原式:
f(k)=∑i=1n+1yi∏j≠ik−xjxi−xjf(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=1n+1(k−xi)g(k)=\prod_{i=1}^{n+1}(k-x_i)g(k)=i=1∏n+1(k−xi)
ti=∏j≠i1xi−xjt_i=\prod_{j\ne i}\frac{1}{x_i-x_j}ti=j=i∏xi−xj1
那么原式可以写成:
f(k)=g(k)∑i=1n+1yitik−xif(k)=g(k)\sum_{i=1}^{n+1}\frac{y_it_i}{k-x_i}f(k)=g(k)i=1∑n+1k−xiyiti
每次加入一个点时,暴力计算其重心 ttt 并更新其他点的重心即可。
单次复杂度达到 O(kn)O(kn)O(kn)(kkk 为求逆元复杂度)。
应用:对函数为多项式形式的证明和对多项式次数的求解
我们要用拉格朗日插值,前提当然得是这个东西得是一个多项式,并且得求出它的次数。(这也往往是拉格朗日插值的难点)
并不是所有的函数都是多项式的! 比如,最简单的正弦函数就是一个无穷项数的函数(它的零点有无穷个)
例题:calc
看这篇博客吧。