快速傅里叶变换(FFT)算法的实现及优化
By Long Luo
之前的文章 快速傅里叶变换(FFT)算法 详细介绍了 \(\textit{FFT}\) 的具体实现,也实现了分治版的 \(\textit{FFT}\)。
分治版 \(\textit{FFT}\) 代码很容易理解,但递归会消耗 \(O(\log n)\) 的栈空间,同时代码实现中还有很多优化空间,这篇文章我们就来优化 \(\textit{FFT}\) 下的实现。
Cooley-Tukey FFT Algorithm
\(\textit{Cooley-Tukey FFT Algorithm}\) 1 通过使用分治算法2 实现将复杂问题简单化。
FFT 分治过程

具体操作流程可以分为 \(3\) 步:
- Divide:按照奇偶分为 \(2\) 个子问题。
- Conquer:递归处理 \(2\) 个子问题
- Combine:合并 \(2\) 个子问题。
蝴蝶操作
分治的前 \(2\) 步之前已经详细讲述过了,第 \(3\) 步合并操作是通过蝴蝶操作(Butterfly Operations)来实现的,其示意图如下所示:
蝴蝶操作可能会让人看了一头雾水,不过没关系,我们一步一步来,彻底弄懂她!
空间优化
回顾之前的 递归版代码 ,在合并这一步,这一层有 \(n\) 项需要处理,我们新建了一个数组 \(y(n)\),这是为什么呢?
我们可以复用之前的 \(y_e\) 或 \(y_o\) 数组以降低空间复杂度吗?
先看下合并需要做的操作:
\[ y(k) = y_e(k) + \omega_{n}^{k} \cdot y_o(k + \frac{n}{2}) \]
\[ y(k + \frac{n}{2}) = y_e(k) - \omega_{n}^{k} \cdot y_o(k + \frac{n}{2}) \]
很明显,我们如果复用 \(y_e\) 或 \(y_o\) 数组的话,那 \(y(k)\) 和 \(y(k + \frac{n}{2})\) 至少有一个数据会受影响,所以我们需要额外的 \(y(n)\) 数组来存储数据。
那么有没有办法来做到复用 \(y_e\) 或 \(y_o\) 数组呢?
当然可以!
我们只要将修改下合并顺序,加入一个临时变量 \(t = \omega_{n}^{k} \cdot y_e(k + \frac{n}{2})\) ,合并过程就可以在原数组中进行:
\[ cd \; t = \omega_{n}^{k} \cdot y_e(k+\frac{n}{2}) \]
\[ y_e(k+\frac{n}{2}) = y_e(k) - t \]
\[ y_e(k) = y_e(k)+t \]
这样就可以原地进行了,不再需要额外数组。