7

Classic Fast Fourier Transfrom (FFT) works fine, when $n$ is power of 2. How to generalize FFT procedure when $n$ is power of 3? Is it possible to easily modify the algorithm and preserve its correctness?

Thanks in advance.

  • 1
    See http://math.stackexchange.com/questions/77118/non-power-of-2-ffts. Basically you do a 4 element FFT, and then transform the result. – Jay Lemmon Jan 21 '14 at 21:27

4 Answers4

6

FFT is defined for every decomposition to prime factors, i.e., if $$ n=p_1^{r_1}\cdots p_k^{r_k}, $$ then the FFT of an $n$-vector is definable in $r_1+\cdots+r_k$ steps:

Step 1. We create a $p_1$-vector,

Step 2. We create a $p_1^2$-vector,...,

Step $r_1$. We create a $p_1^{r_1}$-vector,...,

Step $r_1\!+\!1$. We create a $p_1^{r_1}p_2$-vector, etc.

  • 2
    Yes, and here comes the "how to easily modify" part? Easily?Personally, I wouldn't be satisfied with this answer, but it's up to the OP to judge. – Han de Bruijn Jan 21 '14 at 15:41
2

Let me try to explain my view point for the recursive step for the FFT computation. First define a few things.

Let $F_N=[\omega_n^{2\pi ikl/N}]_{k,l\leq N}$, be the FFT matrix of order $N$ and $\omega_N = e^{2\pi i /N}$ is an $N$'th root of unity.

Let $A\in{\mathbb R}^{m_1\times n_1}$ and $B\in{\mathbb R}^{m_1\times n_1}$ then the Kronecker product $A\otimes B$ and box product $A\boxtimes B$ are defined by $$ (A\otimes B)_{(i-1)m_2+j,(k-1)n_2+l}=a_{ik}b_{jl}=(A\otimes B)_{(i,j),(k,l)} $$ and $$ (A\boxtimes B)_{(i-1)m_2+j,(k-1)n_1+l}=a_{il}b_{jk}=(A\otimes B)_{(i,j),(k,l)} $$ These two operations are useful because $(A\otimes B)\mathrm{vec}(X)=\mathrm{vec}(AXB)$ and $(A\boxtimes B)\mathrm{vec}(X)=\mathrm{vec}(AX^\top B)$.

Now define $$ V_{mk}(\alpha)=\begin{pmatrix} 1 & 1 & 1 & \cdots & 1\\ 1 & \alpha & \alpha^2 & \cdots & \alpha^{k-1}\\ 1 & \alpha^2 & \alpha^4 & \cdots & \alpha^{2(k-1)}\\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & \alpha^{m-1} & \alpha^{2(m-1)} & \cdots & \alpha^{(m-1)(k-1)} \end{pmatrix} $$ then if $N=km$ $$ F_N=(F_k\otimes I_m)\mathrm{diag}(V_{mk}(\omega_N))(I_k\boxtimes F_m) $$ To compute the FFT of a signal $x$ we simply need to compute $F_N x$. Now reshape $x$ into a matrix $X$ such that $x=\mathrm{vec}(X)$ then we can use the rules for the Kronecker and box product to compute the matrix vector product efficiently: $$ F_N \mathrm{vec}(X) = \mathrm{vec}((V_{mk}(\omega_N))\circ (F_m X^\top))F_k^\top), $$ where $\circ$ denotes element-wise multiplication. Instead of taking $O(N^2)=O(k^2m^2)$ operations this expression does the job in $O(km(k+m))$ operations. Now simply repeat the procedure to factorize $F_k$ and $F_m$ recursively.

Peder
  • 2,232
  • But is it still a fast Fourier transform ? – Lierre Jan 22 '14 at 20:24
  • Technically FFT is only for powers of two, but if you recursively apply the formula above you get the standard FFT algorithm. SPIRAL, which is the fastest FFT implementation, used the existence of different factorizations like this to generate super-fast implementations of DFT for various sizes of N. So, yes, it's still a fast Fourier transform. – Peder Jan 23 '14 at 00:19
1

I don't know what you want to do with FFT, so this might not be relevant for you,

A nice generalization of FFT to arbitrary size, not just powers of two, is the truncated Fourier transform, introduced by Joris van der Hoeven which behaves well for all size, and smoothes the jumps of the FFT. It applies very well to multiplication algorithms (polynomial, integers) based on Fourier transform.

Link to the paper : http://www.texmacs.org/joris/issac04/issac04.pdf

Lierre
  • 4,500
0

Disappointed that it took so long to properly answer this. The question is simple and the algorithm it is asking to implement isn't that hard. I just implemented it in Python. Pasting the code below. Will add more explanation later, but reading chapter 30 of the book "Introduction to algorithms" by CLRS should make things clear.


def fft_3(x):
    """
    Perform a fast Fourier transform
    on the given array of complex numbers.
    """ 
    n = len(x)
    if n == 1: 
        return x
    # The complex roots of unity.
    omega_n = np.exp(1j*2*np.pi/n)
    omega = 1+0j
    # Extact the even indices from array.
    y_0 = fft_3(x[::3]) 
    # Extract the odd indices from array.
    y_1 = fft_3(x[1::3])
    y_2 = fft_3(x[2::3]) 
    # The result array.
    y = [0]*n
    # The step that merges the two inputs into the
    # final answer. This step takes O(n) time since
    # it is just a single loop.
    for k in range(n//3):
        y[k] = y_0[k]+omega*y_1[k]+omega**2*y_2[k]
        y[k+n//3] = y_0[k]+np.exp(2*np.pi*1j/3)*omega*y_1[k]+np.exp(4*np.pi*1j/3)*omega**2*y_2[k]
        y[k+(2*n)//3] = y_0[k]+np.exp(4*np.pi*1j/3)*omega*y_1[k]+np.exp(2*np.pi*1j/3)*omega**2*y_2[k]
        omega = omega*omega_n
return y

Just like the original FFT in most books only works on powers of $2$ and will produce incorrect results otherwise, this version will only work on powers of $3$. A basic sanity check:

import numpy as np

fft_3([1,2,3,4,5,6,7,8,9]) np.fft.fft([1,2,3,4,5,6,7,8,9])

The sign of the complex part of my algorithm is the opposite of the numpy version, but that is probably an artifact of convention and can be switched trivially.

Rohit Pandey
  • 7,547