[学习笔记]拉格朗日&重心拉格朗日插值法

〇、前言 ¶

鸽了好久了,于 \(2021/4/5\) 看到重心 \(\tt Lagrange\) 插值法,惊讶于 \(\mathcal O(n)\) 的快速插值,想要把这篇完善一下。

壹、普通 Lagrange 插值法 ¶

对于一个 \(k\) 次多项式 \(f(x)\),假如我们已经知道其中 \(k+1\) 个点的坐标 \((x_i,y_i)\),那么我们并不需要知道这个多项式的系数(其实也是可解的)而直接求任意一个 \(x\) 对应的坐标。

假设我们已知 \(k+1\)\((x_i,y_i)\),现在我们要求 \(x\) 所对应的 \(y\),有

\[y=\sum_{i=0}^k y_i\prod_{j=0,j\neq i}^k \frac{x-x_j}{x_i-x_j} \]

直接求得 \(y\) 咯。

当然,如果你想要联立 \(k+1\) 个多项式高斯消元解出 \(f\),再求 \(f(x)\),这也是可以的 但是似乎拉格朗日比高斯消元好记

求原函数或单点差值均 \(\mathcal O(n^2)\).

贰、重心 Lagrange 插值法 ¶

考虑我们的插值法计算了很多重复的部分,不妨把式子变个型:

\[\begin{aligned} y&=\sum_{i=0}^k y_i\prod_{j=0,j\neq i}^k \frac{x-x_j}{x_i-x_j} \\ &=\sum_{i=0}^ky_i\prod_{j=0,j\neq i}^k(x-x_j)\prod_{p=0,p\neq i}^k{1\over x_i-x_p} \\ &=\sum_{i=0}^k\prod_{j=0,j\neq i}^k(x-x_j)\prod_{p=0,p\neq i}^k{y_i\over x_i-x_p} \\ &=\prod_{j=0}^k(x-x_j)\left (\sum_{i=0}^k{y_i\over \prod_{p=0,p\neq i}^k(x_i-x_p)}\times {1\over x-x_i}\right) \end{aligned} \]

最后一步的变形,为了让 \(\prod\) 脱离 \(i\) 的限制,补上了一个 \(\large {1\over x-x_i}\),再在后面乘上分数。

稍微整理一下,若

\[l(x)\overset\Delta=\prod_{i=0}^k(x-x_i) \\ w_i\overset\Delta={y_i\over \prod_{p=0,p\neq j}^k(x_i-x_j)} \]

那么最后,得到差值过程:

\[y=l(x)\sum_{i=0}^k{w_i\over x-x_i} \]

预处理仍然是 \(\mathcal O(n^2)\),但是单次差值变成 \(\mathcal O(n)\),求原函数仍然是 \(\mathcal O(n^2)\).

叁、参考代码 ¶

还没有时间打重心 \(\tt lagrange\) 的插值法,目前只打了 \(\mathcal O(n^2)\) 的。

模板连接

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long ll;
template<class T>inline T readin(T x){
	x=0; int f=0; char c;
	while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
	for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
	return f? -x: x;
}

const int maxn=2000;
const int mod=998244353;

inline int qkpow(int a, int n){
	int ret=1;
	for(; n>0; n>>=1, a=1ll*a*a%mod)
		if(n&1) ret=1ll*ret*a%mod;
	return ret;
}

int x[maxn+5], y[maxn+5], n, k;

inline void input(){
	n=readin(1), k=readin(1);
	for(int i=1; i<=n; ++i)
		x[i]=readin(1), y[i]=readin(1);
}

inline void getval(int k){
	int ans=0;
	for(int i=1; i<=n; ++i){
		int up=1, down=1;
		for(int j=1; j<=n; ++j) if(j!=i){
			up=1ll*up*(k-x[j])%mod;
			down=1ll*down*(x[i]-x[j])%mod;
		}
		ans=(ans+1ll*y[i]*up%mod*qkpow(down, mod-2)%mod)%mod;
	}
	printf("%d\n", (ans+mod)%mod);
}

signed main(){
	input();
	getval(k);
	return 0;
}
posted @ 2020-07-17 09:50  Arextre  阅读(1620)  评论(0编辑  收藏  举报