logo

The Fast Fourier Transform Algorithm 📂Fourier Analysis

The Fast Fourier Transform Algorithm

Overview1

The Discrete Fourier Transform (DFT), when computed naively following its mathematical definition, has a time complexity of O(N2)\mathcal{O}(N^{2}). However, by using the algorithm described below, the time complexity can be reduced to O(Nlog2N)\mathcal{O}(N\log_{2}N). This efficient computation method of the Discrete Fourier Transform is known as the Fast Fourier Transform (FFT).

Buildup

Let’s define multiplying two numbers and then adding them to another number as one operation. To compute the value of i=0n1anbn\sum\limits_{i=0}^{n-1}a_{n}b_{n}, nn operations are needed.

n=00anbb=a0b0=0+a0×b01 operationn=01anbb=a0b0+a1b1=(0+a0×b01 operation)+a1×b12 operationsn=02anbb=a0b0+a1b1+a2b2=((0+a0×b01 operation)+a1×b12 operations)+a2×b23 operations \begin{align*} \sum\limits_{n=0}^{0} a_{n}b_{b} &= a_{0}b_{0} = \overbrace{0 {\color{red} +} a_{0} {\color{red} \times} b_{0}}^{\color{red} 1 \text{ operation}} \\ \sum\limits_{n=0}^{1} a_{n}b_{b} &= a_{0}b_{0} + a_{1}b_{1} = \overbrace{\big( \overbrace{0 {\color{red} +} a_{0} {\color{red} \times} b_{0}}^{\color{red} 1 \text{ operation}} \big) {\color{#5882FA} + } a_{1} {\color{#5882FA} \times} b_{1}}^{\color{#5882FA}2 \text{ operations}} \\ \sum\limits_{n=0}^{2} a_{n}b_{b} &= a_{0}b_{0} + a_{1}b_{1} + a_{2}b_{2} = \overbrace{\bigg( \overbrace{\big( \overbrace{0 {\color{red} +} a_{0} {\color{red} \times} b_{0}}^{\color{red} 1 \text{ operation}} \big) {\color{#5882FA} + } a_{1} {\color{#5882FA} \times} b_{1}}^{\color{#5882FA}2 \text{ operations}} \bigg) {\color{#FE9A2E} + } a_{2} {\color{#FE9A2E} \times} b_{2}}^{\color{#FE9A2E}3 \text{ operations}} \\ \end{align*}

Now, recall the definition of the Discrete Fourier Transform.

The linear transformation FN:CNCN\mathcal{F}_{N} : \mathbb{C}^{N} \to \mathbb{C}^{N} is called the Discrete Fourier Transform.

FN(a)=a^=[a^0a^1a^N1],a^m=n=0N1ei2πmn/Nan(0m<N)(1) \mathcal{F}_{N}(\mathbf{a}) = \hat{\mathbf{a}} = \begin{bmatrix} \hat{a}_{0} \\ \hat{a}_{1} \\ \dots \\ \hat{a}_{N-1} \end{bmatrix} ,\quad \hat{a}_{m} = \sum_{n=0}^{N-1}e^{-i2\pi mn /N} a_{n}\quad (0\le m < N) \tag{1}

Where a=[a0a1aN1]T\mathbf{a} = \begin{bmatrix} a_{0}& a_{1}& \dots& a_{N-1} \end{bmatrix}^{T}.

To compute a^m\hat{a}_{m}, NN operations are needed, and to compute a^\hat{\mathbf{a}}, this must be performed NN times. Therefore, the total number of operations needed for the Discrete Fourier Transform is N2N^{2}, which means it has a time complexity of O(N2)\mathcal{O}(N^{2}). This implies a significant computational cost for Fourier Transforms from a computer calculation perspective.

Algorithm

Let’s assume the length of the data, NN, is a composite number N=N1N2N = N_{1}N_{2}. Now, define the indices m,nm, n as follows.

m=mN1+m,n=nN2+n m = m^{\prime}N_{1} + m^{\prime \prime},\quad n = n^{\prime}N_{2} + n^{\prime \prime}

Then, 0m,nN210 \le m^{\prime}, n^{\prime \prime} \le N_{2}-1, and 0m,nN110 \le m^{\prime \prime}, n^{\prime} \le N_{1}-1. Let’s express the exponent part of (1)(1) as follows.

ei2πmn/N=ei2π(mN1+m)(nN2+n)/(N1N2)=ei2π(mnN1N2+mnN1+mnN2+mn)/(N1N2)=ei2πmnei2π((mn/N2)+(mn/N1)+(mn/N))=ei2π((mn/N2)+(mn/N1)+(mn/N)) \begin{align*} e^{-i2\pi mn /N} &= e^{-i2\pi (m^{\prime}N_{1} + m^{\prime \prime})(n^{\prime}N_{2} + n^{\prime \prime})/(N_{1}N_{2})} \\ &= e^{-i2\pi (m^{\prime}n^{\prime}N_{1}N_{2} + m^{\prime}n^{\prime \prime}N_{1} + m^{\prime \prime}n^{\prime}N_{2} + m^{\prime \prime}n^{\prime \prime})/(N_{1}N_{2})} \\ &= e^{-i2\pi m^{\prime}n^{\prime}} e^{-i2\pi \big( (m^{\prime}n^{\prime \prime}/N_{2}) + (m^{\prime \prime}n^{\prime}/N_{1}) + (m^{\prime \prime}n^{\prime \prime}/N) \big)} \\ &= e^{-i2\pi \big( (m^{\prime}n^{\prime \prime}/N_{2}) + (m^{\prime \prime}n^{\prime}/N_{1}) + (m^{\prime \prime}n^{\prime \prime}/N) \big)} \\ \end{align*}

Substituting this into (1)(1) gives,

a^m=n=0N1ei2πmn/Nan=n=0N21n=0N11ei2π((mn/N2)+(mn/N1)+(mn/N))anN2+n=n=0N21[n=0N11ei2πmn/N1anN2+n]ei2π[(mn/N2)+(mn/N)] \begin{align*} \hat{a}_{m} &= \sum_{n=0}^{N-1}e^{-i2\pi mn /N} a_{n} \\ &= \sum_{n^{\prime \prime}=0}^{N_{2}-1} \sum_{n^{\prime}=0}^{N_{1}-1} e^{-i2\pi \big( (m^{\prime}n^{\prime \prime}/N_{2}) + (m^{\prime \prime}n^{\prime}/N_{1}) + (m^{\prime \prime}n^{\prime \prime}/N) \big)}a_{n^{\prime}N_{2}+n^{\prime \prime}} \\ &= \sum_{n^{\prime \prime}=0}^{N_{2}-1} \left[ \sum_{n^{\prime}=0}^{N_{1}-1} e^{-i2\pi m^{\prime \prime}n^{\prime}/N_{1}}a_{n^{\prime}N_{2}+n^{\prime \prime}} \right] e^{-i2\pi [ (m^{\prime}n^{\prime \prime}/N_{2}) + (m^{\prime \prime}n^{\prime \prime}/N) ] } \end{align*}

According to the equation above, calculating [n=0N11]\left[ \sum_{n^{\prime}=0}^{N_{1}-1} \right] inside each bracket requires N1N_{1} operations, and computing n=0N21\sum_{n^{\prime \prime}=0}^{N_{2}-1} outside each bracket requires N2N_{2} operations. Therefore, to calculate a^m\hat{a}_{m}, a total of (N1+N2)(N_{1} + N_{2}) operations are needed. To obtain a^\hat{\mathbf{a}}, this must be repeated NN times, resulting in a total cost of N(N1+N2)N(N_{1} + N_{2}), which is a reduction from N2N^{2}.

Upon closer inspection of the brackets, it is apparent that if N1N_{1} is again a composite number, the same logic can be applied. Generally, when the length of the data is a composite number N=N1N2NkN = N_{1} N_{2} \cdots N_{k}, the time complexity is reduced as follows.

O(N2)O(N(N1+N2++Nk)) \mathcal{O}(N^{2}) \searrow \mathcal{O}\big( N(N_{1} + N_{2} + \cdots + N_{k}) \big)

Now, let’s assume NN is a power of 22, N=2kN = 2^{k}. Then log2N=k\log_{2}N = k, and from N2=2kN^{2} = 2^{k}, it is reduced to 2k(2k)2^{k}(2k), hence the time complexity is reduced as follows.

O(N2)O(2Nlog2N) \mathcal{O}(N^{2}) \searrow \mathcal{O}(2N \log_{2}N)

Aside

This method was proposed in 1965 by Cooley and Tukey2, hence it is also called the Cooley-Tukey algorithm. However, they were not the first to invent it. Gauss also researched a similar algorithm but did not publish it properly, so this fact became known later3.


  1. Gerald B. Folland, Fourier Analysis and Its Applications (1992), p252-253 ↩︎

  2. J. W. Cooley and J. W. Tukey, An algorithm for the machine calculation of complex Fourier series, Mathematics of Computation 19 (1965), 297-301. ↩︎

  3. M. T. Heideman, D. H. Johnson, and C. S. Burms, Gauss and the history of the fast Fourier transform, Archive for the History of the Exact Sciences 34 (1985), 264-277. ↩︎