问题: 已知点
(
x
0
,
y
0
)
,
(
x
1
,
y
1
)
,
.
.
.
,
(
x
n
,
y
n
)
(x_0,y_0),(x_1,y_1),...,(x_n,y_n)
(x0,y0),(x1,y1),...,(xn,yn),求一个经过这n个点的n次多项式。
一种做法是设出这个多项式,然后将n个点带入得到n个方程,然后用高斯消元在
O
(
n
3
)
O(n^3)
O(n3)的时间求出。
但是复杂度太高了,所以就有了拉格朗日插值这种优秀算法。
注意,部分定义不保证严谨性,但是不影响拉格朗日插值法的应用
设
l
j
(
x
)
l_j(x)
lj(x)为
j
j
j的拉格朗日基本多项式,表示一个在
x
=
x
j
x=x_j
x=xj处等于1且在
x
=
x
i
(
i
≠
j
)
x=x_i(i\not=j)
x=xi(i=j)处等于0的多项式。
这个多项式很好构造,即
∏
i
=
0
,
i
≠
j
n
x
−
x
i
x
j
−
x
i
\prod_{i=0,i\not=j}^n\frac{x-x_i}{x_j-x_i}
∏i=0,i=jnxj−xix−xi
那么要满足所有点,只需将所有拉格朗日基本多项式乘上
y
j
y_j
yj再加起来就是最终的n次多项式。
即
f
(
x
)
=
∑
j
=
0
n
l
j
(
x
)
y
i
f(x)=\sum_{j=0}^nl_j(x)y_i
f(x)=∑j=0nlj(x)yi
但是直接做好像还是
n
3
n^3
n3?
考虑进行优化(叫重心拉格朗日插值法)
我们设
w
j
=
∏
i
=
0
,
i
≠
j
n
1
x
j
−
x
i
,
g
(
x
)
=
∏
i
=
0
n
+
1
x
−
x
i
w_j=\prod_{i=0,i\not=j}^n\frac{1}{x_j-x_i},g(x)=\prod_{i=0}^{n+1}x-x_i
wj=∏i=0,i=jnxj−xi1,g(x)=∏i=0n+1x−xi
我们先在插值前预处理出
g
(
x
)
g(x)
g(x),然后在枚举
j
j
j时每次
O
(
n
)
O(n)
O(n)计算
w
w
w,即
l
j
(
x
)
l_j(x)
lj(x)的分母部分,然后
O
(
n
)
O(n)
O(n)用
g
(
x
)
g(x)
g(x)暴力除以
x
−
x
j
x-x_j
x−xj得到
l
j
(
x
)
l_j(x)
lj(x)的分子部分,即可在
O
(
n
)
O(n)
O(n)的时间内求出
l
j
(
x
)
l_j(x)
lj(x),加上枚举j的复杂度,总复杂度为
O
(
n
2
)
O(n^2)
O(n2)
注意如果是单点求值,则只需要直接替换
x
x
x即可,时间复杂度仍为
O
(
n
2
)
O(n^2)
O(n2)
代码:
void lagrange(int *f,int n){
memset(l,0,sizeof(l));
l[0]=1;
f[0]=0;
for(int i=0;i<=n;i++){
f[i]=0;
for(int j=i+1;j>0;j--)
l[j]=Minus(l[j-1],1ll*l[j]*x[i]%mod);
l[0]=Minus(0,1ll*l[0]*x[i]%mod);
}//求g
for(int i=0;i<=n;i++){
int w=y[i];
for(int j=0;j<=n;j++)
if(i!=j)
w=1ll*w*inv[x[j]>x[i]][abs(x[j]-x[i])]%mod;//inv[0/1][j]分别表示1~n和mod-n~mod-1的逆元,如在实数范围则可直接取倒数
t[n]=1;
f[n]=(f[n]+1ll*t[n]*w)%mod;
for(int j=n-1;j>=0;j--){
t[j]=(l[j+1]+1ll*t[j+1]*x[i])%mod;//暴力除法
f[j]=(f[j]+1ll*t[j]*w)%mod;//累加答案
}
}
return;
}
也可以利用分治和多项式相关知识得到一个 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)的快速插值算法,但比较复杂,博主写快速插值距今已久,刚中考完回归复习拉格朗日插值,故目前能力不足以讲它。
接下来有一个技巧
在部分求解多项式的题目中,
x
i
x_i
xi是连续的(相差1),这时如果需要进行单点求值,我们可以将拉格朗日插值公式稍微变形之后得到一种
O
(
n
)
O(n)
O(n)的算法。
如
x
∈
[
0
,
n
]
x\in[0,n]
x∈[0,n]
f
(
x
)
=
∑
j
=
0
n
f
(
j
)
∏
i
=
0
,
i
≠
j
n
x
−
i
j
−
i
f(x)=\sum_{j=0}^nf(j)\prod_{i=0,i\not=j}^n\frac{x-i}{j-i}
f(x)=j=0∑nf(j)i=0,i=j∏nj−ix−i
=
∑
j
=
0
n
f
(
j
)
x
(
x
−
1
)
(
x
−
2
)
.
.
.
(
x
−
n
)
j
(
j
−
1
)
(
j
−
2
)
.
.
.
⋅
2
⋅
1
⋅
(
−
1
)
⋅
(
−
2
)
⋅
.
.
.
(
j
−
n
+
2
)
(
j
−
n
+
1
)
(
j
−
n
)
=\sum_{j=0}^nf(j)\frac{x(x-1)(x-2)...(x-n)}{j(j-1)(j-2)...\cdot2\cdot1\cdot(-1)\cdot(-2)\cdot...(j-n+2)(j-n+1)(j-n)}
=j=0∑nf(j)j(j−1)(j−2)...⋅2⋅1⋅(−1)⋅(−2)⋅...(j−n+2)(j−n+1)(j−n)x(x−1)(x−2)...(x−n)
=
∑
j
=
0
n
f
(
j
)
∏
i
=
0
n
x
−
i
(
−
1
)
n
−
j
(
j
−
1
)
!
(
n
−
j
)
!
=\sum_{j=0}^{n}f(j)\frac{\prod_{i=0}^nx-i}{(-1)^{n-j}(j-1)!(n-j)!}
=j=0∑nf(j)(−1)n−j(j−1)!(n−j)!∏i=0nx−i
=
∑
j
=
0
n
(
−
1
)
n
−
j
f
(
j
)
∏
i
=
0
n
x
−
i
(
j
−
1
)
!
(
n
−
j
)
!
=\sum_{j=0}^{n}(-1)^{n-j}f(j)\frac{\prod_{i=0}^nx-i}{(j-1)!(n-j)!}
=j=0∑n(−1)n−jf(j)(j−1)!(n−j)!∏i=0nx−i
预处理一下阶乘和
∏
i
=
0
n
x
−
i
\prod_{i=0}^nx-i
∏i=0nx−i的值,然后就可以
O
(
n
)
O(n)
O(n)求解了。
如果
x
∈
[
d
,
d
+
n
]
]
x\in[d,d+n]]
x∈[d,d+n]](d为任意实数),则可令
x
∈
[
0
,
n
]
x\in[0,n]
x∈[0,n]求出f,然后将函数在水平方向上平移即可。
这个技巧可以用于很多构造点值求解多项式的题,最经典的一种是求单点自然数幂求和
(
∑
i
=
1
n
i
k
)
(\sum_{i=1}^ni^k)
(∑i=1nik)
经过推导可知这是一个
k
+
1
k+1
k+1次多项式(推导过程需用到斯特林数(或伯努利数?),这里放一下TJY大佬利用斯特林数的一种推导方法)
于是我们就可以枚举0到k求值,然后得到k+1个横坐标连续的点值,再把n代入用刚刚的技巧
O
(
n
)
O(n)
O(n)单点求值即可。