文章目录
3. DFT与FFT
(算导30.1节中断言)如果使用单位复数根,可以在 Θ ( n log n ) \Theta(n\log n) Θ(nlogn) 时间内完成求值与插值运算。在本节中给出单位复数根的定义,研究其性质,以及定义DFT,然后说明FFT如何仅用 Θ ( n log n ) \Theta(n\log n) Θ(nlogn) 时间、就可以计算出DFT和它的逆。
3.1 单位复数根
n
n
n 次单位复数根 complex nth root of unity
是满足
ω
n
=
1
\omega^n = 1
ωn=1 的复数
ω
\omega
ω(类比平方根、立方根,且是单位根、复数)。
n
n
n 次单位复数根恰好有
n
n
n 个:对于
k
=
0
,
1
,
…
,
n
−
1
k = 0, 1, \dots, n-1
k=0,1,…,n−1 这些根是
e
2
π
i
k
/
n
(
i
=
−
1
)
e^{2\pi ik / n}\ (i = \sqrt{-1})
e2πik/n (i=−1) 。为了(理解)解释这个表达式,我们利用复数指数的定义 the definition of the exponential of a complex number
:
e
i
u
=
cos
(
u
)
+
i
sin
(
u
)
e^{iu} = \cos(u) + i\sin (u)
eiu=cos(u)+isin(u) 图30-2说明,
n
n
n 个单位复数根均匀地分布在「以复平面的原点为圆心的、单位半径的圆周」上。值
ω
n
=
e
2
π
i
/
n
(30.6)
\omega_n = e^{2\pi i / n} \tag{30.6}
ωn=e2πi/n(30.6) 称为主
n
n
n 次单位根 the principal nth root of unity
,其余的
n
n
n 次单位复数根都是
ω
n
=
ω
n
1
\omega_n = \omega_n^1
ωn=ωn1 的幂次,即
(
ω
n
1
)
k
=
ω
n
k
(\omega_n^1)^k =\omega_n^k
(ωn1)k=ωnk 。注意,有时别人对
ω
n
\omega_n
ωn 有不同的定义:
ω
n
=
e
−
2
π
i
/
n
\omega_n = e^{ -2\pi i /n}
ωn=e−2πi/n ,这个可替的定义一般用在信号处理应用中,这两个
ω
n
\omega_n
ωn 的定义、其背后的数学含义基本上是相同的。
例如(红字为主
n
n
n 次单位根),
ω
8
0
=
e
2
π
×
0
8
i
=
cos
(
0
)
+
i
sin
(
0
)
复
平
面
(
1
,
0
)
ω
8
1
=
e
2
π
×
1
8
i
=
cos
(
π
4
)
+
i
sin
(
π
4
)
复
平
面
(
2
2
,
2
2
)
ω
8
2
=
e
2
π
×
2
8
i
=
cos
(
π
2
)
+
i
sin
(
π
2
)
复
平
面
(
0
,
1
)
ω
8
3
=
e
2
π
×
3
8
i
=
cos
(
3
π
4
)
+
i
sin
(
3
π
4
)
复
平
面
(
−
2
2
,
2
2
)
ω
8
4
=
e
2
π
×
4
8
i
=
cos
(
π
)
+
i
sin
(
π
)
复
平
面
(
−
1
,
0
)
ω
8
5
=
e
2
π
×
5
8
i
=
cos
(
5
π
4
)
+
i
sin
(
5
π
4
)
复
平
面
(
−
2
2
,
−
2
2
)
ω
8
6
=
e
2
π
×
6
8
i
=
cos
(
3
π
2
)
+
i
sin
(
3
π
2
)
复
平
面
(
0
,
−
1
)
ω
8
7
=
e
2
π
×
7
8
i
=
cos
(
7
π
4
)
+
i
sin
(
7
π
4
)
复
平
面
(
2
2
,
−
2
2
)
ω
8
8
=
e
2
π
×
8
8
i
=
cos
(
2
π
)
+
i
sin
(
2
π
)
复
平
面
(
1
,
0
)
\begin{aligned} &\omega^0_8 = e^{\frac{ 2 \pi \times 0 } { 8} i} = \cos (0)+i \sin(0) \quad& 复平面(1, 0) \\ &\textcolor{red} {\omega^1_8 = e^{\frac{ 2 \pi \times 1 } { 8} i}} = \cos (\frac{\pi}{4})+i \sin( \frac{\pi}{4} ) \quad& 复平面(\dfrac{\sqrt{2} }{ 2}, \dfrac{\sqrt{2} }{ 2} )\\ &\omega^2_8 = e^{\frac{ 2 \pi \times 2 } { 8} i} = \cos (\dfrac{\pi}{2} )+i \sin(\dfrac{\pi}{2} ) \quad& 复平面(0, 1) \\ &\omega^3_8 = e^{\frac{ 2 \pi \times 3 } { 8} i} = \cos ( \dfrac{3\pi}{4} )+i \sin( \dfrac{3\pi}{4} ) \quad& 复平面( -\dfrac{ \sqrt{2} }{2}, \dfrac{\sqrt{2} }{2} ) \\ &\omega^4_8 = e^{\frac{ 2 \pi \times 4 } { 8} i} = \cos ( \pi )+i \sin(\pi ) \quad& 复平面(-1, 0) \\ &\omega^5_8 = e^{\frac{ 2 \pi \times 5 } { 8} i} = \cos ( \dfrac{5\pi}{4} )+i \sin(\dfrac{5\pi}{4} ) \quad& 复平面(-\dfrac{ \sqrt{2} }{2}, -\dfrac{\sqrt{2} }{2}) \\ &\omega^6_8 = e^{\frac{ 2 \pi \times 6 } { 8} i} = \cos (\dfrac{3\pi}{2} )+i \sin( \dfrac{3\pi}{2} ) \quad& 复平面(0, -1) \\ &\omega^7_8 = e^{\frac{ 2 \pi \times 7 } { 8} i} = \cos (\dfrac{7\pi}{4} )+i \sin( \dfrac{7\pi}{4} ) \quad& 复平面(\dfrac{ \sqrt{2} }{2}, -\dfrac{\sqrt{2} }{2}) \\ &\omega^8_8 = e^{\frac{ 2 \pi \times 8 } { 8} i} = \cos (2\pi )+i \sin(2\pi ) \quad& 复平面(1, 0) \\ \end{aligned}
ω80=e82π×0i=cos(0)+isin(0)ω81=e82π×1i=cos(4π)+isin(4π)ω82=e82π×2i=cos(2π)+isin(2π)ω83=e82π×3i=cos(43π)+isin(43π)ω84=e82π×4i=cos(π)+isin(π)ω85=e82π×5i=cos(45π)+isin(45π)ω86=e82π×6i=cos(23π)+isin(23π)ω87=e82π×7i=cos(47π)+isin(47π)ω88=e82π×8i=cos(2π)+isin(2π)复平面(1,0)复平面(22,22)复平面(0,1)复平面(−22,22)复平面(−1,0)复平面(−22,−22)复平面(0,−1)复平面(22,−22)复平面(1,0)
n n n 个 n n n 次单位复数根 ω n 0 , ω n 1 , … , ω n n − 1 \omega_n^0, \omega_n^1, \dots, \omega_n^{n-1} ωn0,ωn1,…,ωnn−1 在乘法意义下形成一个群(见算导31.3节)。该群与加法群 ( Z n , + ) (\Z_n, +) (Zn,+) 具有相同的结构,因为 ω n n = ω n 0 = 1 \omega^n_n = \omega^0_n = 1 ωnn=ωn0=1 意味着 ω n j ω n k = ω n ( j + k ) m o d n \omega^j_n \omega^k_n = \omega_n^{{(j+k)} \bmod n} ωnjωnk=ωn(j+k)modn 。类似地, ω n − 1 = ω n n − 1 \omega_n^{-1} = \omega_n^{n-1} ωn−1=ωnn−1 。下面的引理给出了 n n n 次单位复数根的一些基本性质。
引理30.3(消去引理 Cancellation lemma
)对任何整数
n
≥
0
,
k
≥
0
n \ge 0, k \ge 0
n≥0,k≥0 ,以及
d
>
0
d > 0
d>0(
n
n
n 不应该
>
0
>0
>0 吗?),
ω
d
n
d
k
=
ω
n
k
(30.7)
\omega_{dn}^{dk} = \omega_{n}^k \tag{30.7}
ωdndk=ωnk(30.7) 证明:由式30.6可以直接推出引理,因为:
ω
d
n
d
k
=
(
e
2
π
i
d
n
)
d
k
=
(
e
2
π
i
n
)
k
=
ω
n
k
■
\omega_{dn}^{dk} = (e^{ \frac{2\pi i} {dn } } )^{dk } = (e^{\frac{2\pi i }{n}})^k = \omega_n^k \tag*{■}
ωdndk=(edn2πi)dk=(en2πi)k=ωnk■
推论30.4 对任意偶数
n
>
0
n > 0
n>0 ,有
ω
n
n
/
2
=
ω
2
=
−
1
\omega_n^{n/2} = \omega_2 = -1
ωnn/2=ω2=−1 证明:证明见算导练习30.2-1。本人的证明如下:由于
n
n
n 为大于零的偶数,所以
n
/
2
×
2
n/ 2\times 2
n/2×2 仍等于
n
n
n ,于是根据引理30.3有:
ω
n
n
/
2
=
ω
2
n
n
=
(
e
2
π
i
2
n
)
n
=
e
2
π
i
2
=
ω
2
\omega_n^{n/2} = \omega_{2n}^n =(e^{ \frac{2\pi i }{2n}} )^n = e^{\frac{2\pi i}{2}} = \omega_2
ωnn/2=ω2nn=(e2n2πi)n=e22πi=ω2 又可知:
ω
2
=
e
2
π
i
2
=
cos
(
π
)
+
i
sin
(
π
)
=
−
1
\omega_2 = e^{\frac{2\pi i}{2}} = \cos(\pi) + i \sin (\pi)=-1
ω2=e22πi=cos(π)+isin(π)=−1 所以推论30.4可证。
■
\blacksquare
■
引理30.5(折半引理 Halving lemma
)如果
n
>
0
n>0
n>0 为偶数,那么
n
n
n 个
n
n
n 次单位复数根的平方的集合,就是
n
/
2
n/2
n/2 个
n
/
2
n/2
n/2 次单位复数根的集合 If n > 0 is even, then the squares of the n complex nth roots of unity are the n/2 complex (n/2)/th roots of unity
。
证明:根据消去引理,对任意非负整数
k
k
k ,我们有
(
ω
n
k
)
2
=
ω
n
2
k
=
ω
n
/
2
k
(\omega_n^k)^2 =\omega_n^{2k}= \omega_{n/2}^k
(ωnk)2=ωn2k=ωn/2k(即对一个
n
n
n 次单位复数根平方后得到一个
n
/
2
n/2
n/2 次单位复数根)。注意,如果对所有
n
n
n 次单位复数根进行平方,那么获得每个
n
/
2
n/2
n/2 次单位根正好
2
2
2 次,因为:
(
ω
n
k
+
n
/
2
)
2
=
ω
n
2
k
+
n
=
ω
n
2
k
ω
n
n
=
ω
n
2
k
=
(
ω
n
k
)
2
(\omega_n^{k +n / 2})^2 = \omega_n^{2k+n} = \omega_n^{2k}\omega^n_n = \omega_n^{2k} = (\omega_n^k)^2
(ωnk+n/2)2=ωn2k+n=ωn2kωnn=ωn2k=(ωnk)2 因此可知,
ω
n
k
\omega^k_n
ωnk 与
ω
n
k
+
n
/
2
\omega^{k+n/2}_n
ωnk+n/2 平方相同。
我们也可以由推论30.4来证明该性质,因为 ω n n / 2 = − 1 \omega_n^{n/2} = -1 ωnn/2=−1 意味着 ω n k + n / 2 = − ω n k \omega_n^{k+n/2} = - \omega^k_n ωnk+n/2=−ωnk ,从而 ( ω n k + n / 2 ) 2 = ( ω n k ) 2 (\omega^{k+n/2}_n)^2 = (\omega_n^k)^2 (ωnk+n/2)2=(ωnk)2 。 ■ \blacksquare ■
我们将会看到,对于用分治策略来对「多项式的系数与点值表达」进行相互转换,折半引理是非常重要的,因为它保证递归子问题的规模只是递归调用前的一半。
引理30.6(求和引理 Summation lemma
)对任意整数
n
≥
1
n\ge 1
n≥1 和不能被
n
n
n 整除的非负整数
k
k
k ,有
∑
j
=
0
n
−
1
(
ω
n
k
)
j
=
0
\sum^{n-1}_{j = 0} (\omega^k_n)^j = 0
j=0∑n−1(ωnk)j=0 证明:对于实数
x
≠
1
x \ne 1
x=1 ,和式:
∑
k
=
0
n
x
k
=
1
+
x
+
x
2
+
⋯
+
x
n
=
x
n
+
1
−
1
x
−
1
\sum^n_{k=0} x^k = 1 + x +x^2 +\dots + x^n = \dfrac{x^{n+1}- 1}{x - 1}
k=0∑nxk=1+x+x2+⋯+xn=x−1xn+1−1 这是几何级数/等比级数/指数级数求和公式(算导A.5),既适用于实数,也适用于复数。因此有:
∑
j
=
0
n
−
1
(
ω
n
k
)
j
=
(
ω
n
k
)
n
−
1
ω
n
k
−
1
=
(
ω
n
n
)
k
−
1
ω
n
k
−
1
=
(
1
)
k
−
1
ω
n
k
−
1
=
0
\sum^{n-1}_{j = 0} (\omega^k_n)^j =\dfrac{ (\omega^k_n)^n - 1}{ \omega^k_n - 1} = \dfrac{ (\omega^n_n)^k - 1} { \omega_n^k - 1} = \dfrac{ (1)^k- 1} { \omega^k_n - 1} = 0
j=0∑n−1(ωnk)j=ωnk−1(ωnk)n−1=ωnk−1(ωnn)k−1=ωnk−1(1)k−1=0 因为要求
k
k
k 不能被
n
n
n 整除、而仅当
k
k
k 被
n
n
n 整除时
ω
n
k
=
1
\omega^k_n=1
ωnk=1 成立,我们确保分母不为
0
0
0 。
■
\blacksquare
■
3.2 DFT
回顾一下,我们希望计算次数界为
n
n
n 的多项式
A
(
x
)
=
∑
j
=
0
n
−
1
a
j
x
j
A(x) = \sum^{n-1}_{j=0} a_jx^j
A(x)=j=0∑n−1ajxj 在
ω
n
0
,
ω
n
1
,
ω
n
2
,
…
,
ω
n
n
−
1
\omega^0_n, \omega^1_n, \omega^2_n, \dots, \omega^{n-1}_n
ωn0,ωn1,ωn2,…,ωnn−1 处的值(即在
n
n
n 个
n
n
n 次复数根处)。假设
A
A
A 以系数形式给出,即
a
=
(
a
0
,
a
1
,
…
,
a
n
−
1
)
a = (a_0, a_1, \dots, a_{n-1})
a=(a0,a1,…,an−1) 。接下来对
k
=
0
,
1
,
…
,
n
−
1
k = 0, 1, \dots, n - 1
k=0,1,…,n−1 ,定义结果
y
k
y_k
yk :
y
k
=
A
(
ω
n
k
)
=
∑
j
=
0
n
−
1
a
j
ω
n
k
j
(30.8)
y_k = A(\omega^k_n) = \sum^{n-1}_{j=0} a_j \omega^{kj}_n \tag{30.8}
yk=A(ωnk)=j=0∑n−1ajωnkj(30.8) 向量
y
=
(
y
0
,
y
1
,
…
,
y
n
−
1
)
y = (y_0, y_1, \dots, y_{n-1})
y=(y0,y1,…,yn−1) 就是系数向量
a
=
(
a
0
,
a
1
,
…
,
a
n
−
1
)
a=(a_0, a_1, \dots, a_{n-1})
a=(a0,a1,…,an−1) 的离散傅里叶变换 DFT
。我们也记为
y
=
D
F
T
n
(
a
)
y = \mathrm{DFT_n}(a)
y=DFTn(a) 。
3.3 FFT
通过使用一种称为快速傅里叶变换 FFT
的方法,利用复数单位根的特殊性质,我们就能在
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn) 时间内计算出
D
F
T
n
(
a
)
\mathrm{DFT_n}(a)
DFTn(a)(即完成系数表达到点值表达的转换),而直接的方法所需时间为
Θ
(
n
2
)
\Theta(n^2)
Θ(n2) 。这里全篇假设
n
n
n 恰好是
2
2
2 的整数幂,虽然处理非
2
2
2 整数幂的策略已经存在,但算导正文没有提。
FFT利用了分治策略,采用 A ( x ) A(x) A(x) 中偶数下标的系数与奇数下标的系数,分别定义两个新的、次数界为 n / 2 n/2 n/2 的多项式 A [ 0 ] ( x ) A^{ [0] }(x) A[0](x) 和 A [ 1 ] ( x ) A^{ [1]} (x) A[1](x) : A [ 0 ] ( x ) = a 0 + a 2 x + a 4 x 2 + ⋯ + a n − 2 x n / 2 − 1 A [ 1 ] ( x ) = a 1 + a 3 x + a 5 x 2 + ⋯ + a n − 1 x n / 2 − 1 \begin{aligned} &A^{[0]} (x) = a_0 + a_2 x + a_4 x^2 + \dots + a_{n-2} x^{{n/2} - 1} \\ &A^{ [1]}(x) = a_1 + a_3 x + a_5 x^2 + \dots + a_{n-1} x^{n/2- 1}\end{aligned} A[0](x)=a0+a2x+a4x2+⋯+an−2xn/2−1A[1](x)=a1+a3x+a5x2+⋯+an−1xn/2−1 注意到, A [ 0 ] ( x ) A^{ [0] } (x) A[0](x) 包含 A A A 中所有偶数下标的系数(下标的相应二进制表示的最后一位为 0 0 0 ), A [ 1 ] ( x ) A^{ [1] }(x) A[1](x) 包含 A A A 中所有奇数下标的系数(下标的相应二进制表示的最后一位为 1 1 1 )。于是有: A ( x ) = A [ 0 ] ( x 2 ) + x A [ 1 ] ( x 2 ) (30.9) A(x) = A^{ [0] } (x^2) + x A^{ [1] } (x^2) \tag{30.9} A(x)=A[0](x2)+xA[1](x2)(30.9)
所以,求 A ( x ) A(x) A(x) 在 ω n 0 , ω n 1 , … , ω n n − 1 \omega^0_n, \omega^1_n, \dots, \omega^{n-1}_n ωn0,ωn1,…,ωnn−1 处的值的问题,转换为:
- 求次数界为 n / 2 n/2 n/2 的多项式 A [ 0 ] ( x ) A^{[0]}(x) A[0](x) 和 A [ 1 ] ( x ) A^{ [1] }(x) A[1](x) 在点: ( ω n 0 ) 2 , ( ω n 1 ) 2 , … , ( ω n n − 1 ) 2 (30.10) (\omega^0_n)^2, (\omega^1_n)^2, \dots, (\omega^{n-1}_n)^2 \tag{30.10} (ωn0)2,(ωn1)2,…,(ωnn−1)2(30.10) 的取值。
- 根据式30.9综合上述结果。
根据折半引理(作用就在这里!),式30.10并不是由 n n n 个不同值组成,而是仅由 n / 2 n/2 n/2 个 n / 2 n/2 n/2 次单位复数根组成,每个根正好出现 2 2 2 次( n n n 为 2 2 2 的整数幂,也是偶数)。因此,我们递归地对次数界为 n / 2 n/2 n/2 的多项式 A [ 0 ] ( x ) A^{[0]}(x) A[0](x) 和 A [ 1 ] ( x ) A^{[1]} (x) A[1](x) 、在 n / 2 n/2 n/2 个 n / 2 n/2 n/2 次单位复数根处进行求值。这些子问题与原始问题形式相同、但规模变为一半。
现在,我们已经成功地把一个
n
n
n 个元素的
D
F
T
n
\mathrm{DFT_n}
DFTn 计算,划分为两个规模为
n
/
2
n/2
n/2 个元素的
D
F
T
n
/
2
\mathrm{ DFT_{n/2}}
DFTn/2 计算。这一分解是下面递归FFT算法的基础,该算法计算出一个
n
n
n 元素向量
a
=
(
a
0
,
a
1
,
…
,
a
n
−
1
)
a = (a_0, a_1, \dots, a_{n-1})
a=(a0,a1,…,an−1) 的DFT(其中
n
n
n 是
2
2
2 的幂)。 注意,这里的长度
n
n
n 实际上是(算导30.1节)前面所指的
2
n
2n
2n ,因为我们需要在求值以前、加倍给定多项式的次数界,因此在多项式乘法的相关内容中,实际上处理的是
2
n
2n
2n 次单位根。
RECURSIVE-FFT(a)
n = a.length // n is a power of 2
if n == 1
return a
wn = e^(2πi/n)
w = 1
A0 = (a[0], a[2], ..., a[n-2]) // a中偶数下标的系数组成的多项式
A1 = (a[1], a[3], ..., a[n-1]) // a中奇数下标的系数组成的多项式
y0 = RECURSIVE-FFT(A0) // 递归FFT处理A0后得到的点值表达(偶数下标系数)
y1 = RECURSIVE-FFT(A1) // 递归FFT处理A1后得到的点值表达(奇数下标系数)
for k = 0 to (n/2-1)
y[k] = y0[k] + w * y1[k]
y[k + (n/2)] = y0[k] - w * y1[k]
w = w * wn
return y // y is assumed to be a column vector
RECURSIVE-FFT
的执行过程如下。前三行代表递归的基础:一个元素的DFT就是该元素自身,因为在这种情形下:
y
0
=
a
0
ω
1
0
=
a
0
⋅
1
=
a
0
y_0 = a_0 \omega_1^0 = a_0 \cdot 1 = a_0
y0=a0ω10=a0⋅1=a0 定义 Y0, Y1
的两行,定义了多项式
A
[
0
]
(
x
)
A^{[0]} (x)
A[0](x) 和
A
[
1
]
(
x
)
A^{[1]} (x)
A[1](x) 的系数向量。定义 wn, w
的两行和 w = w * wn
这一行,则保证了
ω
\omega
ω 可以正确更新,只要 y[k], y[k + (n/2)]
这两行被执行,就有
ω
=
ω
n
k
\omega = \omega_n^k
ω=ωnk(次次迭代中让
ω
\omega
ω 的值改变,可以节约每次通过for循环重新计算
ω
n
k
\omega^k_n
ωnk 的时间)。递归调用 RECURSIVE-FFT
的两行,执行递归计算
D
F
T
n
/
2
\mathrm{DFT_{n/2}}
DFTn/2 ,对于
k
=
0
,
1
,
…
,
n
/
2
−
1
k = 0, 1, \dots, n / 2-1
k=0,1,…,n/2−1 :
y
k
[
0
]
=
A
[
0
]
(
ω
n
/
2
k
)
y
k
[
1
]
=
A
[
1
]
(
ω
n
/
2
k
)
\begin{aligned} &y_k^{ [0] } = A^{[0]} (\omega^k_{n/2}) \\ &y_k^{ [1]} = A^{ [1] } ( \omega_{n/2}^k ) \end{aligned}
yk[0]=A[0](ωn/2k)yk[1]=A[1](ωn/2k) 或者根据消去引理,有
ω
n
/
2
k
=
ω
n
2
k
=
(
ω
n
k
)
2
\omega^k_{n/2} = \omega^{2k}_n = (\omega^{k}_n)^2
ωn/2k=ωn2k=(ωnk)2 ,于是
y
k
[
0
]
=
A
[
0
]
(
ω
n
2
k
)
y
k
[
1
]
=
A
[
1
]
(
ω
n
2
k
)
\begin{aligned} &y_k^{ [0] } = A^{[0]} (\omega^{2k}_{n}) \\ &y_k^{ [1]} = A^{ [1] } ( \omega_{n}^{2k} ) \end{aligned}
yk[0]=A[0](ωn2k)yk[1]=A[1](ωn2k) 更新 y[k], y[k + (n/2)]
的两行则综合了递归
D
F
T
n
/
2
\mathrm{DFT_{n/2}}
DFTn/2 的计算结果。对
y
0
,
y
1
,
…
,
y
n
/
2
−
1
y_0, y_1, \dots, y_{n/2-1}
y0,y1,…,yn/2−1 ,前一行推出:
y
k
=
y
k
[
0
]
+
ω
n
k
y
k
[
1
]
=
A
[
0
]
(
ω
n
2
k
)
+
ω
n
k
A
[
1
]
(
ω
n
2
k
)
=
A
(
ω
n
k
)
(
根
据
式
30.9
)
\begin{aligned} y_k &= y^{[0]}_k + \omega^k_n y_k^{[1]} \\ &= A^{[0]} (\omega^{2k}_n) + \omega^k_n A^{[1]} (\omega^{2k}_n) \\ &= A(\omega_n^k) \quad &(根据式30.9) \end{aligned}
yk=yk[0]+ωnkyk[1]=A[0](ωn2k)+ωnkA[1](ωn2k)=A(ωnk)(根据式30.9) 对
y
n
/
2
,
y
n
/
2
+
1
,
…
,
y
n
−
1
y_{n/2}, y_{n/2+1}, \dots, y_{n-1}
yn/2,yn/2+1,…,yn−1 ,设
k
=
0
,
1
,
…
,
n
/
2
−
1
k = 0, 1, \dots, n/2-1
k=0,1,…,n/2−1 ,后一行推出:
y
k
+
(
n
/
2
)
=
y
k
[
0
]
−
ω
n
k
y
k
[
1
]
=
y
k
[
0
]
+
ω
k
k
+
(
n
/
2
)
y
k
[
1
]
(
因
为
ω
n
k
+
(
n
/
2
)
=
−
ω
n
k
)
=
A
[
0
]
(
ω
n
2
k
)
+
ω
n
k
+
(
n
/
2
)
A
[
1
]
(
ω
n
2
k
)
=
A
[
0
]
(
ω
n
2
k
+
n
)
+
ω
n
k
+
(
n
/
2
)
A
[
1
]
(
ω
n
2
k
+
n
)
(
因
为
ω
n
2
k
+
n
=
ω
n
2
k
)
=
A
(
ω
n
k
+
(
n
/
2
)
)
(
根
据
式
30.9
)
\begin{aligned} y_{k+(n/2)} &= y_k^{[0]} - \omega^k_n y_k^{[1]} \\ &= y_k^{[0]} + \omega_k^{k + (n/2)} y_k^{[1]} \quad &(因为 \omega^{k+(n/2)}_n = - \omega^k_n) \\ &= A^{[0]} ( \omega^{2k}_n) + \omega^{k+(n/2)}_n A^{[1]} (\omega^{2k}_{n}) \\ &= A^{[0]} (\omega^{2k+n}_n) + \omega^{k+(n/2)}_n A^{[1]} (\omega^{2k+n}_n) \quad &(因为\omega^{2k+n}_n = \omega^{2k}_n) \\ &= A(\omega^{k+(n/2)}_n) \quad &(根据式30.9) \end{aligned}
yk+(n/2)=yk[0]−ωnkyk[1]=yk[0]+ωkk+(n/2)yk[1]=A[0](ωn2k)+ωnk+(n/2)A[1](ωn2k)=A[0](ωn2k+n)+ωnk+(n/2)A[1](ωn2k+n)=A(ωnk+(n/2))(因为ωnk+(n/2)=−ωnk)(因为ωn2k+n=ωn2k)(根据式30.9) 因此,由 RECURSIVE-FFT
返回的向量 y
确实是输入向量 a
的DFT。
更新 y[k], y[k + (n/2)]
的两行中,对
k
=
0
,
1
,
…
,
n
/
2
−
1
k = 0, 1, \dots, n / 2 - 1
k=0,1,…,n/2−1 ,每个值
y
k
[
1
]
y_k^{[1]}
yk[1] 都乘了
ω
n
k
\omega^k_n
ωnk 。前一行中这个乘积加到了
y
k
[
0
]
y_k^{[0]}
yk[0] 上,后一行又减去它。因为应用了每个因子
ω
n
k
\omega^k_n
ωnk 的正数形式和负数形式,我们把因子
ω
n
k
\omega^k_n
ωnk 称为旋转因子 twiddle factor
。
为了确定过程 RECURSIVE-FFT
的运行时间,注意到除了递归调用外,每次调用所需的时间为
Θ
(
n
)
\Theta(n)
Θ(n) ,其中
n
n
n 是输入向量的长度。因此,对运行时间有下列递归式:
T
(
n
)
=
2
T
(
n
/
2
)
+
Θ
(
n
)
=
Θ
(
n
log
n
)
T(n) = 2T(n/2) + \Theta(n) = \Theta(n\log n)
T(n)=2T(n/2)+Θ(n)=Θ(nlogn) 因此,采用快速傅里叶变换,我们可以在
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn) 时间内,求出次数界为
n
n
n 的多项式在
n
n
n 次单位复数根处的值。
3.4 在单位复数根处插值
现在我们展示,如何在单位复数根处插值、来完成多项式乘法方案,使得我们把一个多项式从点值表达转换回系数表达。如下方法进行插值:把DFT写成一个矩阵方程,然后再观察其逆矩阵的形式。
根据等式30.4,我们可以把DFT写成矩阵乘积
y
=
V
n
a
y = V_n a
y=Vna ,其中
V
n
V_n
Vn 是一个由
ω
n
\omega_n
ωn 适当幂次填充成的范德蒙矩阵:
[
y
0
y
1
y
2
y
3
⋮
y
n
−
1
]
=
[
1
1
1
1
…
1
1
ω
n
1
ω
n
2
ω
n
3
…
ω
n
n
−
1
1
ω
n
2
ω
n
4
ω
n
6
…
ω
n
2
(
n
−
1
)
1
ω
n
3
ω
n
6
ω
n
9
…
ω
n
3
(
n
−
1
)
⋮
⋮
⋮
⋮
⋱
⋮
1
ω
n
n
−
1
ω
n
2
(
n
−
1
)
ω
n
3
(
n
−
1
)
…
ω
n
(
n
−
1
)
(
n
−
1
)
]
[
a
0
a
1
a
2
a
3
⋮
a
n
−
1
]
\begin{bmatrix} y_0 \\ y_1 \\ y_2 \\ y_3 \\ \vdots \\ y_{n-1} \end{bmatrix} = \begin{bmatrix} 1 & 1 & 1 & 1 & \dots & 1 \\ 1 & \omega_n^1 & \omega_n^2 & \omega_n^3 & \dots & \omega_n^{n-1} \\ 1 & \omega_n^2 & \omega_n^4 & \omega_n^6 & \dots & \omega_n^{2(n-1)} \\ 1 & \omega_n^3 & \omega_n^6 & \omega_n^9 & \dots & \omega_n^{3(n-1)} \\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & \omega_n^{n-1} & \omega_n^{2(n-1)} & \omega_n^{3(n-1)} & \dots & \omega_n^{(n-1)(n-1)} \end{bmatrix} \begin{bmatrix} a_0 \\ a_1 \\ a_2 \\ a_3 \\ \vdots \\ a_{n-1} \end{bmatrix}
⎣⎢⎢⎢⎢⎢⎢⎢⎡y0y1y2y3⋮yn−1⎦⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡1111⋮11ωn1ωn2ωn3⋮ωnn−11ωn2ωn4ωn6⋮ωn2(n−1)1ωn3ωn6ωn9⋮ωn3(n−1)…………⋱…1ωnn−1ωn2(n−1)ωn3(n−1)⋮ωn(n−1)(n−1)⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤⎣⎢⎢⎢⎢⎢⎢⎢⎡a0a1a2a3⋮an−1⎦⎥⎥⎥⎥⎥⎥⎥⎤ 对
j
,
k
=
0
,
1
,
…
,
n
−
1
j,k = 0, 1, \dots, n - 1
j,k=0,1,…,n−1 ,
V
n
V_n
Vn 的
(
k
,
j
)
(k,j)
(k,j) 处元素为
ω
n
k
j
\omega_n^{kj}
ωnkj 。
V
n
V_n
Vn 中元素的指数组成一张乘法表。对于逆运算
a
=
D
F
T
n
−
1
(
y
)
a = \mathrm{DFT_n^{-1}} (y)
a=DFTn−1(y) ,我们把
y
y
y 乘以
V
n
V_n
Vn 的逆矩阵
V
n
−
1
V_n^{-1}
Vn−1 来进行处理。
定理30.7 对
j
,
k
=
0
,
1
,
…
,
n
−
1
j,k = 0, 1, \dots, n - 1
j,k=0,1,…,n−1 ,
V
n
−
1
V^{-1}_n
Vn−1 的
(
j
,
k
)
(j,k)
(j,k) 处元素为
ω
n
−
k
j
/
n
\omega^{-kj}_n / n
ωn−kj/n 。
证明:我们要做的只是证明,这种形式下
V
n
−
1
V
n
=
I
n
V_n^{-1} V_n = I_n
Vn−1Vn=In ,其中
I
n
I_n
In 为
n
×
n
n\times n
n×n 的单位矩阵。考虑
V
n
−
1
V
n
V_{n}^{-1}V_n
Vn−1Vn 中
(
j
,
j
′
)
(j, j')
(j,j′) 处的元素:
[
V
n
−
1
V
n
]
j
j
′
=
∑
k
=
0
n
−
1
(
ω
n
−
j
k
/
n
)
(
ω
n
k
j
′
)
=
∑
k
=
0
n
−
1
ω
n
k
(
j
′
−
j
)
/
n
[ V^{-1}_n V_n]_{jj'} = \sum^{n-1}_{k=0} ( \omega^{-jk}_n / n ) ( \omega_n^{kj'}) = \sum^{n-1}_{k=0} \omega^{k (j' - j)}_n / n
[Vn−1Vn]jj′=k=0∑n−1(ωn−jk/n)(ωnkj′)=k=0∑n−1ωnk(j′−j)/n 如果
j
′
=
j
j' = j
j′=j ,则此和为
1
1
1 ;否则,根据求和引理(引理30.6),此和为
0
0
0 。注意,只有
−
(
n
−
1
)
≤
j
′
−
j
≤
n
−
1
-(n-1) \le j' - j \le n - 1
−(n−1)≤j′−j≤n−1 ,使得
j
′
−
j
j' - j
j′−j 不能被
n
n
n 整除,才能应用求和引理。
■
\blacksquare
■
给定逆矩阵 V n − 1 V_n^{-1} Vn−1 ,可以推导出 D F T n − 1 ( y ) \mathrm{DFT_n^{-1}}(y) DFTn−1(y) : a j = 1 n ∑ k = 0 n − 1 ω n − j k y k ( j = 0 , 1 , … , n − 1 ) (30.11) a_j = \dfrac{1}{n} \sum^{n-1}_{k=0} \omega_n^{-jk} y_k\quad (j=0, 1, \dots, n-1) \tag{30.11} aj=n1k=0∑n−1ωn−jkyk(j=0,1,…,n−1)(30.11) 通过比较式30.8与式30.11,我们可以看到,对FFT算法进行如下修改就可以计算出逆DFT(算导练习30.2-4):把 a a a 与 y y y 互换,用 ω n − 1 \omega^{-1}_n ωn−1 替换 ω n \omega_n ωn ,并将计算结果的每个元素除以 n n n 。因此,我们也可以在 Θ ( n log n ) \Theta(n\log n) Θ(nlogn) 时间内计算出 D F T n − 1 \mathrm{DFT_n^{-1}} DFTn−1 。
我们可以看到,通过运用FFT与逆FFT,可以在 Θ ( n log n ) \Theta(n\log n) Θ(nlogn) 时间内、把次数界为 n n n 的多项式在其系数表达与点值表达之间进行相互转换。在矩阵乘法的相关内容中,已经说明了下面的结论。
定理30.8(卷积定理 Convolution theorem
)对任意两个长度为
n
n
n 的向量
a
,
b
a, b
a,b ,其中
n
n
n 是
2
2
2 的整数幂。
a
⊗
b
=
D
F
T
2
n
−
1
(
D
F
T
2
n
(
a
)
⋅
D
F
T
2
n
(
b
)
)
a\otimes b = \mathrm{DFT_{2n}^{-1} ( DFT_{2n}(a) \cdot DFT_{2n}(b))}
a⊗b=DFT2n−1(DFT2n(a)⋅DFT2n(b)) 其中,向量
a
,
b
a, b
a,b 用
0
0
0 填充、使其长度达到
2
n
2n
2n ,并用
⋅
\cdot
⋅ 表示两个
2
n
2n
2n 元向量的点乘。
■
\blacksquare
■
4. 高效FFT实现
因为DFT的实际应用(如信号处理)中需要尽可能快的速度,本节将探究两种高效的FFT实现方法。首先,讨论一种运行时间为 Θ ( n log n ) \Theta(n\log n) Θ(nlogn) 的FFT迭代实现方法,不过在此运行时间的 Θ \Theta Θ 记号,隐含的常数要比(算导30.2节)前面递归实现方法的常数小(如果实现精确,这个递归方法可能会更加高效地应用硬件缓存)。然后,深入分析迭代实现方法,设计出一个高效的并行FFT电路。
4.1 FFT的一种迭代实现
首先我们注意到,在 RECURSIVE-FFT
中,第10~13行的for循环中,包含了
ω
n
k
y
k
[
1
]
\omega^k_n y_k^{[1]}
ωnkyk[1] 的
2
2
2 次计算。在编译术语中,称该值为公用子表达式 common subexpression
。我们可以改变循环、使其仅计算一次,并将其存放在临时变量
t
t
t 中。
for k = 0 to n/2-1
t = w * Y1[k]
y[k] = Y0[k] + t
y[k + (n/2)] = Y0[k] - t
w = w * wn
在这个循环中,把旋转因子
ω
=
ω
n
k
\omega =\omega^k_n
ω=ωnk 乘以
y
k
[
1
]
y_k^{[1]}
yk[1] ,把所得乘积存入
t
t
t 中,然后从
y
k
[
0
]
y^{[0]}_k
yk[0] 中增加及减去
t
t
t ,这一系列操作称为一个蝴蝶操作 butterfly operation
,图30-3说明了执行步骤:
现在来说明,如何使FFT算法采用迭代结构、而非递归结构。在图30-4中,我们已把输入向量分配在「一次 RECURSIVE-FFT
调用中的各次递归调用中」 、形成一个树形结构 we have arranged the input vectors to the recursive calls in an invocation of RECURSIVE-FFT in a tree structure
,其中初始调用时有
n
=
8
n = 8
n=8 。树中的每个结点对应每次递归过程调用,由相应的输入向量标记。每次 RECURSIVE-FFT
调用产生两个递归调用,除非该调用已收到了
1
1
1 个元素的向量。第一次调用作为左孩子,第二次调用作为右孩子。
观察此树,我们注意到,如果把初始向量
a
a
a 中的元素按其在叶中出现次序进行安排,就可以对过程 RECURSIVE-FFT
的执行进行追踪,不过是自底向上、而非自顶向下。
- 首先,我们成对取出元素,利用一次蝴蝶操作计算出每对的DFT,然后用其DFT取代这对元素,这样向量中就包含了 n / 2 n/2 n/2 个二元素的DFT。
- 下一步,我们按对取出这 n / 2 n/2 n/2 个DFT,通过两次蝴蝶操作计算出含有四个元素向量的DFT,并用一个具有四个元素的DFT取代对应的两个二元素的DFT。于是,向量中包含 n / 4 n/4 n/4 个四元素的DFT。
- 继续进行这一过程,直到向量包含两个具有 n / 2 n/2 n/2 个元素的DFT,这时我们综合应用 n / 2 n/2 n/2 次蝴蝶操作,就可以合成最终的具有 n n n 个元素的DFT。
为了把这个自底向上的方法变为代码,采用了一个数组
A
[
0
…
n
−
1
]
A[0\dots n - 1]
A[0…n−1] ,初始时该数组包含输入向量
a
a
a 中的元素,其顺序为它们在图30-4中树叶出现的顺序(在后面说明如何确定这个顺序,这也称为位逆序置换 bit-reversal permutation
)。因为需要在树的每一层进行组合,于是引入一个变量
s
s
s 以计算树的层次,取值范围为从
1
1
1(在最底层,这时我们组合对来构成二元素的DFT)到
log
n
\log n
logn(在最顶层,这里我们要对两个具有
n
/
2
n/2
n/2 个元素的DFT进行组合,以产生最后结果)。因此,这个算法有如下结构:
我们可以用更精确的伪代码描述第3行中的循环主体部分。从子程序 RECURSIVE-FFT
中复制for循环,让
y
[
0
]
y^{[0]}
y[0] 与
A
[
k
…
k
+
2
s
−
1
−
1
]
A[k \dots k + 2^{s-1} - 1]
A[k…k+2s−1−1] 一致、
y
[
1
]
y^{[1]}
y[1] 与
A
[
k
+
2
s
−
1
…
k
+
2
s
−
1
]
A[k+2^{s - 1}\dots k + 2^{s} - 1]
A[k+2s−1…k+2s−1] 一致。在每次蝴蝶操作中,使用的旋转因子依赖于
s
s
s 的值,它是
ω
m
\omega_m
ωm 的幂,其中
m
=
2
s
m = 2^s
m=2s(引入变量
m
m
m 仅为使代码易读)。又引入另一个临时变量
u
u
u ,使得能恰当地执行蝴蝶操作。当用循环主体来取代第3行的整个结构时,就得到下面的伪代码,它是稍后将展示的、并行实现的基础。
这一代码首先调用辅助过程 BIT-REVERSE-COPY(a, A)
,把向量 a
按我们所需要的初始顺序复制到数组 A
中。BIT-REVERSE-COPY
如何做到这一点呢?在图30-4中,叶出现的顺序是一个位逆序置换。也就是说,如果让
r
e
v
(
k
)
rev(k)
rev(k) 为「
k
k
k 的二进制表示各位逆序」所形成的
log
n
\log n
logn 位的整数,那么我们希望把向量中的元素
a
k
a_k
ak 放在数组的
A
[
r
e
v
(
k
)
]
A[rev(k)]
A[rev(k)] 位置上。例如,图30-4中叶出现的次序为
0
,
4
,
2
,
6
,
1
,
5
,
3
,
7
0, 4, 2, 6, 1, 5, 3, 7
0,4,2,6,1,5,3,7 ,这个序列用二进制表示为
000
,
100
,
010
,
110
,
001
,
101
,
011
,
111
000, 100, 010, 110, 001, 101, 011, 111
000,100,010,110,001,101,011,111 ,当把二进制表示的各位逆序后,得到序列
000
,
001
,
010
,
011
,
100
,
101
,
110
,
111
000, 001, 010, 011, 100, 101, 110, 111
000,001,010,011,100,101,110,111 。为了获得一般情况下的位逆序置换,我们注意到,在树的最顶层,最低位为
0
0
0 的下标在左子树中、最低位为
1
1
1 的下标在右子树中;在每一层去掉最低位后,我们沿着树往下继续这一过程,直到在叶子得出「由位逆序置换给出的顺序」 at the top level of the tree, indices whose low-order bit is 0 go into the left subtree and indices whose low-order bit is 1 go into the right subtree. Stripping off the low-order bit at each level, we continue this process down the tree, until we get the order given by the bit-reversal permutation at the leaves
。由于很容易计算函数
r
e
v
(
k
)
rev(k)
rev(k) ,因此过程 BIT-REVERSE-COPY
相对简单。
调用 BIT-REVERSE-COPY(a, A)
的运行时间当然是
O
(
n
log
n
)
O(n\log n)
O(nlogn) ,因为迭代了
n
n
n 次,并可以在
O
(
log
n
)
O(\log n)
O(logn) 时间内、把一个
0
∼
n
−
1
0\sim n - 1
0∼n−1 之间的
log
n
\log n
logn 位整数逆序(实际上,通常事先知道
n
n
n 的初始值,我们就可以编制出一张表、把
k
k
k 映射为
r
e
v
(
k
)
rev(k)
rev(k) ,使 BIT-REVERSE-COPY
的运行时间为
Θ
(
n
)
\Theta(n)
Θ(n) ,且该式中隐含的常数因子较小)。此外,可以采用算导思考题17-1中描述的「聪明的摊还逆序二进制计数器方案」。
这种迭代的FFT实现方法的运行时间为
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn) 。为了完成 ITERATIVE-FFT
的运行时间是
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn) 的证明,需要说明最内层循环体(第
8
∼
13
8 \sim 13
8∼13 行)执行次数
L
(
n
)
L(n)
L(n) 为
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn) 。对
s
s
s 的每个值,第
6
∼
13
6\sim 13
6∼13 行的for循环迭代了
n
/
m
=
n
/
2
s
n / m = n / 2^s
n/m=n/2s 次、第
8
∼
13
8 \sim 13
8∼13 行的最内层循环迭代了
m
/
2
=
2
s
−
1
m / 2 = 2^{s - 1}
m/2=2s−1 次。因此:
L
(
n
)
=
∑
s
=
1
log
n
n
2
s
⋅
2
s
−
1
=
∑
s
=
1
log
n
n
2
=
Θ
(
n
log
n
)
L(n) = \sum^{ \log n}_{s = 1} \dfrac{n}{2^s} \cdot 2^{s - 1} = \sum^{\log n}_{s = 1} \dfrac{n}{2} = \Theta(n\log n)
L(n)=s=1∑logn2sn⋅2s−1=s=1∑logn2n=Θ(nlogn)
4.2 并行FFT电路
我们可以利用许多「允许我们实现一个高效的迭代FFT算法」的性质,来产生一个高效的并行FFT算法,并将其表示成一个电路。图30-5给出了 n = 8 n = 8 n=8 时,已知 n n n 个输入,一个并行FFT电路计算FFT。该电路开始时对输入进行位逆序置换,其后电路分为 log n \log n logn 级,每一级由 n / 2 n/2 n/2 个并行执行的蝴蝶操作组成。电路的深度定义为:任意的输入和任意的输出之间、最大的可以达到的计算元素数目,因此上面电路的深度为 Θ ( log n ) \Theta(\log n) Θ(logn) 。
并行FFT电路的最左边部分执行位逆序置换,其余部分模拟迭代的 ITERATIVE-FFT
过程。因为最外层for循环的每次迭代执行
n
/
2
n/2
n/2 次独立的蝴蝶操作,于是电路并行地执行它们。
- 在
ITERATIVE-FFT
内,每次迭代的值 s s s 对应于图30-5中的一个蝴蝶操作的阶段a stage of butterflies
。 - 对于
s
=
1
,
2
,
…
,
log
n
s = 1, 2, \dots, \log n
s=1,2,…,logn ,阶段
s
s
s 有
n
/
2
s
n/ 2^s
n/2s 组蝴蝶操作(对应于
ITERATIVE-FFT
中每个 k k k 值),每组中有 2 s − 1 2^{s - 1} 2s−1 个蝴蝶操作(对应于ITERATIVE-FFT
中的每个 j j j 值)。 - 图30-5所示的蝴蝶操作对应于最内层循环的蝴蝶操作(
ITERATIVE-FFT
的第 9 ∼ 12 9\sim 12 9∼12 行)。此外还要注意,蝴蝶中用到的旋转因子对应于ITERATIVE-FFT
中用到的那些旋转因子:在阶段 s s s ,我们使用 ω m 0 , ω m 1 , … , ω m m / 2 − 1 \omega^0_m, \omega^1_m, \dots, \omega^{m/2-1}_m ωm0,ωm1,…,ωmm/2−1 ,其中 m = 2 s m = 2^s m=2s 。