拉格朗日插值法最先被英国数学家爱德华·华林于1779年发现,不久后(1783年)由莱昂哈德·欧拉再次发现。1795年,拉格朗日在其著做《师范学校数学基础教程》中发表了这个插值方法,今后他的名字就和这个方法联系在一块儿。html
因此我把拉格朗日插值法叫作爱德华插值法ios
那么这个东西能够作什么呢?
知道一个 \(N\) 次多项式函数上的 \(N+1\) 个不一样的点就能够求得这个 \(N\) 次多项式。
或者说给出 \(N+1\) 个点 \((x_i, y_i)\)(保证 \(x_i\) 互不相同),要求找出一个过全部点的多项式函数 \(f(x)\) 。
这个过 \(N+1\) 个点的 \(N\) 次多项式是惟一的。N+1点肯定一条直线
时间复杂度 \(O(n^2)\) 。函数
拉格朗日插值法这个名字听起来很厉害。可是原理简单得让人吃惊。
对于每一个点,咱们尝试找出一个函数 \(f_i(x)\),使得 \(f_i(x_i) = y_i\),而且对于其余的全部横坐标 \(x_j(j\neq i)\) 有 \(f_i(x_j) = 0\)。那么把 \(n\) 个 \(f_i(x)\) 加起来就能获得要求的函数 \(f(x)\)。
那么如何找到函数 \(f_i(x)\) 呢?
\[f_i(x) = y_i∏_{j\neq i}\frac{x − x_j}{x_i − x_j}\]
发现当 \(x=x_i\) 的时候连乘的每一项都是1因此 \(∏_{j\neq i}\frac{x − x_j}{x_i − x_j}=1\),当 \(x=x_j\) 的时候连乘至少有一项为0 \(∏_{j\neq i}\frac{x − x_j}{x_i − x_j}=0\)。
进而得出要求的函数 \(f(x)\) 为:
\[f(x) = \sum _{i=1}^{n}y_i ∏_{j\neq i}\frac{x − x_j}{x_i − x_j}\]
而后呢?就结束了呀!优化
若是你要求函数某一个点的值,直接把 \(x\) 代进去算,复杂度 \(O(n^2)\) 。
若是要求多项式系数,直接暴力算是 \(O(n^3)\) 的。
可是考虑预处理出一个多项式 \(P(x) = ∏_{i=1}^{n}(x − x_i)\),每次用 \(P(x)\) 除一下 \(x − x_i\) 就能够获得要求的乘积了。时间复杂度优化到了 \(O(n^2)\)。spa
模板题code
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; #define int long long const int N=3000;; const int mod=998244353; int n,k,x[N],y[N],ans; int ksm(int x,int b){ int tmp=1; while(b){ if(b&1)tmp=tmp*x%mod; b>>=1; x=x*x%mod; } return tmp; } int read(){ int sum=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return sum*f; } signed main(){ n=read(),k=read(); for(int i=1;i<=n;i++)x[i]=read()%mod,y[i]=read()%mod; for(int i=1;i<=n;i++){ int tmp=y[i]; for(int j=1;j<=n;j++){ if(i==j)continue; tmp=tmp*((k-x[j]+mod)%mod)%mod*ksm((x[i]-x[j]+mod)%mod,mod-2)%mod; } ans=(ans+tmp)%mod; } printf("%lld",ans); return 0; }
若是以为我讲得很差,佷正常!
而后推荐此博客。htm