库利-图基快速傅里叶变换算法

库利－图基快速傅里叶变换算法（Cooley–Tukey FFT algorithm）是最常见的快速傅里叶变换算法。这一方法以分治法为策略递归地将长度为"N" = "N"1"N"2的DFT分解为长度分别为"N"1和"N"2的两个较短序列的DFT，以及与旋转因子的复数乘法。这种方法以及FFT的基本思路在1965年詹姆斯·库利和约翰·图基合作发表《"An algorithm for the machine calculation of complex Fourier series"》之后开始为人所知。但后来发现，实际上这两位作者只是重新发明了高斯在1805年就已经提出的算法（此算法在历史上数次以各种形式被再次提出）。
库利－图基算法最有名的应用，是将序列长为N的DFT分割为两个长为N/2的子序列的DFT，因此这一应用只适用于序列长度为2的幂的DFT计算，即基2-FFT。实际上，如同高斯和库利与图基都指出的那样，库利－图基算法也可以用于序列长度N为任意因数分解形式的DFT，即混合基FFT，而且还可以应用于其他诸如分裂基FFT等变种。尽管库利－图基算法的基本思路是采用递归的方法进行计算，大多数传统的算法实现都将显示的递归算法改写为非递归的形式。另外，因为库利－图基算法是将DFT分解为较小长度的多个DFT，因此它可以同任一种其他的DFT算法联合使用。
复杂度.
离散傅立叶变换的复杂度为formula_1（可参考大O符号）
快速傅立叶变换的复杂度为formula_2，分析可见下方架构图部分，级数为formula_3而每一级的复杂度为formula_4，故复杂度为formula_2
蝶形结.
在FFT演算法中，使用到的蝶形结可以分为Cooley-Tukey和Gentleman-Sande两种，而他们进行的运算互为对方的逆运算，同时对应了DFT和IDFT当中会使用到的运算单元。
在Cooley-Tukey蝶形结当中，上下两列的输出分别为：
而在Gentleman–Sande蝶形结当中，上下两列的输出分别为：
可以注意到在进行完一次Cooley-Tukey蝶形结和Gentleman–Sande蝶形结的运算之后，原先输入的(a, b)会变成(2a, 2b)，而这项常数倍的差异可以在所有的蝶形结运算结束后再处理，不需要直接改变这两项蝶形结运算单元的常数。
以下是这两种蝶形运算应用在快速傅立叶转换中性质的比较。
Gentleman-Sande 蝶形运算:.
而Utsav Banerjee等人在2019年发表的〈An Energy-Efficient Configurable Lattice Cryptography Processor for the Quantum-Secure Internet of Things〉提出了一个整合式蝶形运算单元的架构，同时包含了Cooley-Tukey蝶形结和Gentleman–Sande蝶形结的运算，以满足在硬体实作上同时兼顾两者的需求。
时域/频域抽取法.
在FFT演算法中，针对输入做不同方式的分组会造成输出顺序上的不同。如果我们使用时域抽取（Decimation-in-time），那么输入的顺序将会是位元反转排列（bit-reversed order），输出将会依序排列。但若我们采取的是频域抽取（Decimation-in-frequency），那么输出与输出顺序的情况将会完全相反，变为依序排列的输入与位元反转排列的输出。
时域抽取法.
我们可以将DFT公式中的项目在时域上重新分组，这样就叫做时域抽取（Decimation-in-time），在这里formula_10将会被代换为旋转因子（twiddle factor）formula_11。
formula_12
formula_13
formula_14
formula_15
formula_16
formula_17
在这边我们要注意的是，我们所替换的G与H具有周期性：
还注意到系数具有对称性：
上述的推导可以划成下面的图：
划红框处所示的formula_20点DFT架构如下图所示：
划红框处所示的formula_21点DFT架构如下图所示：
下图是一个8点DIT FFT的完整架构图。
频域抽取法.
我们可以将DFT公式中的项目在频域上重新分组，这样就叫做频域抽取（Decimation-in-frequency）。
首先先观察在频域上偶数项的部分：
formula_22
formula_23
formula_24
formula_25
formula_26
formula_27
formula_28
再观察在频域上奇数项的部分：
formula_29
formula_30
formula_31
formula_32
formula_33
formula_34
formula_35
formula_36
上述的推导可以画成下面的图：
更进一步的拆解formula_20-point DFT的架构
下图为8点FFT下formula_21-point DFT的架构
总结上述架构，完整的8点DIF FFT架构图为
单一基底.
利用库利－图基演算法进行离散傅立叶拆解时，能够依需求而以2, 4, 8…等2的幂次方为单位进行拆解，不同的拆解方法会产生不同层数快速傅里叶变换的架构，基底越大则层数越少，复数乘法器也越少，但是每级的蝴蝶形架构则会越复杂，因此常见的架构为2基底、4基底与8基底这三种设计。
2基底.
2基底-快速傅立叶演算法（Radix-2 FFT）是最广为人知的一种库利－图基快速傅立叶演算法分支。此方法不断地将N点的FFT拆解成两个'N/2'点的FFT，利用旋转因子formula_39的对称性借此来降低DFT的计算复杂度，达到加速的功效。
而其实前述有关时域抽取或是频域抽取的方法介绍，即为2基底的快速傅立叶转换法。以下展示其他种2基底快速傅立叶演算法的连线方法，此种不规则的连线方法可以让输出与输入都为循序排列，但是连线的复杂度却大大的增加。
4基底.
4基底快速傅立叶变换演算法则是承接2基底的概念，在此里用时域抽取的技巧，将原本的DFT公式拆解为四个一组的形式：
formula_12
formula_41
formula_42
formula_43
formula_44
formula_45
在这里再利用formula_46的特性来进行与2基数FFT类似的化减方法，以降低演算法复杂度。
8基底.
在库利－图基算法里，使用的基底（radix）越大，复数的乘法与记忆体的存取就越少，所带来的好处不言而喻。但是随之而来的就是实数的乘法与实数的加法也会增加，尤其计算单元的设计也就越复杂，对于可应用FFT之点数限制也就越严格。在表中我们可以看到在不同基底下所需的计算成本。
在DFT的公式中，我们重新定义x=X=[X(0),X(1),…,X(N-1)T，则DFT可重写为X=FN‧x。FN是一个N×N的DFT矩阵，其元素定义为∈ [0,N-1)，当N=8时，我们可以得到以下的F8矩阵并且进一步将其拆解。
在拆解成三个矩阵相乘之后，我们可以将复数运算的数量从56个降至24个复数的加法。底下是8基底的SFG。要注意的是所有的输出与输入都是复数的形式，而输出与输入的排序也并非规律排列，此种排列方式是为了要达到接线的最佳化。
混合基底.
2/8基底.
在2/8基底的演算法中，我们可以看到我们对于偶数项的输出会使用2基底的分解法，对于奇数项的输出采用8基底的分解法。这个做法充分利用了2基底与4基底拥有最少乘法数与加法数的特性。当使用了2基底的分解法后，偶数项的输出如下所示。
formula_47
奇数项的输出则交由8基底分解来处理，如下四式所述。
formula_48
formula_49
formula_50
formula_51
以上式子就是2/8基底的FFT快速演算法。在架构图上可化为L型的蝴蝶运算架构，如下图所示。
而下图表示的是一个64点的FFT使用2/8基底的架构图。虽然2/8基底的演算法缩减了formula_52的乘法量，但是这种演算法最大的缺点是跟其他固定基底或是混合基底比较起来，他的架构较为不规则。所以在硬体上比4基底或是2基底更难实现。
2/4/8基底.
为了改进Radix 2/8在架构上的不规则性，我们在这里做了一些修改，如下图4.。此修改可让架构更加规则，且所使用的加法器与乘法器数量更加减少，如下表所示。
在这里我们最小的运算单元称为PE（Process Element）,PE内部包含了2/8基底、2/4基底、2基底的运算，简化过的信号处理流程与蝴蝶型架构图可见下图
22基底.
基底的选择越大会造成蝴蝶形架构更加复杂，控制电路也会复杂化。因此Shousheng He和Mats Torkelson在1996提出了一个2^2基底的FFT演算法，利用旋转因子的特性：formula_53。而–j的乘法基本上只需要将被乘数的实部虚部对调，然后将虚部加上负号即可，这样的负数乘法被定义为'简单乘法'，因此可以用很简单的硬体架构来实现。利用上面的特性，22基底FFT能用串接的方式将旋转因子以4为单位对DFT公式进行拆解，将蝴蝶形架构层数降到log4N，大幅减少复数乘法器的用量，而同时却维持了2基底蝴蝶形架构的简单性，在效能上获得改进。22基底DIF FFT演算法的拆解方法如下列公式所述：
N点DFT公式：
formula_54
利用线性映射将n与k映射到三个维度上面
formula_55
然后套用Common Factor Algorithm（CFA）
formula_56
formula_57
而蝴蝶型架构会变成以下形式
formula_58
利用旋转因子formula_53的特性，可以观察出
formula_60
formula_61
再将此公式带入原式中可以得到
formula_62
formula_63
如上述公式所示，原本的DFT公式会被拆解成多个formula_64，而formula_64又可分为BF2I与BF2II两个阶层，分别会对应到之后所介绍的两种硬体架构。
一个16点的DFT公式经过一次上面所述之拆解之后可得下面的FFT架构
可以看出上图的架构保留了2基底的简单架构，然而复数乘法却降到每两级才出现一次，也就是formula_66次。而BF2I以及BF2II所对应的硬体架构下图：
其中BF2II硬体单元中左下角的交叉电路就是用来处理-j的乘法。
一个256点的FFT架构可以由下面的硬体来实现：
其中图下方的为一7位元宽的计数器，而此架构的控制电路相当单纯，只要将计数器的各个位元分别接上BF2I与BF2II单元即可。
下表将2基底、4基底与22基底演算法做一比较，可以发现22基底演算法所需要的乘法气数量为2基底的一半，加法弃用量是4基底的一半，并维持一样的记忆体用量和控制电路的简单性。
23基底.
如上所述，22演算法是将旋转因子formula_67视为一个简单乘法，进而由公式以及硬体上的化简获得硬体需求上的改进。而借由相同的概念，Shousheng He和Mats Torkelson进一步将旋转因子formula_68的乘法化简成一个简单乘法，而化简的方法将会在下面讲解。
formula_69乘法化简.
在2基数FFT演算法中的基本概念是利用旋转因子formula_39的对称性，4基数演算法则是利用
formula_46
的特性。但是我们会发现在这些旋转因子的对称特性中─
formula_72
─并没有被利用到。主要是因为formula_73的乘法运算会让整个FFT变得复杂，但是如果借由近似的方法，我们便可以将此一运算化简为12个加法。
formula_74
我们可以从上式注意到，formula_69可以被近似为五个加法的结果，所以formula_76就可以被简化为只有六个加法，再算入实部与虚部的计算，总共只需12个加法器就可以运用到此一简化特性。
经由与22基底类似的推导，并用串接的方式将旋转因子以8为单位对DFT公式进行拆解，23基底FFT演算法进一步将复数乘法器的用量缩减到log8N，并同时维持硬体架构的简单性。
推导的方法与22基底相当类似。借由这样的方法，23基底能将乘法器的用量缩减到2基底的1/3，并同时维持一样的记忆体用量以及控制电路的简单性。
一般性分解.
除了常在应用中见到与formula_77相关基底的拆解法，对于更加一般性的formula_78点离散傅立叶变换问题，
我们也有办法在理论上进行拆解，将问题化为数个formula_79与formula_80点离散傅立叶变换问题，并可对计算量进行估计。
而特别的是，透过互质在数论上的特性，对于formula_79与formula_80互质的情况，可以进一步节省一些运算，
在下面会特别分开讨论。
formula_83非互质.
为了避免之后的符号混淆，我们先将formula_83置换为formula_85，也就是说接著要将formula_86点离散傅立叶变换，
想办法拆解为数个formula_87与formula_88点离散傅立叶变换问题。
接著定义要拆分的问题，要拆分的问题为formula_4点离散傅立叶变换，将formula_90转换至formula_91：
直观地说，这个formula_4点离散傅立叶变换，将由formula_94作为参数的函数formula_90，转换成由formula_96作为参数的函数formula_91，
并且formula_98都有formula_4个可能的数值。
待定义好要拆分的问题，便可以开始讨论如何进行拆分，基本概念是将有formula_4个可能的数值的formula_98，
分别化为个使用两个参数进行描述的函数formula_102，并借此将原问题化为二维度离散傅立叶变换，
便可使用数个较小的离散傅立叶变换问题描述整个过程。
而一种很直觉的转换方式，便是透过将formula_98分别除以formula_104，
以商数与余数来做为参数描述formula_98的值：
其中formula_108作为将formula_94除以formula_87的商数，与作为formula_96除以formula_88的余数的formula_113相同，
具有formula_88个可能数值，同理formula_115与formula_116有formula_87个可能数值。
将上述的参数代换及formula_86带入原式，可以得到：
将右式的指数部分乘开并分项化简可以得到：
最后透过formula_121与formula_122，可以得到：
观察上式，并加上括号辅助厘清运算顺序我们可以得到：
最内层的运算可以视为将双参数函数formula_125中的一个参数formula_108，透过离散傅立叶变换取代为由formula_113描述，
得到一个新函数formula_128(这步因为对每个不同formula_115，都需要做一次将formula_108取代为formula_113的转换，
共需要formula_87个formula_88点离散傅立叶变换)：
下一层的运算则可视为单纯的乘法，将formula_135相乘，得到formula_136
(这步需要的计算量视formula_137的特殊性而会有变化)：
最后的运算可以视为将函数formula_139中formula_115，透过离散傅立叶变换取代为由formula_116描述，
得到一个新函数formula_142(这步因为对每个不同formula_113，都需要做一次将formula_115取代为formula_116的转换，
共需要formula_88个formula_87点离散傅立叶变换)：
就成功仅使用formula_87与formula_88点离散傅立叶变换，描述了原先的formula_4点离散傅立叶变换。
而在这样的分解下，我们使用了formula_87个formula_88点离散傅立叶变换与formula_88个formula_87点离散傅立叶变换与一些额外的乘法，
并且这些额外使用的复数乘法formula_156，
在电脑的运算架构中，如果formula_137是formula_158的倍数则不需要使用乘法，
如果formula_137是formula_160的倍数则仅需两个实数乘法，
其他则需三个实数乘法，所以总运算量可以如下方式表示：
其中formula_162是formula_163傅立叶所需乘法数，formula_164是formula_165傅立叶所需乘法数，
formula_166是需三个实数乘法formula_167组合个数，formula_168是需两个实数乘法formula_167组合个数。
而常见以formula_170为基底的分解则是为了使离散傅立叶变换所需乘法数为零，这样就仅需考虑上面提到的额外乘法，可以提高效率也有较简单的结构。
formula_83互质.
在formula_83互质的情况下，仍是采取和上面相近的思路来将问题进行拆分，首先，为了避免之后的符号混淆，我们同样将formula_83置换为formula_85。
接著同样定义要拆分的问题：
接著就是和上面的算法有最大差异的部分，在将formula_98化为个使用两个参数进行描述的函数formula_102时，
最直觉的作法便是使用商数和余数，但在formula_178互质的情况下，可以有一些其他更具技巧性的选择。
当formula_178互质，对所有formula_180我们可以找到唯一且不重复的一组formula_181使得：
其中formula_183，代表取余数的意思，formula_184是一个整数。
例如假设formula_185，则formula_186对应到的formula_187就是formula_188，
有formula_189。
并且对所有formula_181的组合(有formula_191组)，都对应到一个特定不重复的formula_94。
同理我们可以把formula_193表示为formula_194的双参数函数：
将上述的参数代换及formula_86带入原式，可以得到：
接著透过一次的展开化简及应用formula_121可得：
再将formula_86带入并再透过一次的展开化简及应用formula_121可得：
观察上式，并加上括号辅助厘清运算顺序我们可以得到：
内层的运算可以视为将双参数函数formula_204中的一个参数formula_108，
透过离散傅立叶变换取代为由一个与formula_113有关的变数formula_207描述，
得到一个新函数formula_208(这步因为对每个不同formula_115，都需要做一次将formula_108取代为formula_211的转换，
共需要formula_87个formula_88点离散傅立叶变换)：
外层的运算可以视为将函数formula_208中的参数formula_115，
透过离散傅立叶变换取代为由一个与formula_116有关的变数formula_218描述，
得到一个新函数formula_219(这步因为对每个不同formula_211，都需要做一次将formula_115取代为formula_222的转换，
共需要formula_88个formula_87点离散傅立叶变换)：
最后透过formula_91与formula_227在不同formula_194时的点点数值对应关系，
就成功仅使用formula_87与formula_88点离散傅立叶变换，描述了原先的formula_4点离散傅立叶变换。
而这个方法透过聪明的选取表达formula_98的方式，使得拆解的过程中完全不需要多余的乘法运算，
总运算量可以简单表示为：
其中formula_162是formula_163傅立叶所需乘法数，formula_164是formula_165傅立叶所需乘法数。
虽然这个方法可以较上面的方法节省运算量，
但这个方法也牵涉较为复杂的formula_98与formula_239转换，较为不直觉且不易理解，
也会遇到许多需要取余数的运算，可能会需要较大的空间建表进行查表法。
最后关于实际上要如何求得formula_94与formula_181的转换关系，
可以先透过辗转相除法取得一对特定的formula_242使得：
然后我们可以知道对于任意整数formula_244有：
然后就可以得到：
满足：