对于
A
(
x
)
=
a
0
+
a
1
x
+
a
2
x
2
+
.
.
.
+
a
n
−
1
x
n
−
1
A(x)=a_0+a_1x+a_2x^2+...+a_{n-1}x^{n-1}
A(x)=a0+a1x+a2x2+...+an−1xn−1,
B
(
x
)
=
b
0
+
b
1
x
+
b
2
x
2
+
.
.
.
+
b
m
−
1
x
m
−
1
B(x)=b_0+b_1x+b_2x^2+...+b_{m-1}x^{m-1}
B(x)=b0+b1x+b2x2+...+bm−1xm−1
求
C
(
x
)
=
A
(
x
)
∗
B
(
x
)
C(x)=A(x)*B(x)
C(x)=A(x)∗B(x)
暴力
我们显然可以枚举A的每一次项,B的每一次项。
然后一一乘起来,就是答案了!
c
i
=
∑
j
=
0
j
=
i
a
j
∗
b
i
−
j
c_i=\sum_{j=0}^{j=i}a_j*b_{i-j}
ci=j=0∑j=iaj∗bi−j
但是这样子做的时间复杂度
O
(
n
∗
m
)
O(n*m)
O(n∗m)的,显然很慢!
所以,我们要想办法优化
点值与插值
在介绍更优的算法之前,就让我们先了解另一个多项式的表示方式
在通常情况下,我们是用“系数表示法”,也就是刚才的形如:
A
(
x
)
=
a
0
+
a
1
x
+
a
2
x
2
+
.
.
.
+
a
n
−
1
x
n
−
1
A(x)=a_0+a_1x+a_2x^2+...+a_{n-1}x^{n-1}
A(x)=a0+a1x+a2x2+...+an−1xn−1的形式
但其实,还有另一种表示法,叫做“点值表示法”。
对于一个最高次数为
n
−
1
n-1
n−1的多项式,我们可以选
n
n
n个不同的数
(
x
0
,
x
1
,
.
.
.
,
x
n
−
1
)
(x_0,x_1,...,x_{n-1})
(x0,x1,...,xn−1)代入次多项式,得出
n
n
n个结果
(
y
0
,
y
1
,
.
.
.
,
y
n
−
1
)
(y_0,y_1,...,y_{n-1})
(y0,y1,...,yn−1)
可以证明,只要给出这
2
∗
n
2*n
2∗n个数,
x
x
x与
y
y
y,就可以得出有且仅有一个多项式
证明,考虑变成矩阵乘法:
只要证明“x矩阵”是可逆的,就可以用
a
=
y
∗
x
−
1
a=y*x^{-1}
a=y∗x−1来得出解。
我们把x矩阵变成范德蒙德行列式,可以通过证明得出右图的等式(数学归纳法)
我们知道这是不为0的,因为这些数两两不相同。
所以x矩阵是可逆的,a也就是唯一的
而我们把点值变回原多项式的操作叫做插值
瓶颈
既然我们只要知道了点值与插值,我们就可以把
A
(
x
)
A(x)
A(x)与
B
(
x
)
B(x)
B(x)用相同的
x
x
x带入,然后得出两组不同的
y
y
y,再把这两组
y
y
y一一乘起来,最后用插值求出
C
(
x
)
C(x)
C(x)就行了。
但很可惜的是,朴素的点值和插值不管怎么样,似乎都是
O
(
n
2
)
O(n^2)
O(n2)的,这样子还不如直接暴力呢!!!(说那么多有什么用?)
但不要就此放弃,就在这时,傅里叶站了出来,他找到了突破口。
突破口
他提出了一个关键的思想:如果把点值时取的
x
x
x特殊化,会不会有什么新发现呢?
顺着这个思想,他找到了n次单位复根。
n次单位复根
这个名词听起来很高深,实际也挺高深的。
说白了,就是如果
x
n
=
1
x^n=1
xn=1,它就是n次单位复根。
在实数数域内,可能符合条件的只有1和-1,而且-1得在偶数次方的情况下才成立。
但如果推广到复数,满足条件的就有n个数了。
首先来简单地讲一下复数是什么。
其实,实数已经包含了所有存在的数,但就在这时,人们发现又有一类数,它们虽然不存在,但在解决一些数学问题中有重要的作用,这类数的核心就是——
−
1
\sqrt {-1}
−1,也就是
i
i
i。
(
a
+
b
i
)
∗
(
c
+
d
i
)
=
(
a
c
−
b
d
)
+
(
a
d
+
b
c
)
i
(a+bi)*(c+di)=(ac-bd)+(ad+bc)i
(a+bi)∗(c+di)=(ac−bd)+(ad+bc)i,显然
但如果把它放在坐标系上,乘积也是有规律的。就是模长相乘,幅角相加。
模长指的是这个复数所在的点与原点的距离,而幅角就是这一条连线与x轴正半轴的夹角。
具体证明这里就不赘述了。
那么,对于一个复数,它的n次方的模长是原数的n次方,而幅角就是原数的n倍。
好的,下面是关键部分:
我们知道,如何一个非零数的0次方都为1,所以
(
1
n
)
0
=
1
(\sqrt[n]{1})^0=1
(n1)0=1(这里的次方根是在复数数域内的)
而
(
1
n
)
n
=
1
(\sqrt[n]{1})^n=1
(n1)n=1也是成立的。
也就是说在乘了
n
n
n次
1
n
\sqrt[n]{1}
n1之后,模长没有变(1),幅角则是转了360度之后又回到了原位。
所以,
w
n
1
w_n^1
wn1(我们一般把
(
1
x
)
y
(\sqrt[x]{1})^y
(x1)y表示为
w
x
y
w_x^y
wxy)在平面坐标系上模长为1,幅角为
2
π
n
\frac{2\pi}{n}
n2π。
那么,剩下的就是一些简单的三角函数了,这里就不赘述了,最终
w
n
1
w_n^1
wn1的坐标就是
(
c
o
s
(
2
π
n
)
,
s
i
n
(
2
π
n
)
)
(cos(\frac{2\pi}{n}),sin(\frac{2\pi}{n}))
(cos(n2π),sin(n2π))。
有关的定理
群的性质
因为
w
n
0
=
w
n
n
=
1
w_n^0=w_n^n=1
wn0=wnn=1
所以
w
n
i
∗
w
n
j
=
w
n
i
+
j
=
w
n
(
i
+
j
)
%
n
w_n^i*w_n^j=w_n^{i+j}=w_n^{(i+j) \% n}
wni∗wnj=wni+j=wn(i+j)%n
于是有
w
n
−
1
=
w
n
n
−
1
w_n^{-1} =w_n^{n-1}
wn−1=wnn−1,
w
n
k
=
w
n
k
+
n
w_n^k=w_n^{k+n}
wnk=wnk+n,
w
n
k
=
w
n
2
k
w_n^k=w_n^{2k}
wnk=wn2k
消去引理
w
d
n
d
i
=
w
n
i
w_{dn}^{di}=w_n^i
wdndi=wni
折半引理
对于任意正整数n,2*n次单位根的平方的集合与n次单位根集合相同。
也就是
{
(
w
2
n
)
2
}
=
{
w
n
}
\{(w_{2n})^2\}=\{w_n\}
{(w2n)2}={wn}
求和引理
∑
j
=
0
n
−
1
(
w
n
k
)
j
=
0
\sum_{j=0}^{n-1}(w_n^k)^j=0
j=0∑n−1(wnk)j=0
证明,通过等比数列求和可得到原式等于
(
w
n
k
)
n
−
1
w
n
k
−
1
\frac{(w_n^k)^n-1}{w_n^k-1}
wnk−1(wnk)n−1
其分子=
w
n
n
k
−
1
w_n^{nk}-1
wnnk−1=
1
−
1
1-1
1−1=
0
0
0
得证。
FFT
好了,铺垫了那么久,终于来到了最终的正题。
我们把这特殊的n次单位根带入,就会有特殊的结果。
这特殊的结果就是多项式A的离散傅里叶变换(DFT)
紧接着,我们考虑分治。
首先,我们把n补成形如2^x(x为正整数)的数,原本没有的项就把系数补为0。
然后,我们考虑这样的一个多项式
A
(
x
)
A(x)
A(x):
a
0
+
a
1
x
+
a
2
x
2
+
.
.
.
+
a
n
−
1
x
n
−
1
a_0+a_1x+a_2x^2+...+a_{n-1}x^{n-1}
a0+a1x+a2x2+...+an−1xn−1
我们把奇数次项和偶数次项分开:
(
a
0
+
a
2
x
2
+
.
.
.
+
a
n
−
2
x
n
−
2
)
+
(
a
1
x
+
a
3
x
3
+
.
.
.
+
a
n
−
1
x
n
−
1
)
(a_0+a_2x^2+...+a_{n-2}x^{n-2})+(a_1x+a_3x^3+...+a_{n-1}x^{n-1})
(a0+a2x2+...+an−2xn−2)+(a1x+a3x3+...+an−1xn−1)
于是,我们把这个多项式分成了两个多项式:
A
0
(
x
2
)
+
x
∗
A
1
(
x
2
)
A_0(x^2)+x*A_1(x^2)
A0(x2)+x∗A1(x2)
在这里,项数也就变为了
n
/
2
n/2
n/2,一直到变为1为止,这时候,只剩下1个常数项,就返回1个代入
w
1
0
w_1^0
w10(因为有多少项就要有多少个点值),也就是当前的这个系数。
假设我们已经处理好了
A
0
A_0
A0与
A
1
A_1
A1两个
w
n
/
2
0
w_{n/2}^0
wn/20~
w
n
/
2
n
/
2
−
1
w_{n/2}^{n/2-1}
wn/2n/2−1的点值,考虑如何合并。
对于代入
w
n
k
w_n^k
wnk,有以下两种情况:
k
<
n
2
k<\frac{n}{2}
k<2n,那么
A
(
w
n
k
)
=
A
0
(
w
n
/
2
k
)
+
w
n
k
∗
A
1
(
w
n
/
2
k
)
A(w_n^k)=A_0(w_{n/2}^k)+w_n^k*A_1(w_{n/2}^k)
A(wnk)=A0(wn/2k)+wnk∗A1(wn/2k),直接相加即可(非常简单)
2.否则,
A
(
w
n
k
+
n
/
2
)
=
A
0
(
w
n
/
2
k
+
n
/
2
)
+
w
n
k
+
n
/
2
∗
A
1
(
w
n
/
2
k
+
n
/
2
)
=
A
0
(
w
n
/
2
k
)
−
w
n
k
∗
A
1
(
w
n
/
2
k
)
A(w_n^{k+n/2})=A_0(w_{n/2}^{k+n/2})+w_n^{k+n/2}*A_1(w_{n/2}^{k+n/2})=A_0(w_{n/2}^k)-w_n^k*A_1(w_{n/2}^k)
A(wnk+n/2)=A0(wn/2k+n/2)+wnk+n/2∗A1(wn/2k+n/2)=A0(wn/2k)−wnk∗A1(wn/2k)
然后,就直接合并就好了(是不是比想象中的要简单呢?)
然后就这样合并
log
2
n
\log_2n
log2n次,就可以得到点值了!!!!!
这样点积的时间复杂度就是
O
(
n
l
o
g
2
n
)
O(nlog_2n)
O(nlog2n)
插值
点积已经被我们KO了,那么插积怎么搞呢?
实际上,DFT有一个非常巧妙的结论。
对于一个多项式A,我们求出它的DFT,然后把得出来的
(
y
0
,
y
1
,
.
.
.
,
y
n
−
1
)
(y_0,y_1,...,y_{n-1})
(y0,y1,...,yn−1)当做是一个新的多项式B的系数,然后用n次单位复根的的倒数作为代入值再对B求一遍DFT,得出一个
z
z
z
可证
a
i
=
z
i
n
a_i=\frac{z_i}{n}
ai=nzi
证明,我们先把
z
k
z_k
zk用式子求出来
z
k
=
∑
i
=
0
n
−
1
y
i
∗
(
w
n
−
k
)
i
=
∑
i
=
0
n
−
1
(
∑
j
=
0
n
−
1
a
j
∗
(
w
n
i
)
j
)
∗
(
w
n
−
k
)
i
z_k=\sum_{i=0}^{n-1}y_i*(w_n^{-k})^i=\sum_{i=0}^{n-1}(\sum_{j=0}^{n-1}a_j*(w_n^i)^j)*(w_n^{-k})^i
zk=i=0∑n−1yi∗(wn−k)i=i=0∑n−1(j=0∑n−1aj∗(wni)j)∗(wn−k)i
=
∑
i
=
0
n
−
1
∑
j
=
0
n
−
1
a
j
∗
(
w
n
−
k
)
i
∗
(
w
n
i
)
j
=
∑
i
=
0
n
−
1
∑
j
=
0
n
−
1
a
j
∗
w
n
(
j
−
k
)
∗
i
=\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_j*(w_n^{-k})^i*(w_n^i)^j=\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_j*w_n^{(j-k)*i}
=i=0∑n−1j=0∑n−1aj∗(wn−k)i∗(wni)j=i=0∑n−1j=0∑n−1aj∗wn(j−k)∗i
=
∑
j
=
0
n
−
1
a
j
∗
∑
i
=
0
n
−
1
w
n
(
j
−
k
)
∗
i
=\sum_{j=0}^{n-1}a_j*\sum_{i=0}^{n-1}w_n^{(j-k)*i}
=j=0∑n−1aj∗i=0∑n−1wn(j−k)∗i
若
j
=
k
j=k
j=k,
∑
i
=
0
n
−
1
w
n
(
j
−
k
)
∗
i
\sum_{i=0}^{n-1}w_n^{(j-k)*i}
∑i=0n−1wn(j−k)∗i显然为
n
n
n,不然,就是
∑
i
=
0
n
−
1
(
w
n
i
)
j
−
k
\sum_{i=0}^{n-1}(w_n^i)^{j-k}
∑i=0n−1(wni)j−k,通过求和引理我们可以知道这个式子为0.
所以
a
k
=
z
k
n
a_k=\frac{z_k}{n}
ak=nzk,得证
这样子的话,就相当于把插值变成了点值,时间复杂度为
O
(
n
l
o
g
2
n
)
O(nlog_2n)
O(nlog2n)