浅谈拉格朗日插值法及其应用

前言

最近在做历年的NOI原题,然后就做到了[NOI2019] 机器人,惊讶地发现我居然没学过拉格朗日插值🤡!

之前的省选题本来有一道拉插的题([省选联考 2022] 填树),但是在 O n e I n D a r k \rm O\red{neInDark} OneInDark 的推荐下用了斯特林反演。其实拉插很简单,而且更适合做像填树、机器人这样的DP优化题目。

简介

粘的别人的
在数值分析中,拉格朗日插值法是以法国18世纪数学家约瑟夫·拉格朗日命名的一种多项式插值方法。如果对实践中的某个物理量进行观测,在若干个不同的地方得到相应的观测值,拉格朗日插值法可以找到一个多项式,其恰好在各个观测的点取到观测到的值。上面这样的多项式就称为拉格朗日(插值)多项式。

拉格朗日插值法

众所周知,给出一个 n n n 次多项式的 n + 1 n+1 n+1 个点值可以唯一地求出这个多项式,一个朴素的求法就是列出 n + 1 n+1 n+1 个方程然后使用高斯消元,复杂度 O ( n 3 ) O(n^3) O(n3),往往还伴随精度问题。

而拉格朗日插值法可以在 O ( n 2 ) O(n^2) O(n2) 时间内以足够高的精度求出多项式。

直接上拉格朗日插值公式:
f ( x ) = ∑ i = 0 n y i ∏ j ≠ i x − x j x i − x j f(x)=\sum_{i=0}^ny_i\prod_{j\neq i}\frac{x-x_j}{x_i-x_j} f(x)=i=0nyij=ixixjxxj这个式子十分巧妙,因为带入后可以发现它刚好能取到所有的点值,并且次数最大为 n n n

用这个公式直接求多项式虽然也是 O ( n 3 ) O(n^3) O(n3)(还比高消慢),但是是可以优化滴!

我们可以先求出 g ( x ) = ∏ i = 0 n ( x − x i ) g(x)=\prod_{i=0}^n(x-x_i) g(x)=i=0n(xxi),然后枚举 y i y_i yi 时先 O ( n ) O(n) O(n) 计算下面的 ∏ j ≠ i 1 x i − x j \prod_{j\neq i}\frac{1}{x_i-x_j} j=ixixj1,然后由于 g ( x ) g(x) g(x) 在计算的时候没有被掐断,所以单个 ( x − x i ) (x-x_i) (xxi) 可以整除 g ( x ) g(x) g(x),直接 O ( n ) O(n) O(n) 递推求出 ∏ j ≠ i ( x − x j ) \prod_{j\neq i}(x-x_j) j=i(xxj) 即可(注意特判 x i = 0 x_i=0 xi=0)。这样总的复杂度就降为了 O ( n 2 ) O(n^2) O(n2)

求单个点值

这里求单个点值的意思是给出 k ∉ { x i ∣ 0 ≤ i ≤ n } k\notin \{x_i|0\le i\le n\} k/{xi0in},我们需要求 f ( k ) f(k) f(k)

我们显然可以直接求出 f ( x ) f(x) f(x) 然后带入求解,但是当我们的目的仅仅是求出 f ( k ) f(k) f(k) 时,这么做就显得麻烦且低效。

我们不妨直接带入拉格朗日插值公式:
f ( k ) = ∑ i = 0 n y i ∏ j ≠ i k − x j x i − x j f(k)=\sum_{i=0}^ny_i\prod_{j\neq i}\frac{k-x_j}{x_i-x_j} f(k)=i=0nyij=ixixjkxj由于不再是求多项式,所以我们可以在 O ( n ) O(n) O(n) 时间内求出所有的 ∏ j ≠ i ( k − x j ) \prod_{j\neq i}(k-x_j) j=i(kxj),然后每次枚举 y i y_i yi 时就只用求 ∏ j ≠ i 1 x i − x j \prod_{j\neq i}\frac{1}{x_i-x_j} j=ixixj1 了。虽然还是 O ( n 2 ) O(n^2) O(n2),但是减小了常数且更简单。

O(n)求单个点值

还是只要求求出 f ( k ) f(k) f(k)

在多数应用到拉插的题目中,这 n + 1 n+1 n+1 个点值是满足下标连续,或者等间距的,即 x 1 − x 0 = x 2 − x 1 = . . . = x n − x n − 1 x_1-x_0=x_2-x_1=...=x_n-x_{n-1} x1x0=x2x1=...=xnxn1

此时我们可以做些预处理(比如当间距等于 1 的时候,你需要预处理阶乘的逆元),然后就可以单次 O ( 1 ) O(1) O(1) 求出 ∏ j ≠ i 1 x i − x j \prod_{j\neq i}\frac{1}{x_i-x_j} j=ixixj1。(需要特别注意符号问题)

应用

有一个经典问题:计算 ∑ i = 1 n i k \sum_{i=1}^ni^k i=1nik。这个问题显然可以用斯特林反演 O ( k log ⁡ k ∼ k 2 ) O(k\log k\sim k^2) O(klogkk2) 解决。

众所周知这个东西是一个关于 n n n k + 1 k+1 k+1 次多项式,所以我们可以先 O ( k ) O(k) O(k)(忽略快速幂,你也可以用线性筛)求出前 k + 2 k+2 k+2 个点值,然后用上面的方法 O ( k ) O(k) O(k) 拉插求出 n n n 处的答案,这样总复杂度只有 O ( k ) O(k) O(k)

更实用的场景是在一些DP题中,比如你需要求 m m m 个DP值来转移,但是你发现这 m m m 个点值的函数是一个次数较小的 n n n 次多项式,那么就可以只求前 n + 1 n+1 n+1 个DP值,剩下的拉插即可。

一般要看出这是个多项式需要归纳证明,但是我不会

进阶:快速插值

回到最初的问题上,我们需要求出多项式 f ( x ) = ∑ i = 0 n y i ∏ j ≠ i x − x j x i − x j f(x)=\sum_{i=0}^ny_i\prod_{j\neq i}\frac{x-x_j}{x_i-x_j} f(x)=i=0nyij=ixixjxxj。如果 n ≤ 1 0 5 n\le 10^5 n105 怎么办?

我们需要把 O ( n 2 ) O(n^2) O(n2) 的算法再一次优化。

稍微变一下式子:
f ( x ) = ∑ i = 0 n y i ∏ j ≠ i ( x i − x j ) ∏ j ≠ i ( x − x j ) = ∑ i = 0 n y i ( x i − x i ) g ( x i ) g ( x ) x − x i f(x)=\sum_{i=0}^n\frac{y_i}{\prod_{j\neq i}(x_i-x_j)}\prod_{j\neq i}(x-x_j)\\ =\sum_{i=0}^n\frac{y_i(x_i-x_i)}{g(x_i)}\frac{g(x)}{x-x_i} f(x)=i=0nj=i(xixj)yij=i(xxj)=i=0ng(xi)yi(xixi)xxig(x)但是这个时候式子就变得很奇怪: x i − x i g ( x i ) \frac{x_i-x_i}{g(x_i)} g(xi)xixi 上下两边都是0!这求个邒!

然而,正因为上下两边都是0,我们可以使用洛必达法则:
lim ⁡ x → x i x − x i g ( x ) = lim ⁡ x → x i 1 g ′ ( x ) = 1 g ′ ( x i ) \lim_{x\rightarrow x_i}\frac{x-x_i}{g(x)}=\lim_{x\rightarrow x_i}\frac{1}{g'(x)}=\frac{1}{g'(x_i)} xxilimg(x)xxi=xxilimg(x)1=g(xi)1所以第一步,我们可以先分治NTT求出 g ( x ) g(x) g(x) g ′ ( x ) g'(x) g(x),然后多项式多点求值求出每一个 g ′ ( x i ) g'(x_i) g(xi)

剩下还有 g ( x ) x − x i \frac{g(x)}{x-x_i} xxig(x) 这部分,再用一次分治NTT即可。

于是我们可以在 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n) 时间内解决此问题。如果使用转置原理的多点求值,代码将会简单很多。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值