首页 > 技术文章 > 洛谷P4781 【模板】拉格朗日插值

hehe54321 2019-03-14 21:33 原文

https://www.luogu.org/problemnew/show/P4781

https://www.cnblogs.com/zj75211/p/8028165.html讲的很清楚了

(这里有隐藏的转载&备份)

今天做题需要用到插值法, 就简单入门了一下, 同时记了一点浅显的东西于此。

定理

对于给定的 N+1个 (x坐标不相等的二维平面上的)点,存在唯一一个最高项次数不超过 N 的多项式 Y=P(X)其图像经过这N+1个点。

作用

求出的插值函数P(X)用于估计原函数F[X]。

(但如果原函数可以由多项式表示(既不是一个相关函数),且告诉了图像上其最高次项次数+1个点,就可以通过插值法得出该函数F[x],即求得的插值函数P[x]=F[x])

 求法(对于给定的一组N+1个点(X0,y0),(X1,y1)......(XN,YN))

1.直接法

设出N次方程,得到N+1个线性方程,求解出多项式的系数即可。

(高斯消元哦,N3哦,很慢哦)

2.拉格朗日插值法(可以叫它构造法么?)

先构造出一组(N+1个)基函数 l0(X) , l1(X) , ...... , lN(X)

使得 li (X)只经过(Xi , 1),且在X=Xj ( j ≠ i )时,li (Xj)=0。

li(X)的构造如上。

显然,上式在 X=Xj ( j ≠ i )时,li ( Xj )=0

同时在 X=Xi 时,li(Xi)=1,即 yi li ( Xi )=yi

然后插值函数 P(X)即为 yi li ( X) 的线性相加:

P(X)=y0 l0 ( X)+y1 l1 ( X)+y2 l2 ( X)+……+yN lN ( X)

以下四个点为例:

拉格朗日基函数

P(X)即为我们所求的函数。

复杂度O(N2)

想要了解更多的话就看看这里:


拉格朗日插值

设给定点为$(x_0,y_0)$到$(x_{n-1},y_{n-1})$,求经过它们的多项式F

公式:$F(x)=\sum_{i=0}^{n-1}y_i\prod_{j=0,j\ne i}^{n-1}\frac{x-x_j}{x_i-x_j}$

对于此题,不需要真的把多项式求出来,直接在计算过程中把给定数带入求值就行($O(n^2)$)

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<vector>
 5 using namespace std;
 6 #define fi first
 7 #define se second
 8 #define mp make_pair
 9 #define pb push_back
10 typedef long long ll;
11 typedef unsigned long long ull;
12 const int md=998244353;
13 int n,K,ans;
14 int x[2011],y[2011];
15 int poww(int a,int b)
16 {
17     int ans=1;
18     for(;b;b>>=1,a=ll(a)*a%md)
19         if(b&1)
20             ans=ll(a)*ans%md;
21     return ans;
22 }
23 int main()
24 {
25     int i,j,t1,t2;
26     scanf("%d%d",&n,&K);
27     for(i=0;i<n;++i)
28         scanf("%d%d",x+i,y+i);
29     for(i=0;i<n;++i)
30     {
31         t1=t2=1;
32         for(j=0;j<n;++j)
33             if(j!=i)
34             {
35                 t1=ll(t1)*(K-x[j]+md)%md;
36                 t2=ll(t2)*(x[i]-x[j]+md)%md;
37             }
38         ans=(ans+ll(t1)*poww(t2,md-2)%md*y[i])%md;
39     }
40     printf("%d\n",ans);
41     return 0;
42 }
View Code

真的有必要求出多项式的话,可以先算出全部$(x-x_i)$的积,每次把它除以一项,来得到各个$l_i(x)$

这个“除以一项”怎么搞?就相当于模拟一个大除法

设除完$(x-x_i)$的结果为A(x),各项系数为$a_i$,除之前为B(x),各项系数为$b_i$,令$y=x_i$

则$b_{n+1}=a_n$,$b_n=a_{n-1}-a_ny$,$b_{n-1}=a_{n-2}-a_{n-1}y$,...

$a_n=b_{n+1}$,$a_{n-1}=b_n+a_ny$,$a_{n-2}=b_{n-1}+a_{n-1}y$,...

还是$O(n^2)$

当然,此题这么做要慢一点;不过这个在某些情况下有用,由于多项式求值可以O(n)完成

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<vector>
 5 using namespace std;
 6 #define fi first
 7 #define se second
 8 #define mp make_pair
 9 #define pb push_back
10 typedef long long ll;
11 typedef unsigned long long ull;
12 const int md=998244353;
13 int n,K,ans;
14 int x[2011],y[2011];
15 int an1[2011],tt1[2011],tt2[2011];
16 //tt1维护所有(x-x_i)的积,an1维护答案
17 int poww(int a,int b)
18 {
19     int ans=1;
20     for(;b;b>>=1,a=ll(a)*a%md)
21         if(b&1)
22             ans=ll(a)*ans%md;
23     return ans;
24 }
25 int main()
26 {
27     int i,j,t2;
28     scanf("%d%d",&n,&K);
29     for(i=0;i<n;++i)
30         scanf("%d%d",x+i,y+i);
31     tt1[0]=1;
32     for(i=0;i<n;++i)
33     {
34         tt1[i+1]=tt1[i];
35         for(j=i;j>=1;--j)
36             tt1[j]=(ll(tt1[j])*(md-x[i])+tt1[j-1])%md;
37         tt1[0]=ll(tt1[0])*(md-x[i])%md;
38     }
39     for(i=0;i<n;++i)
40     {
41         tt2[n-1]=tt1[n];
42         for(j=n-2;j>=0;--j)
43             tt2[j]=(tt1[j+1]+ll(tt2[j+1])*x[i])%md;
44         t2=1;
45         for(j=0;j<n;++j)
46             if(j!=i)
47                 t2=ll(t2)*(x[i]-x[j]+md)%md;
48         t2=ll(poww(t2,md-2))*y[i]%md;
49         for(j=0;j<n;++j)
50             an1[j]=(an1[j]+ll(t2)*tt2[j])%md;
51     }
52     ans=0;
53     for(j=n-1;j>=0;--j)
54         ans=(ll(ans)*K+an1[j])%md;
55     printf("%d\n",ans);
56     return 0;
57 }
View Code

重心拉格朗日插值

https://blog.csdn.net/qq_35649707/article/details/78018944

https://zh.wikipedia.org/wiki/%E6%8B%89%E6%A0%BC%E6%9C%97%E6%97%A5%E6%8F%92%E5%80%BC%E6%B3%95

设$l(x) = \prod_j(x-x_j)$

$\omega_i= \frac{1}{\prod\limits_{j\not = i}(x_i-x_j)}$

则$F(x)=l(x)\sum_i\frac{\omega _i}{x-x_i}y_i$(的确是对的,过程省略了)

这个东西的好处是,在增加一个新点的坐标时,只需要O(n)的时间来更新解,不需要$O(n^2)$重新计算整个解(具体方法先鸽着)

还有一个公式(重心拉格朗日插值(第二型))

将$g(x)=1$在对原多项式给出的所有x坐标处用以上方法插值,得到$l(x)\sum_i\frac{\omega _i}{x-x_i}=1$(并不知道怎么想出来的)

所以$F(x)=\frac{F(x)}{1}=\frac{\sum_i\frac{\omega _i}{x-x_i}y_i}{\sum_i\frac{\omega _i}{x-x_i}}$(暂时不知道有什么特别的用处)

其他资料https://www.cnblogs.com/ECJTUACM-873284962/p/6833391.html

推荐阅读