拉格朗日插值学习笔记

众所周知,\(n+1\)\(x\) 不同的坐标会确定一个最高次为 \(n\) 的多项式,使其函数图像经过所有坐标

最简单的方法就是高斯消元,但是是 \(O(n^3)\) 的,考虑更快的方法

可以先让每个点先找到自己的投影点,也就是 \((x,0)\)

接下来对于每一个点 \(i\),求出一个函数 \(g_i(x)\),使它进过点 \(i\) 和其他点的投影点

此时 \(f(x)=\sum_{i=1}^n g_i(x)\)

\(g_i(x)=a*\prod_{j\neq i} (x-x_j)\),将 \((x_i,y_i)\) 代入得 \(a=\dfrac{y_i}{\prod_{j\neq i} (x_i-x_j)}\),所以:

\[g_i(x)=\prod_{j\neq i} (x-x_j)*\dfrac{y_i}{\prod_{j\neq i} (x_i-x_j)}=y_i*\prod_{j\neq i} \dfrac{x-x_j}{x_i-x_j} \]

最后相加即可

【模板】拉格朗日插值

https://www.gxyzoj.com/d/gxyznoi/p/270

\(x=k\),然后直接求就行了

点击查看代码
#include<cstdio>
#define ll long long
using namespace std;
const int mod=998244353;
int n,k,x[2005],y[2005];
ll qpow(ll x,int y)
{
	ll res=1;
	while(y)
	{
		if(y&1) res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
	}
	ll ans=0;
	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*qpow(x[i]-x[j],mod-2)%mod;
		}
		ans=(ans+tmp)%mod;
	}
	printf("%lld",(ans+mod)%mod);
	return 0;
}

接下来看怎么把系数弄出来,最简单的方法是 \(O(n^3)\) 暴力背包,但是可以发现主要的时间花在了处理分子上

注意到不考虑 \(i\),时,分子都是相同的,可以先单独算出来,再依次去除

\(g(x)=\prod_{i-1}^n (x-x_i)\)\(f(x)_i\) 表示 \(i\) 次项系数

所以 \(g(x)_0=-x_jfj(x)_0,g(x)_i=fj(x)_{i-1} -x_jfj(x)_i\)

所以 \(fj(x)_0=-g(x)_0/x_j,fj(x)_i=(fj(x)_{i-1} -g(x)_i)/x_j\)

点击查看代码
void lagrange(int n)
{
	for(int i=1;i<=n;i++) c[i]=1;
	for(int i=1;i<=n;i++)
	{
//		printf("%d %d\n",x[i],y[i]);
		inv[i]=qpow(x[i],mod-2);
		for(int j=1;j<=n;j++)
		{
			if(j!=i) c[i]=c[i]*(x[i]-x[j]+mod)%mod;
		}
		c[i]=qpow(c[i],mod-2)*y[i]%mod;
	}
	tmp[0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j>=0;j--)
		{
			tmp[j]=(tmp[j-1]-x[i]*tmp[j]%mod+mod)%mod;
		}
	}
	for(int i=1;i<=n;i++)
	{
		t[0]=mod-tmp[0]*inv[i]%mod;
		for(int j=1;j<n;j++)
		{
			t[j]=((t[j-1]-tmp[j])*inv[i]%mod+mod)%mod;
		}
		for(int j=0;j<n;j++) f[j]=(f[j]+t[j]*c[i])%mod;
	}
//	for(int i=0;i<n;i++) f[i]=f[i+2];
}
posted @ 2025-03-09 21:11  wangsiqi2010916  阅读(21)  评论(0)    收藏  举报