拉格朗日插值法 学习小结

基本定理

我们中学就知道:两点确定一直线。对于这一点的拓展:n点可以确定n-1阶的多项式。
关于证明,可以联想待定系数法:
设多项式为 f ( x ) = a 0 + a 1 x n + x 2 x 2 + … … + a n 1 x n − 1 f(x)=a_0+a_1x^n+x_2x^2+……+a_{n_1}x^{n-1} f(x)=a0+a1xn+x2x2++an1xn1
由此可知,我们只需要n个独立方程就能确定此多项式。
那么,假设说我们要求这个多项式的某点的取值 f ( k ) f(k) f(k),最直接的想法是确定待定系数,再来求 f ( k ) f(k) f(k)。也就是解方程组:
{ a 0 + a 1 x 1 + a 2 x 1 n + … … a n x 1 n = b 1 a 0 + a 1 x 2 + a 2 x 2 n + … … a n x 2 n = b 2 … … a 0 + a 1 x n + a 2 x n n + … … a n x n n = b 3 \begin{cases} a_0+a_1x_1+a_2x_1^n+……a_nx_1^n=b_1\\ a_0+a_1x_2+a_2x_2^n+……a_nx_2^n=b_2\\ ……\\ a_0+a_1x_n+a_2x_n^n+……a_nx_n^n=b_3 \end{cases} a0+a1x1+a2x1n+anx1n=b1a0+a1x2+a2x2n+anx2n=b2a0+a1xn+a2xnn+anxnn=b3
写成算法就是用高斯消元,复杂度为 O ( n 3 ) O(n^3) O(n3)
那么如果只求某个点的取值呢?拉格朗日大佬给我们一个新的方法:
f ( k ) = ∑ i = 1 n ( y i ∏ j ≠ i k − x j x i − x j ) f(k)=\sum_{i=1}^n(y_i\prod_{j\neq i}{k-x_j\over x_i-x_j}) f(k)=i=1n(yij=ixixjkxj)
证明可以看下oiwiki上的,类似于crt那样构造出解的方法。
这个解法的复杂度为 O ( n 2 ) O(n^2) O(n2),有些情况下还能继续优化。
模板题:https://www.luogu.com.cn/problem/P4781
直接套式子写就行了。

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int mod=998244353;
typedef long long ll;
template<typename T>
void read(T&x){
	x=0;
	int f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f*=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=x*10+(ch-'0');
		ch=getchar();
	}x*=f;
}
template<typename T>
void write(T x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
//===============================================
const int maxn=2e3+10;
int x[maxn],y[maxn];
int n,k;
ll ksm(ll a,ll n){
	ll res=1;
	while(n){
		if(n&1)res=res*a%mod;
		a=a*a%mod;
		n>>=1;
	}
	return res;
}
int solve(int x[],int y[],int n,int k){
	int res=0;
	for(int i=1;i<=n;++i){
		int up=y[i],down=1;
		for(int j=1;j<=n;++j){
			if(i==j)continue;
			up=1ll*up*(k-x[j])%mod;up=(up%mod+mod)%mod;
			down=1ll*down*(x[i]-x[j])%mod;down=(down%mod+mod)%mod;
		}
		res=(res+1ll*up*ksm(down,mod-2)%mod)%mod;
		res=(res%mod+mod)%mod;
	}
	return res;
}

int main(){
	//freopen("in.txt","r",stdin);
	read(n),read(k);
	for(int i=1;i<=n;++i){
		read(x[i]);read(y[i]);
	}
	cout<<solve(x,y,n,k)<<endl;
	return 0;
}

当x取值为连续时

观察下原式:
f ( k ) = ∑ i = 1 n ( y i ∏ j ≠ i k − x j x i − x j ) f(k)=\sum_{i=1}^n(y_i\prod_{j\neq i}{k-x_j\over x_i-x_j}) f(k)=i=1n(yij=ixixjkxj)
如果x的取值为0,1,2,3,……,n-1,那么,分子和分母就都是阶乘形式了(可以找个样例手写下),那么,我们就可以我们可以预处理分子分母后, O ( n ) O(n) O(n)构造解。具体可以看代码。
模板题:https://nanti.jisuanke.com/t/40254

#include <bits/stdc++.h>
using namespace std;
template<typename T>
void read(T&x){
    x=0;
    int f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f*=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        x=x*10+(ch-'0');
        ch=getchar();
    }x*=f;
}
template<typename T>
void write(T x){
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
//=========================================================
typedef long long ll;
#define int ll
const int mod=9999991;
const int maxn=1e3+10;
int t,n,m;
int y[maxn];
int f[maxn],finv[maxn];
inline int sub(int x,int y){
    return x-y>=0?x-y:x-y+mod;
}

inline int add(int x,int y){
    return x+y<mod?x+y:x+y-mod;
}

inline ll mul(int x,int y){
    return (1ll*x*y%mod+mod)%mod;
}

inline int ksm(int a,int n){
    int res=1;
    while(n){
        if(n&1)res=1ll*res*a%mod;
        a=1ll*a*a%mod;
        n>>=1;
    }
    return res;
}

inline void init(){
    f[0]=1;
    for(int i=1;i<maxn;++i)f[i]=1ll*f[i-1]*i%mod;
    finv[maxn-1]=ksm(f[maxn-1],mod-2);
    for(int i=maxn-2;i>=0;--i){
        finv[i]=1ll*finv[i+1]*(i+1)%mod;
    }
}

int pre[maxn],suf[maxn];
int calc(int y[],int k,int n){
    if(k<=n)return y[k];
    pre[0]=1;suf[n]=1;
    //预处理分子
    for(int i=1;i<=n;++i)pre[i]=1ll*pre[i-1]*(k-(i-1))%mod;
    for(int i=n-1;i>=0;--i)suf[i]=1ll*suf[i+1]*(k-(i+1))%mod;
    int res=0;
    for(int i=0;i<=n;++i){
        //int up=mul(y[i],mul(pre[i],suf[i]));
        //int down=mul(finv[i],finv[n-i]);
        //int fu=((n-i&1)?-1:1);
        //res=(res+1ll*fu*res*pre[i]%mod*suf[i]%mod*finv[i]%mod*finv[n-i]%mod);
        //res=(res%mod+mod)%mod;
        //res=(res+((fu*up)%mod*down)%mod)%mod;
        //res=(res%mod+mod)%mod;
        int fu=(n-i)&1?-1:1;
        res +=1ll*y[i]*fu*pre[i]%mod * suf[i]%mod * finv[i]%mod * finv[n-i]%mod;
        res=(res%mod+mod)%mod;
        //if(!flag)cerr<<f[i]<<" "<<fu<<" "<<pre[i]<<" "<<suf[i]<<" "<<finv[i]<<" "<<finv[n-i]<<endl;
    }
    return res;
}

inline void prelude(){
    y[n+1]=calc(y,n+1,n);
    //cerr<<"**"<<y[n+1]<<endl;
    for(int i=1;i<=n+1;++i)y[i]=add(y[i-1],y[i]);
    //y[n+1]=add(y[n+1],y[n]);
}

signed main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    init();
    read(t);
    while(t--){
        read(n),read(m);
        for(int i=0;i<=n;++i)read(y[i]);
        prelude();//预处理前缀和
        //cerr<<y[n+1]<<endl;
        while(m--){
            int L,R;
            read(L),read(R);
            write(sub(calc(y,R,n+1),calc(y,L-1,n+1)));//差分求部分和
            putchar('\n');
        }
    }
    return 0;
}

练习题:
https://www.luogu.com.cn/problem/P4593
https://ac.nowcoder.com/acm/problem/218514

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
拉格朗日插值法是一种常用的数值插值方法,用于根据给定的数据点构造一个多项式函数。这个多项式函数可以通过给定数据点的函数值来近似地表示原函数。MATLAB是一种功能强大的数值计算和科学计算软件,可以用于实现拉格朗日插值法。 在MATLAB中,我们可以使用多种方法实现拉格朗日插值法,其中一种方法是通过定义拉格朗日多项式的形式来实现。拉格朗日多项式是一种特殊的插值多项式,可以通过给定数据点的函数值来计算插值多项式的系数。 在《拉格朗日插值法MATLAB实现(附代码、实例、详解).pdf》中,作者提供了拉格朗日插值法的MATLAB实现,包括代码、实例和详细解释。你可以参考这个文件来实现拉格朗日插值法的MATLAB代码。 另外,《形如上式的插值多项式便称为拉格朗日(Lagrange)插值多项式。线性插值和抛物线插值只是拉格朗日插值的特殊情况。》这句引用说明了拉格朗日插值是一种通用的插值方法,线性插值和抛物线插值只是拉格朗日插值的特殊情况。 根据《代码部分由于线性插值和抛物线插值是拉格朗日的特殊情况,所以小编在编写的时候,为了让看起来没有重复,选择了直接按照运算形式编写代码。》这句引用,可以得出在代码实现中,为了避免重复,作者直接按照运算形式编写了代码。 综上所述,在MATLAB中实现拉格朗日插值法,你可以参考《拉格朗日插值法MATLAB实现(附代码、实例、详解).pdf》中提供的代码、实例和详细解释。此外,注意到线性插值和抛物线插值是拉格朗日插值的特殊情况,你可以根据需要调整代码以适应不同的插值情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值