版权声明:本文为hzy原创文章,未经博主允许不可随意转载。 https://blog.csdn.net/Binary_Heap/article/details/82918827
快速傅里叶变换(
Fast Fourier Transform,FFT
\text{Fast Fourier Transform,FFT}
Fast Fourier Transform,FFT )是一种能在
O
(
n
log
n
)
O(n \log n)
O ( n log n ) 的时间内完成多项式乘法的算法,在
O
I
OI
O I 中的应用很多,是多项式相关内容的基础。下面从头开始介绍
FFT
\text{FFT}
FFT 。
前置技能:弧度制、三角函数、平面向量。
多项式
形如
f
(
x
)
=
a
0
+
a
1
x
+
a
2
x
2
+
.
.
.
+
a
n
x
n
f(x)=a_0+a_1x+a_2x^2+...+a_nx^n
f ( x ) = a 0 + a 1 x + a 2 x 2 + . . . + a n x n 的式子称为
x
x
x 的
n
n
n 次多项式。其中
a
0
,
a
1
,
.
.
.
,
a
n
a_0,a_1,...,a_n
a 0 , a 1 , . . . , a n 称为多项式的系数。
系数表达法
上面定义中的表示就是系数表达法。其系数可看成
n
+
1
n+1
n + 1 维向量
a
⃗
=
(
a
0
,
a
1
,
.
.
.
,
a
n
)
\vec a=(a_0,a_1,...,a_n)
a
= ( a 0 , a 1 , . . . , a n ) 。
点值表达法
把多项式看成一个函数,点值表示就用它图像上的
n
+
1
n+1
n + 1 个不同的点
(
x
0
,
y
0
)
,
.
.
.
,
(
x
n
,
y
n
)
(x_0,y_0),...,(x_n,y_n)
( x 0 , y 0 ) , . . . , ( x n , y n ) 来确定这个多项式。多项式有不止一个点值表示,可以证明每个点值表示确定唯一的系数表达多项式。
复数
虚数单位
i
i
i 被称为虚数单位。规定
i
=
−
1
i=\sqrt {-1}
i = − 1
。
复平面
复数的平面由
x
,
y
x,y
x , y 轴组成。
x
x
x 轴称为实轴,
y
y
y 轴称为虚轴。平面内的每一个从原点到某个点
(
a
,
b
)
(a,b)
( a , b ) 的向量
a
⃗
=
(
a
,
b
)
\vec a=(a,b)
a
= ( a , b ) 表示复数
a
+
b
i
a+bi
a + b i .
复数的模长:
a
2
+
b
2
\sqrt {a^2+b^2}
a 2 + b 2
.实轴到复数向量的转角
θ
\theta
θ 称为幅角。
复数的基本运算
复数的加(减)法:
(
a
+
b
i
)
+
(
c
+
d
i
)
=
(
a
+
c
)
+
(
b
+
d
)
i
(a+bi)+(c+di)=(a+c)+(b+d)i
( a + b i ) + ( c + d i ) = ( a + c ) + ( b + d ) i
复数的乘法:
(
a
+
b
i
)
(
c
+
d
i
)
=
(
a
c
-
b
d
)
+
(
b
c
+
a
d
)
i
(a+bi)(c+di)=(ac-bd)+(bc+ad)i
( a + b i ) ( c + d i ) = ( a c - b d ) + ( b c + a d ) i
一个结论:复数乘法,模长相乘,幅角相加 。
共轭复数
a
+
b
i
a+bi
a + b i 与
a
−
b
i
a-bi
a − b i 互为共轭复数。
单位根
n
n
n 次单位根是满足
z
n
=
1
z^n=1
z n = 1 的
n
n
n 个复数,它们均分复平面的单位圆。
这些复数满足模长为
1
1
1 ,幅角的
n
n
n 倍是
2
π
2\pi
2 π 的倍数
根据欧拉公式:
欧拉公式 :
e
x
i
=
cos
x
+
i
sin
x
e^{xi}=\cos x+i \sin x
e x i = cos x + i sin x ,其中
e
e
e 为自然对数的底数,
i
i
i 为虚数单位。
可得
n
n
n 次单位根为
e
2
π
k
i
n
,
k
∈
[
0
,
n
−
1
]
e^{\frac{2\pi ki}{n}},k\in [0,n-1]
e n 2 π k i , k ∈ [ 0 , n − 1 ]
得:记
ω
n
=
e
2
π
i
n
,
\omega_n=e^{\frac{2\pi i}{n}},
ω n = e n 2 π i , 则
n
n
n 次单位根为
ω
n
0
,
.
.
.
,
ω
n
n
−
1
\omega_n^0,...,\omega_n^{n-1}
ω n 0 , . . . , ω n n − 1
单位根的性质
性质
1
1
1 :根据定义得到:
ω
2
n
2
k
=
ω
n
k
\omega_{2n}^{2k}=\omega_{n}^{k}
ω 2 n 2 k = ω n k
性质
2
2
2 :
ω
n
n
2
+
k
=
−
ω
n
k
\omega_{n}^{\frac{n}{2}+k}=-\omega_n^k
ω n 2 n + k = − ω n k
证明:
ω
n
n
2
=
e
2
π
i
n
n
2
=
e
π
i
=
cos
π
+
i
sin
π
=
−
1
\omega_{n}^{\frac{n}{2}}=e^{\frac{2\pi i}{n} \frac{n}{2}}=e^{\pi i}=\cos \pi+i \sin \pi=-1
ω n 2 n = e n 2 π i 2 n = e π i = cos π + i sin π = − 1
ω
n
n
2
+
k
=
ω
n
n
2
ω
n
k
=
−
ω
n
k
\omega_{n}^{\frac{n}{2}+k}=\omega_{n}^{\frac{n}{2}}\omega_{n}^{k}=-\omega_n^k
ω n 2 n + k = ω n 2 n ω n k = − ω n k
Fast Fourier Transform
多项式乘法
系数表达的多项式乘法:
c
(
x
)
=
a
(
x
)
b
(
x
)
c(x)=a(x)b(x)
c ( x ) = a ( x ) b ( x ) ,则
c
(
x
)
=
∑
i
=
0
2
n
c
i
x
i
c(x)=\sum_{i=0}^{2n} c_i x^i
c ( x ) = ∑ i = 0 2 n c i x i
其中
c
i
=
∑
j
=
0
n
a
j
b
i
−
j
c_i=\sum_{j=0}^{n}a_jb_{i-j}
c i = ∑ j = 0 n a j b i − j .
时间复杂度
O
(
n
2
)
O(n^2)
O ( n 2 )
点值表达的多项式乘法:
时间复杂度
O
(
n
)
O(n)
O ( n )
因此多项式乘法的基本思路 是先插值得到点值表达,再
O
(
n
)
O(n)
O ( n ) 乘,最后求值得到系数表达。
DFT
把
n
n
n 次单位根
ω
n
0
,
.
.
.
,
ω
n
n
−
1
\omega_n^0,...,\omega_n^{n-1}
ω n 0 , . . . , ω n n − 1 带入多项式
A
(
x
)
=
a
0
+
a
1
x
+
.
.
.
+
a
n
x
n
A(x)=a_0+a_1x+...+a_nx^n
A ( x ) = a 0 + a 1 x + . . . + a n x n ,
得到点值向量
y
⃗
=
(
A
(
ω
n
0
)
,
A
(
ω
n
1
)
,
.
.
.
,
A
(
ω
n
n
−
1
)
)
\vec y=(A(\omega_n^0),A(\omega_n^1),...,A(\omega_n^{n-1}))
y
= ( A ( ω n 0 ) , A ( ω n 1 ) , . . . , A ( ω n n − 1 ) ) ,
称为系数向量
a
⃗
=
(
a
0
,
a
1
,
.
.
.
,
a
n
)
\vec a=(a_0,a_1,...,a_n)
a
= ( a 0 , a 1 , . . . , a n ) 的离散傅里叶变换 (
Discrete Fourier Transform, DFT
\text{Discrete Fourier Transform, DFT}
Discrete Fourier Transform, DFT ),写作
y
⃗
=
DFT
n
(
a
⃗
)
\vec y=\text{DFT}_n(\vec a)
y
= DFT n ( a
) 。
直接求
DFT
\text{DFT}
DFT 是
O
(
n
2
)
O(n^2)
O ( n 2 ) 的。
FFT
\text{FFT}
FFT 的常用算法
Cooley-Tukey
\text{Cooley-Tukey}
Cooley-Tukey 使用分治方法做到
O
(
n
log
n
)
O(n\log n)
O ( n log n ) .
以下讨论基于
n
=
2
m
,
m
∈
N
n=2^m,m \in \N
n = 2 m , m ∈ N ,若不足则高位系数补
0
0
0 .
考虑点值向量的第
k
+
1
k+1
k + 1 维:
A
(
ω
n
k
)
=
∑
i
=
0
n
−
1
a
i
(
ω
n
k
)
i
=
∑
i
=
0
n
−
1
a
i
ω
n
k
i
A(\omega_{n}^{k})=\sum_{i=0}^{n-1}a_i(\omega_{n}^{k})^{i}=\sum_{i=0}^{n-1}a_i\omega_{n}^{ki}
A ( ω n k ) = ∑ i = 0 n − 1 a i ( ω n k ) i = ∑ i = 0 n − 1 a i ω n k i
=
∑
i
=
0
n
2
−
1
a
2
i
ω
n
2
k
i
+
∑
i
=
0
n
2
−
1
a
2
i
ω
n
2
k
i
+
k
=\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{n}^{2ki}+\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{n}^{2ki+k}
= ∑ i = 0 2 n − 1 a 2 i ω n 2 k i + ∑ i = 0 2 n − 1 a 2 i ω n 2 k i + k
=
∑
i
=
0
n
2
−
1
a
2
i
ω
n
2
k
i
+
ω
n
k
∑
i
=
0
n
2
−
1
a
2
i
ω
n
2
k
i
=\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{n}^{2ki}+\omega_{n}^{k}\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{n}^{2ki}
= ∑ i = 0 2 n − 1 a 2 i ω n 2 k i + ω n k ∑ i = 0 2 n − 1 a 2 i ω n 2 k i
利用性质1:
ω
2
n
2
k
=
ω
n
k
\omega_{2n}^{2k}=\omega_{n}^{k}
ω 2 n 2 k = ω n k
当
k
<
n
2
k<\frac{n}{2}
k < 2 n 时:
A
(
ω
n
k
)
=
∑
i
=
0
n
2
−
1
a
2
i
ω
n
2
k
i
+
ω
n
k
∑
i
=
0
n
2
−
1
a
2
i
ω
n
2
k
i
A(\omega_{n}^{k})=\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}+\omega_{n}^{k}\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}
A ( ω n k ) = ∑ i = 0 2 n − 1 a 2 i ω 2 n k i + ω n k ∑ i = 0 2 n − 1 a 2 i ω 2 n k i
并且可以推出:
A
(
ω
n
k
+
n
2
)
=
(
−
1
)
n
2
∑
i
=
0
n
2
−
1
a
2
i
ω
n
2
k
i
+
(
−
1
)
n
2
ω
n
k
+
n
2
∑
i
=
0
n
2
−
1
a
2
i
ω
n
2
k
i
A(\omega_{n}^{k+\frac{n}{2}})=(-1)^{\frac{n}{2}}\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}+(-1)^{\frac{n}{2}}\omega_{n}^{k+\frac{n}{2}}\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}
A ( ω n k + 2 n ) = ( − 1 ) 2 n ∑ i = 0 2 n − 1 a 2 i ω 2 n k i + ( − 1 ) 2 n ω n k + 2 n ∑ i = 0 2 n − 1 a 2 i ω 2 n k i
利用性质2:
ω
n
n
2
+
k
=
−
ω
n
k
\omega_{n}^{\frac{n}{2}+k}=-\omega_n^k
ω n 2 n + k = − ω n k
A
(
ω
n
k
+
n
2
)
=
∑
i
=
0
n
2
−
1
a
2
i
ω
n
2
k
i
−
ω
n
k
∑
i
=
0
n
2
−
1
a
2
i
ω
n
2
k
i
A(\omega_{n}^{k+\frac{n}{2}})=\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}-\omega_n^k\sum_{i=0}^{\frac{n}{2}-1}a_{2i}\omega_{\frac{n}{2}}^{ki}
A ( ω n k + 2 n ) = ∑ i = 0 2 n − 1 a 2 i ω 2 n k i − ω n k ∑ i = 0 2 n − 1 a 2 i ω 2 n k i
上面把求和分成
0
,
2
,
.
.
.
,
n
−
2
0,2,...,n-2
0 , 2 , . . . , n − 2 与
1
,
3
,
.
.
.
,
n
−
1
1,3,...,n-1
1 , 3 , . . . , n − 1 两部分,把大小为
n
n
n 的问题转化成两个规模为
n
2
\frac{n}{2}
2 n 的子问题,可以进行分治求解了。
IDFT
求值过程使用离散傅里叶逆变换 (
Inverse Discrete Fourier Transform, IDFT
\text{Inverse Discrete Fourier Transform, IDFT}
Inverse Discrete Fourier Transform, IDFT )
结论 :只要把
DFT
\text{DFT}
DFT 的
ω
n
\omega_n
ω n 都取倒数,最后除以
n
n
n 即可。
证明:
设
Y
⃗
=
(
y
0
,
y
1
,
.
.
.
,
y
n
)
\vec Y=(y_0,y_1,...,y_n)
Y
= ( y 0 , y 1 , . . . , y n ) 为
A
⃗
=
(
a
0
,
a
1
,
.
.
.
,
a
n
)
\vec A = (a_0,a_1,...,a_n)
A
= ( a 0 , a 1 , . . . , a n ) 的离散傅里叶变换。
考虑一个向量:
C
⃗
=
(
c
0
,
c
1
,
.
.
.
,
c
n
)
\vec C=(c_0,c_1,...,c_n)
C
= ( c 0 , c 1 , . . . , c n ) 满足
c
k
=
∑
i
=
0
n
−
1
y
i
(
ω
n
−
k
)
i
c_k=\sum_{i=0}^{n-1}y_i(\omega_n^{-k})^i
c k = ∑ i = 0 n − 1 y i ( ω n − k ) i
(即
C
⃗
\vec C
C
是多项式
Y
⃗
\vec Y
Y
在
ω
n
0
,
ω
n
−
1
,
.
.
.
,
ω
n
−
(
n
−
1
)
\omega_n^0,\omega_n^{-1},...,\omega_n^{-(n-1)}
ω n 0 , ω n − 1 , . . . , ω n − ( n − 1 ) 处的点值)
将上式展开:
c
k
=
∑
i
=
0
n
−
1
y
i
(
ω
n
−
k
)
i
c_k=\sum_{i=0}^{n-1}y_i(\omega_n^{-k})^i
c k = ∑ i = 0 n − 1 y i ( ω n − k ) i
=
∑
i
=
0
n
−
1
(
∑
j
=
0
n
−
1
a
j
(
ω
n
i
)
j
)
(
ω
n
−
k
)
i
=\sum_{i=0}^{n-1}(\sum_{j=0}^{n-1}a_j(\omega_n^i)^j)(\omega_n^{-k})^i
= ∑ i = 0 n − 1 ( ∑ j = 0 n − 1 a j ( ω n i ) j ) ( ω n − k ) i
=
∑
i
=
0
n
−
1
(
∑
j
=
0
n
−
1
a
j
(
ω
n
j
)
i
)
(
ω
n
−
k
)
i
=\sum_{i=0}^{n-1}(\sum_{j=0}^{n-1}a_j(\omega_n^j)^i)(\omega_n^{-k})^i
= ∑ i = 0 n − 1 ( ∑ j = 0 n − 1 a j ( ω n j ) i ) ( ω n − k ) i
=
∑
i
=
0
n
−
1
∑
j
=
0
n
−
1
a
j
(
ω
n
j
)
i
(
ω
n
−
k
)
i
=\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_j(\omega_n^j)^i(\omega_n^{-k})^i
= ∑ i = 0 n − 1 ∑ j = 0 n − 1 a j ( ω n j ) i ( ω n − k ) i
=
∑
i
=
0
n
−
1
∑
j
=
0
n
−
1
a
j
(
ω
n
j
−
k
)
i
=\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_j(\omega_n^{j-k})^i
= ∑ i = 0 n − 1 ∑ j = 0 n − 1 a j ( ω n j − k ) i
=
∑
j
=
0
n
−
1
a
j
(
∑
i
=
0
n
−
1
(
ω
n
j
−
k
)
i
)
=\sum_{j=0}^{n-1}a_j(\sum_{i=0}^{n-1}(\omega_n^{j-k})^i)
= ∑ j = 0 n − 1 a j ( ∑ i = 0 n − 1 ( ω n j − k ) i )
考虑一个前缀和
S
(
ω
n
k
)
=
1
+
ω
n
k
+
(
ω
n
k
)
2
+
.
.
.
+
(
ω
n
k
)
n
−
1
S(\omega_n^k)=1+\omega_n^k+(\omega_n^k)^2+...+(\omega_n^k)^{n-1}
S ( ω n k ) = 1 + ω n k + ( ω n k ) 2 + . . . + ( ω n k ) n − 1 。
当
(
ω
n
k
)
̸
=
1
(\omega_n^k)\not = 1
( ω n k ) ̸ = 1 即
k
̸
=
0
k\not = 0
k ̸ = 0 时,使用等比数列求和方法:
ω
n
k
S
(
ω
n
k
)
=
ω
n
k
+
(
ω
n
k
)
2
+
(
ω
n
k
)
3
+
.
.
.
+
(
ω
n
k
)
n
\omega_n^kS(\omega_n^k)=\omega_n^k+(\omega_n^k)^2+(\omega_n^k)^3+...+(\omega_n^k)^{n}
ω n k S ( ω n k ) = ω n k + ( ω n k ) 2 + ( ω n k ) 3 + . . . + ( ω n k ) n
ω
n
k
S
(
ω
n
k
)
−
S
(
ω
n
k
)
=
(
ω
n
k
)
n
−
1
\omega_n^kS(\omega_n^k)-S(\omega_n^k)=(\omega_n^k)^{n}-1
ω n k S ( ω n k ) − S ( ω n k ) = ( ω n k ) n − 1
S
(
ω
n
k
)
=
(
ω
n
k
)
n
−
1
ω
n
k
−
1
S(\omega_n^k)=\frac{(\omega_n^k)^{n}-1}{\omega_n^k-1}
S ( ω n k ) = ω n k − 1 ( ω n k ) n − 1
分母不为
0
0
0 ,分子
(
ω
n
k
)
n
−
1
=
(
ω
n
n
)
k
−
1
=
1
k
−
1
=
0
(\omega_n^k)^{n}-1=(\omega_n^n)^{k}-1=1^{k}-1=0
( ω n k ) n − 1 = ( ω n n ) k − 1 = 1 k − 1 = 0
因此
k
̸
=
0
k\not = 0
k ̸ = 0 时,
S
(
ω
n
k
)
=
0
S(\omega_n^k)=0
S ( ω n k ) = 0
k
=
0
k=0
k = 0 时,
S
(
ω
n
k
)
=
n
(
ω
n
k
)
0
=
n
S(\omega_n^k)=n(\omega_n^k)^0=n
S ( ω n k ) = n ( ω n k ) 0 = n
继续考虑刚刚那个式子
c
k
=
∑
j
=
0
n
−
1
a
j
(
∑
i
=
0
n
−
1
(
ω
n
j
−
k
)
i
)
c_k=\sum_{j=0}^{n-1}a_j(\sum_{i=0}^{n-1}(\omega_n^{j-k})^i)
c k = ∑ j = 0 n − 1 a j ( ∑ i = 0 n − 1 ( ω n j − k ) i )
只有
j
−
k
=
0
j-k=0
j − k = 0 时
∑
i
=
0
n
−
1
(
ω
n
j
−
k
)
i
\sum_{i=0}^{n-1}(\omega_n^{j-k})^i
∑ i = 0 n − 1 ( ω n j − k ) i 才为
n
n
n ,否则为
0
0
0 。
c
k
=
a
k
n
c_k=a_kn
c k = a k n
得到结论:
a
k
=
c
k
n
a_k=\frac{c_k}{n}
a k = n c k 。
再次总结一下,离散傅里叶逆变换就是先求出多项式在
ω
n
0
,
ω
n
−
1
,
.
.
.
,
ω
n
−
(
n
−
1
)
\omega_n^0,\omega_n^{-1},...,\omega_n^{-(n-1)}
ω n 0 , ω n − 1 , . . . , ω n − ( n − 1 ) 处的点值表示,再每一项除以
n
n
n 。
递归版代码
#include <complex>
#include <cstdio>
using namespace std;
typedef complex< double > comp;
const int N = ( 1 << 20 ) + 10 << 1 ;
const double PI2 = 2.0 * acos ( - 1.0 ) ;
int read ( ) {
int x = 0 ; char c = getchar ( ) ;
for ( ; c < '0' || c > '9' ; c = getchar ( ) ) ;
for ( ; c >= '0' && c <= '9' ; c = getchar ( ) )
x = x * 10 + ( c & 15 ) ;
return x;
}
int n, m;
comp a[ N] , b[ N] ;
void fft ( int n, comp * a, int type) {
if ( n == 1 ) return ;
comp a1[ n >> 1 ] , a2[ n >> 1 ] ;
for ( int i = 0 ; i < n; i + = 2 )
a1[ i >> 1 ] = a[ i] , a2[ i >> 1 ] = a[ i + 1 ] ;
fft ( n >> 1 , a1, type) , fft ( n >> 1 , a2, type) ;
comp w ( 1 , 0 ) , wn ( cos ( PI2 / n) , type * sin ( PI2 / n) ) ;
for ( int i = 0 ; i < n >> 1 ; i ++ , w * = wn)
a[ i] = a1[ i] + w * a2[ i] ,
a[ i + ( n >> 1 ) ] = a1[ i] - w * a2[ i] ;
}
int main ( ) {
n = read ( ) , m = read ( ) ;
for ( int i = 0 ; i <= n; i ++ ) a[ i] = read ( ) ;
for ( int i = 0 ; i <= m; i ++ ) b[ i] = read ( ) ;
int lim = 1 ;
for ( ; lim <= n + m; lim <<= 1 ) ;
fft ( lim, a, 1 ) , fft ( lim, b, 1 ) ;
for ( int i = 0 ; i <= lim; i ++ ) a[ i] * = b[ i] ;
fft ( lim, a, - 1 ) ;
for ( int i = 0 ; i <= n + m; i ++ )
printf ( "%d " , ( int ) ( 0.5 + a[ i] . real ( ) / lim) ) ;
return 0 ;
}
FFT 的优化
迭代优化
蝴蝶操作
待更。qwq