拉格朗日插值

最好有小学六年级的数学水平(doge)。这篇博客的语言会比较简单通俗,大家可以先略作了解,之后通过别的文章继续深入。

基础拉格朗日插值

我们先了解最简单的拉格朗日插值可以干什么。

由小学知识可知 n 个点 (xi,yi) 可以唯一地确定一个多项式 y=f(x)

现在,给定这 n 个点,请你确定这个多项式。

第一眼,我们很容易想到可以使用高斯消元法在 O(n3) 的时间内求解。但是拉格朗日插值可以做到 O(n2) 甚至 O(nlog2n) 。我们这里讲解 O(n2) 的插值。

为了举个例子,我们需要对以下四个点求出函数值:

我们考虑构造 4 个基函数,期中,第 i 个基函数需要满足对于第 i 个点,函数值为 1 ,其余点,函数值都为 0 。怎么构造这个基函数呢?我们拿第一个点 (6,4) 来说,如果第三个点 (32) 要在 x=3y=0 ,那么我们可以为这个基函数构造 (x3) 项,这样子就会一定满足条件。我们很容易构造出基函数 f1 :

f1(x)=c×x(x3)(x6)

我们对于 x=6 时需求 f1(x)=1 ,那么有:

c=1x(x3)(x6)=16×(9)×(12)=1648

所以有:

f1(x)=1648x(x3)(x6)

类似的,我们可以得到另外的基函数 f2,f3,f4 。但是这样我们怎么得到最终的函数呢?

f(x)=i=1nyifi(x)

因为这个函数时一定满足我们需求的,对于给定的 n 个点之一的 (xi,yi) ,它只有在循环到 i 时, yifi(xi)=yi 有值,其余时刻按照基函数的定义都为 0 。所以这十分合理。我们可以改写成公式的形式:

f(x)=i=1nyij=1,jixxixixj

模板题

贴出一份封装了 namespace 的代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-f;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar(); 
	}
	return x*f;
}
const int mod=998244353;
const int MAXN=2e3+10;
namespace Lagrange{
	int qpow(int a,int b){
		int ans=1,base=a;
		while(b){
			if(b&1) ans=ans*base%mod;
			base=base*base%mod;
			b>>=1;
		}
		return ans;
	}
	int inv(int x){
		return qpow(x,mod-2);
	}
	int lagrange(int n,int *x,int *y,int k){
		int ans=0;
		for(int i=1;i<=n;i++){
			int top=1,down=1;
			for(int j=1;j<=n;j++){
				if(j==i) continue;
				top=top*((k-x[j]+mod*2)%mod)%mod;
				down=down*((x[i]-x[j]+mod*2)%mod)%mod;
			}
			ans=(ans+y[i]*top%mod*inv(down))%mod; 
		}
		return ans;
	}
}
using namespace Lagrange;
int n,x[MAXN],y[MAXN],k;
signed main(){
	n=read(),k=read();
	for(int i=1;i<=n;i++) x[i]=read(),y[i]=read();
	cout<<lagrange(n,x,y,k);
	return 0;
}

连续点的拉格朗日插值

很多时候,我们给出的点的 xi 是连续的,这样我们的插值是 O(n) 的。我们先拿出刚才的式子:

f(x)=i=1nyij=1,jixxixixj

可以想到,后面的分母就是两个阶乘的和,分子大概也是一段连续的数的积。具体来讲,我们可以优化 j=1,jixxixixj 的计算。我们定义:

faci=j=1ii

gi=j=1i(kxi)

hi=j=in(kxj)

那么原式就可以表示为:

$f(x)=i=1nyigi1hi+1faci1facni

注意分母的奇偶性问题,当 ni 是奇数的时候,分母取原来的相反数。

为了不让这个博客太短,我们进行一些拓展: i=1nik

没错求它的取值,但是 n 很巨大, k 会小一些。我们可以做到 O(k) ,其实是因为这个式子可以被描述为一个以 n 为变量的 k+1 次多项式。我们可以先看到一些简单例子:

i=1n1=n

i=1ni=(1+n)n2

i=1ni=n(n+1)(2n+1)6

貌似是这么个道理,但是有没有证明呢?我们看到三种证法:

第一种流氓证法

其实关于这一个证法是我在第一次看到这个问题的时候的个人思考,发现网上目前没有看见有这种理解方法,所以讲一下。主要的思想就是,对于任意 k ,总是有方法构造一个 k+1 次多项式函数满足条件。我们设 f(x)=i=1xik ,那么有 f(x+1)f(x)=(x+1)k 。我们对于 f(x+1)f(x) 展开来看:

f(x+1)f(x)=(i=0k+1ai(x+1)i)(i=0k+1aixi)

我们对 (x+1)i 使用二项式定理,

=i=0k+1ai((j=0iCijxj)xi)

也就是说,对于 xi 项,它的系数就是:

bi=(j=ik+1aiCji)ai

我们知道,f(x+1)f(x)=(x+1)k=i=0kCkixi ,所以有

(j=ik+1aiCji)ai=Cki

这就是我们需要构造的,满足条件的那对系数 a 。但是怎么说明这样一定会有构造方案呢?

首先,在上面的公式中, i 取任何值的时候, j=i 这一项会被抵消,这是显然的:

Cki=j=i+1k+1aiCji

我们考虑 i=k 的时候的取值,这个取值只根 j>ij 有关系,

08

az这个坑先放着

posted @   Diavolo-Kuang  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示