拉格朗日插值法學習筆記


拉格朗日插值法是一個根據點對求回原函數的算法,原理挺好懂的。

推薦博客:https://www.cnblogs.com/ECJTUACM-873284962/p/6833391.html

https://www.cnblogs.com/zwfymqz/p/10063039.html#_label1_0

題目集合https://blog.csdn.net/qq_35649707/article/details/78018944#bzoj2655-calc

原理和優化方法上面的大佬都講得很好。

其實主要就是這個式子:

然后暴力算這個式子的話是每求一項f(k)的時間復雜度都是n^2。

這個時間很不優秀,於是我們想辦法在一些特殊時候優化它。

在x連續的時候我們可以通過預處理的方式加快速度,式子為

 

 然后其實拉格朗日插值就上面兩個式子,具體題目的具體點對不一樣,但是插值的方法就是這樣。

 

 

然后重心拉格朗日插值法就是把朴素的式子變形,它的好處是

 

 

雖然原理好懂代碼好寫,但是做題並不好做,得看出所求函數是幾次多項式也得簡略證明一下,這要求有一定的數學素養。這一個知識點蒟蒻還是差得很多,基本上就是做一題看一題題解。主要是雖然拉格朗日插值法不難,也好寫。但是架不住我根本不知道要求的是哪個函數呀qwq qwq qwq,這樣的話初始點對都求不出來,那還插個球。。。

總的來說,一定不能有式子恐懼症,要多加訓練推式子的能力。加上這個知識點不怎么熟練,一定要回看。

 

 題目練習:

洛谷P4781

模板題:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e3+10;
const int MOD=998244353;
int n,k,x[N],y[N];

int power(LL x,LL p,LL MOD) {
    LL ret=1;
    for (;p;p>>=1) {
        if (p&1) ret=ret*x%MOD;
        x=x*x%MOD;
    }
    return (int)ret;
}

int Lagrange() {
    int ret=0;
    for (int i=1;i<=n;i++) {
        int up=1,down=1;
        for (int j=1;j<=n;j++) {
            if (i==j) continue;
            up=(LL)up*((k-x[j])%MOD+MOD)%MOD;  //分子 
            down=(LL)down*((x[i]-x[j])%MOD+MOD)%MOD;  //分母 
        }
        int tmp=(LL)y[i]*up%MOD*power(down,MOD-2,MOD)%MOD;
        ret=(ret+tmp)%MOD;
    }
    return ret;
}

int main()
{
    cin>>n>>k;
    for (int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
    cout<<Lagrange()<<endl;
    return 0;
}
View Code

 

洛谷P4593

看大佬題解,主要矛盾就是算sigma(i^m) (i=1~m+1)  這個用拉格朗日插值法可以解決。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100;
const int P=1e9+7;
int n,m;
LL jc[N],pre[N],suf[N],a[N],y[N];

void prework() {
    jc[0]=1; for (int i=1;i<=60;i++) jc[i]=(jc[i-1]*i)%P;
}

LL power(LL x,LL p,LL MOD) {
    LL ret=1;
    for (;p;p>>=1) {
        if (p&1) ret=ret*x%MOD;
        x=x*x%MOD;
    }
    return ret;
}

//拉格朗日插值計算:sigma(i^m) (i=1~m+1)  因為計算m項要用m+1個點 
LL Lagrange(int n) {
    LL ret=0;
    pre[0]=n; for (int i=1;i<=m+1;i++) pre[i]=(pre[i-1]*(n-i))%P;  //預處理分子前綴 
    suf[m+2]=1; for (int i=m+1;i;i--) suf[i]=(suf[i+1]*(n-i))%P;  //預處理分母后綴 
    for (int i=1;i<=m+1;i++) {  //m+1個點插點 
        LL up=pre[i-1]*suf[i+1]%P;
        LL down=jc[i]*jc[m+1-i]%P;
        if ((m+1-i)%2==1) down=P-down;  //注意,(m+1-i)奇數項分母取反 
        LL tmp=up*power(down,P-2,P)%P*y[i]%P;
        ret=(ret+tmp)%P;
    }
    return ret;
}

int main()
{
    prework();
    int T; cin>>T;
    while (T--) {
        scanf("%d%d",&n,&m); 
        for (int i=1;i<=m;i++) scanf("%d",&a[i]); a[++m]=++n;
        sort(a+1,a+m+1);
        for (int i=1;i<=m+2;i++) y[i]=power(i,m,P);
        for (int i=1;i<=m+2;i++) y[i]=(y[i]+y[i-1])%P; //算出(x,y)點對 
        LL ans=0;
        for (int i=1;i<=m;i++) {  //計算ans 
            for (int j=i;j<=m;j++) ans=(ans+Lagrange(a[j]-1)-Lagrange(a[j-1]))%P,ans=(ans+P)%P;
            for (int j=i+1;j<=m;j++) a[j]=a[j]-a[i]; a[i]=0;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

BZOJ-3453

慚愧的說,這題我現在還是不能完全理解為什么就能表示成K+4次多項式。但是其實能看出來的話也是直接插值就可以了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL P=1234567891;
const LL N=200+10;
LL k,a,n,d;
LL f[N],g[N];

LL power(LL x,LL p,LL MOD) {
    LL ret=1;
    for (;p;p>>=1) {
        if (p&1) ret=ret*x%MOD;
        x=x*x%MOD;
    }
    return ret;
}

//計算k項式(i,f[i])的第n項 
LL Lagrange(LL *f,LL k,LL n) {
    LL ret=0;
    for (int i=0;i<=k;i++) {
        LL up=f[i],down=1;
        for (int j=0;j<=k;j++) {
            if (i==j) continue;
            up=up*((n-j+P)%P)%P;
            down=down*((i-j+P)%P)%P;
        }
        ret=(ret+up*power(down,P-2,P)%P)%P;
    }
    return ret;
}

int main()
{
    int T; cin>>T;
    while (T--) {
        memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); 
        scanf("%lld%lld%lld%lld",&k,&a,&n,&d);
        for (int i=1;i<=k+4;i++) f[i]=power(i,k,P);
        for (int i=1;i<=k+4;i++) f[i]=(f[i-1]+f[i])%P;
        for (int i=1;i<=k+4;i++) f[i]=(f[i-1]+f[i])%P;
        for (int i=0;i<=k+4;i++) 
            g[i]=((i>0?g[i-1]:0)+Lagrange(f,k+4,a+i*d))%P;
        printf("%lld\n",Lagrange(g,k+4,n));
    }
    return 0;
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM