拉格朗日插值法

任意一个有穷数列都有通项公式。

给出 $n$ 个点 $(x_1,y_1),(x_2,y_2),\cdots,(x_n,y_n)$,怎么找到一个函数 $f(x)$ 同时过这 $n$ 个点?

拉格朗日的想法是这样的:构造一个开关函数

$$L_k(t)=\begin{cases} 1,&t=x_k\\ \\ 0,&t=\text{other}\ x \end{cases}$$

这个开关函数在学术上称为「插值基函数」。只有代入 $x_k$ 时开关才打开,代入其它 $x$ 时开关关闭。

进而构造

$$f(x)=y_1L_1(x)+y_2L_2(x)+\cdots+y_nL_n(x)$$

代入 $x_k$ 时,仅 $y_k$ 后面的开关是开着的,其余皆关闭,所以函数值为 $y_k$,即 $\forall k,f(x_k)=y_k$。这就是我们想要的函数。

问题就变成了如何构造开关函数。


容易发现 $\forall k,t=x_k,$

$$\prod (t-x_i)=(t-x_1)(t-x_2)\cdots(t-x_n)=0$$

把 $(t-x_k)$ 这一项拿走,得到

$$\prod_{i\not=k}(t-x_i)=\begin{cases} \not=0,&t=x_k\\ \\ 0,&t=\text{other}\ x \end{cases}$$

为使 $t=x_k$ 时得到 $1$,再作以下改变

$$\prod_{i\not=k}\frac{t-x_i}{x_k-x_i}=\begin{cases} 1,&t=x_k\\ \\ 0,&t=\text{other}\ x \end{cases}$$

于是我们成功地构造出了开关函数

$$L_k(t)=\prod_{i\not=k}\frac{t-x_i}{x_k-x_i}$$

模板 #

#include <iostream>
#include <vector>

using namespace std;

double f(vector<double>& x, vector<double>& y, double t) {
    double result = 0.0;
    int n = x.size();
    
    for (int i = 0; i < n; i ++) {
        double term = y[i];
        for (int j = 0; j < n; j ++) {
            if (j != i) {
                term *= (t - x[j]) / (x[i] - x[j]);
            }
        }
        result += term;
    }
    
    return result;
}

int main() {
    vector<double> x = {1, 2, 3, 4, 5};
    vector<double> y = {1, 4, 9, 16, 25};

    double t;
    while (cin >> t) {
        cout << f(x, y, t) << endl;
    }

    return 0;
}