模板:拉格朗日插值(多项式)

60 篇文章 0 订阅
13 篇文章 0 订阅

所谓拉格朗日插值,就是在“拉格朗日”进行的一项民俗活动。拉格朗日通常在每年2月的第82个星期三。

(逃)

前言

非常强大的算法。
当可以证明某个函数是一个 k k k 次多项式时,我们就可以插入 k + 1 k+1 k+1 个函数值并快速的求出我们要求的函数值。

拉格朗日插值

情境:

对于一个 n − 1 n-1 n1 次的多项式,若给出了 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=1nyij=ixixjxxj
对于第 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 n1 次的,所以它就是我们要找的那个唯一确定的函数。
那么我们直接把 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=1nyij=iijxj
这个东西就非常好看,我们随便预处理出 一些前缀和和逆元就可以 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=1n+1yij=ixixjkxj
设:
g ( k ) = ∏ i = 1 n + 1 ( k − x i ) g(k)=\prod_{i=1}^{n+1}(k-x_i) g(k)=i=1n+1(kxi)
t i = ∏ j ≠ i 1 x i − x j t_i=\prod_{j\ne i}\frac{1}{x_i-x_j} ti=j=ixixj1
那么原式可以写成:
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=1n+1kxiyiti
每次加入一个点时,暴力计算其重心 t t t 并更新其他点的重心即可。
单次复杂度达到 O ( k n ) O(kn) O(kn) k k k 为求逆元复杂度)。

应用:对函数为多项式形式的证明和对多项式次数的求解

我们要用拉格朗日插值,前提当然得是这个东西得是一个多项式,并且得求出它的次数。(这也往往是拉格朗日插值的难点)

并不是所有的函数都是多项式的! 比如,最简单的正弦函数就是一个无穷项数的函数(它的零点有无穷个)

例题:calc
这篇博客吧。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值