数论 拉格朗日插值法(Lagrange Interpolation Polynomial)
Hongse_Fox · · 个人记录
零.简介
历史
在数值分析中,拉格朗日插值法是以法国十八世纪数学家约瑟夫·路易斯·拉格朗日命名的一种多项式插值方法。许多实际问题中都用函数来表示某种内在联系或规律,而不少函数都只能通过实验和观测来了解。如对实践中的某个物理量进行观测,在若干个不同的地方得到相应的观测值,拉格朗日插值法可以找到一个多项式,其恰好在各个观测的点取到观测到的值。这样的多项式称为拉格朗日(插值)多项式。数学上来说,拉格朗日插值法可以给出一个恰好穿过二维平面上若干个已知点的多项式函数。拉格朗日插值法最早被英国数学家爱德华·华林于1779年发现,不久后(1783年)由莱昂哈德·欧拉再次发现。1795年,拉格朗日在其著作《师范学校数学基础教程》中发表了这个插值方法,从此他的名字就和这个方法联系在一起。[1]
概念
一般地,若已知
任意一个实数
我们将上面
定理: 满足插值条件的、次数不超过
用人话转述
如果我们知道一条函数
那么我们就可以唯一地确定一条次数不超过
并且可以用
而这个方法就被称为拉格朗日插值法
一.拉格朗日插值法
1.思路
我们令
在拉格朗日插值法中我们把最终得到的函数
我们把这些部分记作
试想如果我们让
那对应的函数是不是就是我们要求的函数了呢
从
那我们从
先不妨假设
根据
再把
所以
所以
推广到一般的情况就是
那么新得到的函数
在这里举一个例子
代
2.例题\& 相当于模板
【模板】拉格朗日插值
【题目描述】
由小学知识可知
现在,给定这
【输入格式】
第一行两个整数
接下来
【输出格式】
一行一个整数,表示
样例输入
3 100
1 4
2 9
3 16
样例输出
10201
正如题目所言是一道模板题
我们就
code
#include<cstdio>
#include<cctype>
const int mod=998244353;
inline int R(){
int r=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) r=(r<<1)+(r<<3)+(c^48),c=getchar();
return r;
}
int n,k;
long long x[2005],y[2005];
inline long long inv(long long a){//求逆元
int k=mod-2;
long long node=a,ans=1;
while(k){
if(k&1) ans=(node*ans)%mod;
node=(node*node)%mod;
k>>=1;
}
return ans;
}
int main(){
n=R();k=R();
for(register int i=1;i<=n;i++){
x[i]=R();y[i]=R();
}
long long ans=0;
for(register int i=1;i<=n;i++){
long long sum=y[i];//sum即是h_i
for(register int j=1;j<=n;j++){
if(j==i) continue;
sum=(sum*(k-x[j]))%mod;
sum=(sum*inv(x[i]-x[j]))%mod;
}
ans+=sum;ans=(ans+mod)%mod;
}
printf("%lld\n",ans);
return 0;
}
二.重心拉格朗日插值
再看一遍这个式子就发现
在程序中我们重复计算了很多次
我们就新设一个参数
我们就把式子中的
再把只和
于是式子就变成了
看下这个式子是不是简洁多了
于是我们每次只求出
在这种情况下上面的模板题代码就是
#include<cstdio>
#include<cctype>
const int mod=998244353;
inline int R(){
int r=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) r=(r<<1)+(r<<3)+(c^48),c=getchar();
return r;
}
inline long long fastm(int a,int k){
long long ans=1,node=a;
while(k){
if(k&1) ans=(ans*node)%mod;
node=(node*node)%mod;
k>>=1;
}
return ans;
}
int n,k;
int x[2005],y[2005];
long long g=1,t[2005],ans;
int main(){
n=R();k=R();
for(register int i=1;i<=n;i++){
x[i]=R();y[i]=R();
g=g*(k-x[i])%mod;
}
for(register int i=1;i<=n;i++){
t[i]=y[i];
for(register int j=1;j<=n;j++){
if(i==j) continue;
t[i]=(t[i]*fastm(x[i]-x[j],mod-2))%mod;
}
}
for(register int i=1;i<=n;i++){
ans+=(t[i]*fastm(k-x[i],mod-2))%mod;
ans%=mod;
}
printf("%lld\n",(ans*g%mod+mod)%mod);
return 0;
}
三.总结
拉格朗日插值法真的是一个很神奇的东西
这个方法启示我们没有最难的问题,只有更巧妙的构造
我们可以通过它找出题目中涉及到未知的高阶函数
当然我们很多时候我们也没办法确定一个问题是否是一条高阶函数
更多时候会在式子题会有应用
Finally,谢谢大家
推荐习题
参考资料
[1] .拉格朗日插值法[OL].百度百科.2020-08-21/2021-4-30.