针对长度 n 为合数和质数的情况,分别使用 Cooley-Tukey 和 Rader 算法进行实现。
Cooley-Tukey
Cooley-Tukey 是一种求解长度为合数的 FFT 算法。基 2 的 FFT 是一种特殊的 Cooley Tukey,简单回顾一下:
(E0,E1,…,En/2−1)=FFT(x0,x2,…,xn−2)(O0,O1,…,On/2−1)=FFT(x1,x3,…,xn−1)for k=0,1,…,n/2−1: Xk=Ek+wnkOkfor k=n/2,n/2+1,…,n−1: Xk=Ek−n/2+wnkOk−n/2
简单来说,就是将长度为 n 的 DFT 拆分成两个长度为 n/2 的 DFT,从而使得计算的时间复杂度降低。
Cooley-Tukey 也是使用了这种分而治之的思想,根据长度的因数对序列进行拆分。假设 x 的长度为 N,其中 N=N1N2,那么 DFT 的公式为
Xk=n=0∑N−1exp(−iN2πnk)xn
将以下两个式子代入上式
k=N2k1+k2, k1∈{0,1,…,N1−1}, k2∈{0,1,…,N2−1}n=N1n2+n1, n1∈{0,1,…,N1−1}, n2∈{0,1,…,N2−1}
可以得到
XN2k1+k2=n1=0∑N1−1n2=0∑N2−1exp(−iN1N22π(N1n2+n1)(N2k1+k2))xN1n2+n1=n1=0∑N1−1exp(−iN12πn1k1)exp(−iN2πn1k2)n2=0∑N2−1xN1n2+n1exp(−iN22π(n2k2))
分析一下以上式子,n2=0∑N2−1xN1n2+n1exp(−iN22π(n2k2)) 正是长度为 N2 的 DFT,对应的序列为 xN1n2+n1, n2∈{0,1,…,N2−1}
记 yn1,k2=n2=0∑N2−1xN1n2+n1exp(−iN22π(n2k2)),zn1,k2=exp(−iN2πn1k2)yn1,k2,则有
XN2k1+k2=n1=0∑N1−1exp(−iN12πn1k1)zn1,k2
这正是长度为 N1 的 DFT。
根据以上的分析,对于 N 为合数的情况,我们可以先做 N1 个长度为 N2 的 DFT,然后每个元素乘上一个系数,再做 N2 个长度为 N1 的 DFT。那么时间复杂度可由下式表示:
T(N)=N1T(N2)+N2T(N1)+Θ(N)
具体的算法步骤如下:
- 将长为 N 序列(向量)按列排列变为 N1×N2 的矩阵
- 对矩阵的每一行分别做 DFT
- 矩阵的每一个元素乘上系数 exp(−iN2πn1n2),n1,n2 分别表示该元素所处的行和列(从 0 开始)
- 对矩阵的每一列分别做 DFT
- 将 N1×N2 矩阵按行排列变为长为 N 的向量
推导思路如下:
记 x[n1][n2]=x[N1n2+n1],对每一行进行 DFT 变换,即固定 n1,得到 X1[n1][k2],乘上系数后得到 X2[n1][k2],再对每一列进行 DFT 变换,固定k2,得到 X[k1][k2]=X[N2k1+k2],所以最后按行排列即可得到原序列对应的 DFT。
Rader
对于长度为质数的情况,就不能将其简单的拆分了。注意到假如 N 为质数的话,那么 N−1 一定为合数,如果能把一个元素单独抽出来,那么剩下就可以用 Cooley Tukey 进行求解了。
Rader 算法需要用到数论的知识,首先介绍相关的知识。由于相关内容超出这门课的范围,就不详细展开证明,只进行叙述。
假设 N 为质数,定义 a,b 之间的运算 ⋅ 为 (ab)modN,则 {0,1,…,N−1} 和 ⋅ 可以构成一个群,这个群叫做整数模 N 乘法群。根据数论知识,这样的群存在一个整数 g,也叫做原根,使得对于任意非零的 n 都有唯一对应的 q∈{0,1,…,N−2},对应关系为 n=gpmodN。同样对于任意非零的 k 都有唯一对应的 p∈{0,1,…,N−2},使得 k=g−pmodN。此处,k=g−pmodN 等价于 kgpmodN=1。
有了以上的结论,就可以将 {1,…,N−1} 映射到 {0,…,N−1}。 将上述 n, k 代入 DFT 的公式中,单独写出第 0 项,可以得到
X0Xg−p(modN)=n=0∑N−1xn=x0+q=0∑N−2xgq(modN)exp(−iN2πgq−p(modN))
令 aq=xgq(modN),bq=exp(−iN2πg−q(modN)),q∈{0,…,N−2},那么 q=0∑N−2xgq(modN)exp(−iN2πgq−p(modN)) 实际上是 {aq} 和 {bq} 的循环卷积。根据循环卷积定理,有
a∗b=(a^⋅b^)ˇ
这样循环卷积就可以转化为两个 DFT 和 一个 IDFT,这就转化成了我们熟悉的问题。
对于这种情况,为了方便进行 DFT,可以将其填充到 2 的幂次方,使用 基-2 的 FFT 算法进行求解。如果不将其补到 2 的幂次,使用 Rader 算法进行求解的过程中可能还会产生大的质数,这种情况下时间也会很大。
对于长度为 N 的两个序列,按以下方法进行补充:
- 首先找到一个最小的 m∈Z,使得 M=2m>2N
- 令 a~=[a0,M−N0,…,0,a1,…,aN−1], b~=[b0,…,bN−1,b0,…] (一直重复 b,直到长度为 M,对超出的部分直接截断)
- 求 a~∗b~,并取前 N 项,则得到 a∗b
证明:
(a~∗b~)k=j=0∑M−1a~jb~k−j=a0bk+j=M−N∑M−1a~jb~k−j=a0bk+j=1∑N−1ajb~k+N−j=j=0∑N−1ajbk−j=(a∗b)k