2 点 DFT 是 FFT 计算中的最基本单元。在这个最小的分解级别下,只需要进行简单的加法和乘法计算,无需再进一步分解。让我们通过一个更详细的解释来看清楚这一点。
2 点 DFT 的计算公式
假设我们有 2 个输入点,记为 x [ 0 ] x[0] x[0] 和 x [ 1 ] x[1] x[1]。根据离散傅里叶变换(DFT)的定义,2 点 DFT 的输出为:
X
[
0
]
=
x
[
0
]
+
x
[
1
]
X[0] = x[0] + x[1]
X[0]=x[0]+x[1]
X
[
1
]
=
x
[
0
]
−
x
[
1
]
X[1] = x[0] - x[1]
X[1]=x[0]−x[1]
这里的计算不需要复杂的旋转因子,因为对于 2 点 DFT,旋转因子 e − j 2 π 2 ⋅ k ⋅ n e^{-j \frac{2 \pi}{2} \cdot k \cdot n} e−j22π⋅k⋅n 的取值很简单。让我们具体看一看。
推导过程
对于 2 点 DFT,我们只需要计算两个输出点 X [ 0 ] X[0] X[0] 和 X [ 1 ] X[1] X[1],它们的定义如下:
-
当 k = 0 k = 0 k=0 时:
X [ 0 ] = x [ 0 ] + x [ 1 ] ⋅ e − j 2 π 2 ⋅ 0 ⋅ 1 = x [ 0 ] + x [ 1 ] X[0] = x[0] + x[1] \cdot e^{-j \frac{2 \pi}{2} \cdot 0 \cdot 1} = x[0] + x[1] X[0]=x[0]+x[1]⋅e−j22π⋅0⋅1=x[0]+x[1] -
当 k = 1 k = 1 k=1 时:
X [ 1 ] = x [ 0 ] + x [ 1 ] ⋅ e − j 2 π 2 ⋅ 1 ⋅ 1 = x [ 0 ] − x [ 1 ] X[1] = x[0] + x[1] \cdot e^{-j \frac{2 \pi}{2} \cdot 1 \cdot 1} = x[0] - x[1] X[1]=x[0]+x[1]⋅e−j22π⋅1⋅1=x[0]−x[1]
因此,2 点 DFT 的结果就是:
X
[
0
]
=
x
[
0
]
+
x
[
1
]
X[0] = x[0] + x[1]
X[0]=x[0]+x[1]
X
[
1
]
=
x
[
0
]
−
x
[
1
]
X[1] = x[0] - x[1]
X[1]=x[0]−x[1]
为什么不需要进一步分解?
在 2 点 DFT 的计算中,我们已经完成了所有必要的操作,只需一次加法和一次减法,无需更小的子问题,也不涉及复杂的复数乘法。这种简化是因为对于 2 点 DFT,旋转因子很简单:
- e − j 2 π 2 ⋅ 1 = − 1 e^{-j \frac{2 \pi}{2} \cdot 1} = -1 e−j22π⋅1=−1
因此,对于 2 点 DFT 的计算,所有旋转因子都已化简,不再需要复数乘法,这使得它成为 FFT 中的最小计算单元。
背景介绍
假设我们要计算一个长度为 M M M的离散傅里叶变换(DFT),公式如下:
X [ k ] = ∑ n = 0 M − 1 x [ n ] ⋅ e − j 2 π M k n X[k] = \sum_{n=0}^{M-1} x[n] \cdot e^{-j \frac{2 \pi}{M} k n} X[k]=n=0∑M−1x[n]⋅e−jM2πkn
这个直接计算的复杂度是 O ( M 2 ) O(M^2) O(M2),因为我们需要对每个频率 k k k 计算 M M M 次乘法和加法。
FFT通过递归分解将这个复杂度降为 O ( M log 2 ( M ) ) O(M \log_2(M)) O(Mlog2(M))。
示例:计算 8 点的 FFT
假设 M = 8 M = 8 M=8,我们有 8 个输入点 x [ 0 ] , x [ 1 ] , … , x [ 7 ] x[0], x[1], \ldots, x[7] x[0],x[1],…,x[7]。FFT 的核心思想是把计算分解成多个小的DFT,然后利用递归把大的问题转化成小的问题来解决。
第一步:分解信号
FFT 采用的分治法(称为“蝶形算法”)会把 M M M 点的数据分成偶数点和奇数点:
- 偶数点序列: x [ 0 ] , x [ 2 ] , x [ 4 ] , x [ 6 ] x[0], x[2], x[4], x[6] x[0],x[2],x[4],x[6]
- 奇数点序列: x [ 1 ] , x [ 3 ] , x [ 5 ] , x [ 7 ] x[1], x[3], x[5], x[7] x[1],x[3],x[5],x[7]
这样,我们可以把 8 点 DFT 分解成两个 4 点 DFT。公式变为:
X [ k ] = ∑ n = 0 3 x [ 2 n ] ⋅ e − j 2 π 8 k ⋅ 2 n + ∑ n = 0 3 x [ 2 n + 1 ] ⋅ e − j 2 π 8 k ⋅ ( 2 n + 1 ) X[k] = \sum_{n=0}^{3} x[2n] \cdot e^{-j \frac{2 \pi}{8} k \cdot 2n} + \sum_{n=0}^{3} x[2n+1] \cdot e^{-j \frac{2 \pi}{8} k \cdot (2n+1)} X[k]=n=0∑3x[2n]⋅e−j82πk⋅2n+n=0∑3x[2n+1]⋅e−j82πk⋅(2n+1)
我们可以将这个公式进一步简化为:
X [ k ] = DFT 4 ( 偶数点 ) + e − j 2 π 8 k ⋅ DFT 4 ( 奇数点 ) X[k] = \text{DFT}_{4}(\text{偶数点}) + e^{-j \frac{2 \pi}{8} k} \cdot \text{DFT}_{4}(\text{奇数点}) X[k]=DFT4(偶数点)+e−j82πk⋅DFT4(奇数点)
第二步:递归分解 4 点 DFT
对于每个 4 点 DFT,我们继续将其分解为两个 2 点 DFT:
- 偶数点的 4 点 DFT 分为两个 2 点 DFT。
- 奇数点的 4 点 DFT 也分为两个 2 点 DFT。
这样,原来的 8 点 DFT 被分解成 4 个 2 点 DFT。
第三步:计算 2 点 DFT
2 点 DFT 是最基本的计算单元,只需要一次复数乘法,因此不需要进一步分解。
第四步:合并计算结果(蝶形计算)
通过前面的步骤,我们得到了四个 2 点 DFT 的结果。我们再把这些小的 DFT 结果组合起来,逐步得到 4 点 DFT,然后是 8 点 DFT 的结果。
在这个过程中,我们使用了旋转因子 e − j 2 π M k e^{-j \frac{2 \pi}{M} k} e−jM2πk,称为“蝶形因子”,将偶数和奇数点组合。
递归的优势
每次分解后,问题的规模减少一半,最终复杂度降低到 O ( M log 2 ( M ) ) O(M \log_2(M)) O(Mlog2(M)),因为我们在每一层递归中只需要 O ( M ) O(M) O(M) 的计算量,而总共有 log 2 ( M ) \log_2(M) log2(M) 层。
对于快速傅里叶变换(FFT)算法,其计算复杂度通常可以通过复数乘法的次数来估计。如果我们考虑M点的FFT变换,FFT的计算复杂度约为:
M 2 ( log 2 ( M ) − 1 ) \frac{M}{2} \left(\log_2(M) - 1\right) 2M(log2(M)−1)
次复数乘法。
解释这个公式的来源
-
FFT的分解步骤:FFT是一种通过递归地将大规模的离散傅里叶变换分解为多个较小规模的傅里叶变换的方法。每次分解使得计算量减少,因此使用分治法逐渐减少计算复杂度。
-
二叉树结构的递归:每次分解会把原来的M点分成两组,每个包含 M 2 \frac{M}{2} 2M点。这样,整个过程会进行 log 2 ( M ) \log_2(M) log2(M)次递归,因为每次递归会将问题规模减半。
-
每层需要的复数乘法:在每层递归中,约需要 M 2 \frac{M}{2} 2M次复数乘法来计算“旋转因子”(也称为“蝶形计算”)。
-
总复数乘法次数:总的复数乘法次数就是每一层的复数乘法次数之和,因此可以得到复杂度近似为:
M 2 ( log 2 ( M ) − 1 ) \frac{M}{2} \left(\log_2(M) - 1\right) 2M(log2(M)−1)
这个公式表明了FFT相对于直接计算DFT的优势:直接计算DFT的复杂度为 O ( M 2 ) O(M^2) O(M2),而FFT的复杂度为 O ( M log 2 ( M ) ) O(M \log_2(M)) O(Mlog2(M))。