其他分享
首页 > 其他分享> > 【数学】拉格朗日插值法

【数学】拉格朗日插值法

作者:互联网

定义

image-20200908004924165

对某个多项式函数,已知有

细节

拉格朗日插值法的 \(k\) 的取值很容易让人迷惑,本文中所有的公式、代码都用 \(k\) 表示多项式的最高次数,也就是不超过 \(k\) 次的多项式

  1. 插值一个 \(k\) 次多项式函数需要 \(k+1\) 个点。

  2. \(k\) 次多项式的部分和,是一个 \(k+1\) 次多项式,需要 \(k+2\) 个点。

代码

时间复杂度: \(O(k^2)\) 预处理拉格朗日多项式的分母,然后用前缀积和后缀积 \(O(k)\) 求解出分子。

typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 1000 + 5;

int y[MAXN], d[MAXN], pu[MAXN], su[MAXN];

int pow_mod(ll x, int n) {
    ll res = 1LL;
    while(n) {
        if(n & 1) {
            res *= x;
            if(res >= MOD)
                res %= MOD;
        }
        x *= x;
        if(x >= MOD)
            x %= MOD;
        n >>= 1;
    }
    return (int)res;
}

void init_lagerange(int k) {
    for(int i = 1; i <= k + 1; ++i) {
        ll res = 1LL;
        for(int j = 1; j <= k + 1; ++j) {
            if(i == j)
                continue;
            int tmp = i - j;
            if(tmp < 0)
                tmp += MOD;
            res *= tmp;
            if(res >= MOD)
                res %= MOD;
        }
        d[i] = pow_mod(res, MOD - 2);
    }
}

int lagerange(int *y, int k, int xx) {
    if(xx <= k + 1)
        return y[xx];
    for(int i = 1; i <= k + 1; ++i) {
        pu[i] = xx - i;
        if(pu[i] < 0)
            pu[i] += MOD;
    }
    su[k + 2] = 1LL;
    for(int i = k + 1; i >= 1; --i) {
        ll tmp = 1LL * su[i + 1] * pu[i];
        if(tmp >= MOD)
            tmp %= MOD;
        su[i] = (int)tmp;
    }
    pu[0] = 1LL;
    for(int i = 1; i <= k + 1; ++i) {
        ll tmp = 1LL * pu[i - 1] * pu[i];
        if(tmp >= MOD)
            tmp %= MOD;
        pu[i] = (int)tmp;
    }
    int yy = 0;
    for(int i = 1; i <= k + 1; ++i) {
        ll tmp = 1LL * pu[i - 1] * su[i + 1];
        if(tmp >= MOD)
            tmp %= MOD;
        tmp *= d[i];
        if(tmp >= MOD)
            tmp %= MOD;
        tmp *= y[i];
        if(tmp >= MOD)
            tmp %= MOD;
        yy += (int)tmp;
        if(yy >= MOD)
            yy -= MOD;
    }
    return yy;
}

拓展

重心拉格朗日插值法

每次新增一个点,得到一个新的函数,O(1)。

特殊点拉格朗日插值法

例如要求 \(\sum_{i=1}^ni^{k}\) 的值,众所周知这是一个 \(k+1\) 次多项式,需要 \(k+2\) 个点。

这个值有很多种求法,有 \(O(n\log k)\) 的朴素解法(枚举+快速幂),有 \(O(k^2)\) 的系数递推法,使用上述的适用范围广泛的拉格朗日插值法,也可以做到 \(O(k^2)\) 。

但这里可以选取一些特殊的点来加速拉格朗日插值法的分母的求解。通过选取一个等差数列,那么这个分母会变得非常有规律。简单起见可以取自然数或正整数。

也就是说,假如有办法自行选取点,则拉格朗日插值法优化为特殊点拉格朗日插值法,时间复杂度为 \(O(k)\) 。(忽略求解特殊点时的复杂度)

题外话: \(\sum_{i=1}^ni^{k}\) 这个的求解确实可以是 \(O(k)\) 的,求解特殊点的复杂度朴素解法(枚举+快速幂)是 \(O(k\log k)\) ,但是有一种奇怪的解法:线性筛。因为这是一个完全积性函数,即 \((pq)^k=p^kq^k\) ,快速幂求出所有质数位置的值,然后合数拆出一个最小质因子即可,因为质数大概有 \(O(k/{\ln k})\) 个,所以总体复杂度依然是 \(O(k)\) 。

标签:tmp,拉格朗,插值法,res,int,数学,MOD
来源: https://www.cnblogs.com/purinliang/p/13670493.html