拉格朗日插值法


1.拉格朗日插值法的由来

​ 拉格朗日插值法究竟解决的是什么问题喃?

问题:在二维图像上给出$n$个点,如何得到一个通过这$n$个点的一条线?

​ 例如:已知以下几个点$(0,1)、(1,0)、(2,1)$,问这条曲线是什么?

显然易见对于这三个点,是可以用多项式画出这三个点的,因此我们可以假设这个二次多项式为

我们带入这三个点$(x_1,y_1),(x_2,y_2),(x_3,y_3)$

可以得到以下方程组

对于这样一个方程组,我们可以用高斯消元解出系数。

但是拉格朗日是如何思考这个问题的喃?

拉格朗日的思考:

拉格朗日从另外的角度思考了这个问题,在他看来可以通过三根二次曲线相加来达到目标。那这是怎么的三根二次曲线呢?

这三根曲线只需满足一些条件即可:

则所求曲线为:

而这样一条曲线正好可以过给的三个点

2、拉格朗日插值法的推导

对于过每一个点$(x_i,y_i)$的曲线的函数设为$y_jf_i(x_j)$

因此需要满足的条件为:

因此我们最后构造出$f_i(x_j)$即可

显而易见,以下这个函数满足条件

因此

上模板题:https://www.luogu.com.cn/problem/P4781

#include<iostream>
#include<algorithm>
#include<cmath>
#include<string.h>
using namespace std;
#define int long long
const int maxn=1e4+10;
int x[maxn];
int y[maxn];
const int mod =998244353;
int qp(int x,int n){
    int ans=1;
    while(n){
        if(n&1){
            ans=ans*x%mod;
        }
        x=x*x%mod;
        n>>=1;
    }
    return ans;
}
int inv(int x){
    return qp(x,mod-2);
}
int lagrange(int num,int n)
{
    int ans=0;
    for(int k=1;k<=n;k++){
        int s1=y[k];
        int s2=1;
        for(int i=1;i<=n;i++){
            if(i==k)continue;
            s1=s1*(num-x[i])%mod;
            s2=s2*(x[k]-x[i])%mod;
        }
        ans=(ans+s1*inv(s2))%mod;
    }
    ans=(ans%mod+mod)%mod;
    return ans;
}

signed main()
{
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>x[i]>>y[i];
    }
    cout<<lagrange(k,n)<<endl;
    return 0;
}

当然这样的时间复杂度$O(n^2)$

在有些情况下不能做到最优,因此对于一些情况我们可以进行优化

3、横坐标为连续的自然数(1,2,3….)时,可以进行优化

由于横坐标为连续的自然数:

故插值公式可以优化为:

这样一来,可以通过预处理后缀积、前缀积然后代入这个式子,所以这种情况时间复杂度为$O(n)$

例题:求自然数的k次幂的和

相同题意的例题:https://codeforces.com/problemset/problem/622/F

代码:

#include <iostream>
#include <algorithm>
#include <cmath>
#include <string.h>
#define int long long
using namespace std;
const int maxn = 1e6 + 10;
const int mod = 1e9 + 7;
int x[maxn];
int y[maxn];
int qp(int a, int b)
{
    int ans = 1;
    while (b)
    {
        if (b & 1)
        {
            ans = ans * a % mod;
        }
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
int inv(int x)
{
    return qp(x, mod - 2);
}
int fac[maxn];
void init_fac()
{
    fac[0] = 1;
    fac[1] = 1;
    for (int i = 2; i < maxn - 5; i++)
    {
        fac[i] = fac[i - 1] * i % mod;
    }
}
int back;
int lagrange(int n, int k)
{
    int ans = 0;
    for (int i = 1; i <= k + 2; i++)
    {
        int s1 = back * y[i] % mod;
        int s2 = ((k + 2 - i) % 2 == 0 ? 1 : -1) * (n - i);
        s2 = s2 * fac[i - 1] % mod;
        s2 = s2 * fac[k + 2 - i] % mod;
        ans = (ans + s1 * inv(s2) % mod) % mod;
    }
    ans = (ans % mod + mod) % mod;
    return ans;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, k;
    cin >> n >> k;
    init_fac();
    for (int i = 1; i <= k + 2; i++)
    {
        x[i] = i;
        y[i] = (y[i - 1] + qp(i, k)) % mod;
    }
    if (n <= k+2)
    {
        cout << y[n] << endl;
    }
    else
    {
        back = 1;
        for (int j = 1; j <= k + 2; j++)
        {
            back = back * (n - j) % mod;
        }
        cout << lagrange(n, k) << endl;
    }
    return 0;
}

文章作者: fatzard
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 fatzard !
评论
  目录
本站总访问量