【算法笔记】Lagrange 插值法

  • 本文总计约 5000 字,阅读大约需要 30 分钟
  • 警告!警告!警告!本文有大量的 LATEX 公式渲染,可能会导致加载异常缓慢!

前言

又一次没有前言,以后要不就不写前言了吧,怪麻烦的 QwQ。

题目引入

根据代数基本定理,我们知道 (n+1)横坐标互不相同的点可以唯一确定一个最高次项为 n 的多项式。例如:

(1,2)(2,3) 就可以唯一确定多项式 f(x)=x+1;点 (1,1),(2,4),(3,9) 可以唯一确定多项式 g(x)=x2

那么,给定任意 n 个点,你能计算出其所唯一确定的多项式 f(x) 吗?其中 x104

暴力怎么做

当然,我们依旧是要想一想,暴力怎么做?

假如这 n 个点分别为 (x1,y1),(x2,y2),,(xn,yn),那么我们可以定义其确定的多项式为 f(x)=a0+a1x+a2x2++an1xn1

分别代入这 n 个点,得到一个方程:

{y1=a0+a1x1+a2x12++an1x1n1,y2=a0+a1x2+a2x22++an1x2n1,yn=a0+a1xn+a2xn2++an1xnn1.

当然,这个方程,我们就可以用 Gauss 消元法来解决,复杂度为 O(n3)

『哇啊,那这个复杂度太高了吧?能过 104 的数据吗?』

显然是不能的,所以,我们要做时间复杂度的优化。

Lagrange 插值

为了优化时间复杂度,著名的数学家 JL.Lagrange 给出了一个更加高效的算法,即 Lagrange 插值法。

我们知道,对任意多项式 f(x),若 x=a,那么 f(x)f(a)(mod(xa))

因此,我们分别代入 (x1,y1),(x2,y2),,(xn,yn),就有了:

{f(x)y1(mod(xx1)),f(x)y2(mod(xx2)),f(x)yn(mod(xxn)).

我们使用中国剩余定理,得到:

M=i=1n(xxi), Mi=Mxai, ti=ij1xxj,f(x)=i=1nyiMiti

因此,有:

f(x)=i=1nyiijxxjxixj.

上述多项式就是 Lagrange 插值法的公式,通过这个公式,我们可以通过 n 个点值求出其唯一确定的多项式。

通过这个公式,求多项式的时间复杂度为 Θ(n2)

代码

这个算法在洛谷上的模板为 P4781【模板】拉格朗日插值,因为我们只需要求 f(k) 的值,所以直接把 x=k 代入到上式中即可知:

f(k)=i=1nyijikxjxixj.

于是就可以直接模拟上述公式了,代码如下:

#include <cstdio>
using namespace std;

typedef long long ll;
const ll MOD = 998244353;
const int MAXN = 5001;

int n;
ll x[MAXN], y[MAXN], k, ans;

ll inv(ll val) {  //Fermat 小定理求逆元 
    val = (val % MOD + MOD) % MOD;

    ll p = MOD - 2, res = 1;
    
    while(p) {
        if(p & 1) res = (res * val) % MOD;
        val = (val * val) % MOD; p >>= 1;
    }
    return res;
}

int main(void) {
    scanf("%d%lld", &n, &k);
    
    for(int i = 1; i <= n; ++i) 
        scanf("%lld%lld", &x[i], &y[i]);
        
    for(int i = 1; i <= n; ++i) {  //插值主体 
        ll tmp = y[i];
        
        for(int j = 1; j <= n; ++j) {
            if(i == j) continue;
            tmp = (tmp * ((k - x[j]) % MOD + MOD) % MOD * inv(x[i] - x[j])) % MOD; 
        }
        
        ans = (ans + tmp) % MOD;
    }
    
    printf("%lld", ans);
    return 0;
}
//by CaO

重心 Lagrange 插值

Lagrange 插值固然效率高,但我们发现其有一个缺点:不可维护

所谓不可维护,就是如果我们在已知的点的基础上新增一些未知的点,那么我们就需要整个重新套用一次公式。

那么,单次重构的时间复杂度依旧为 Θ(n2)

『如果我要进行 m 次重构,那么总时间复杂度就是 Θ(mn2) 了,如果 n103m103,那么时间复杂度就不够了!』

所以,单次重构的时间复杂度太高,那么我们就要想办法降低重构的复杂度。

我们手玩一下上面的那个式子:

f(x)=i=1nyiijxxjxixj.

我们可以把求积符号拆分:

f(x)=i=1n(yiij(xxj)ij1xixj)

在求和号里面同时乘 (xxi) 并除以 (xxi)。就有:

f(x)=i=1n(yij=1n(xxj)1xxiijyixixj).

注意到 j=1nxxj 是一个常数,提出来,则有:

f(x)=j=1n(xxj)(i=1nyi1xxiij1xixj)=i=1n(xxi)(i=1nyiij(xixj)1xxi)

定义:

A(x)=i=1n(xxi),Wi=yiij(xixj).

原式即变为:

f(x)=A(x)i=1nWixxi.

我们会发现,每次新插值时,维护 A(x) 的复杂度为 O(1) 的,维护 W(1),W(2),,W(n+1) 的总复杂度为 O(n) 的。所以单次重构的时间复杂度为 O(n)

这种算法被称为重心 Lagrange 插值法,代码留给读者作为练习(其实是我太菜了 QwQ)。

例题

本题目列表会持续更新

posted @   CaO氧化钙  阅读(220)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示