《算法导论》是我目前为止看到的最好的有关于写FFT的书,虽然和信号处理一类的树不太相同,算法导论对于FFT的出发点更加纯粹一点,那就是如果快速计算DFT,而并不深究DFT的物理意义,切入点也是多项式乘法。说实话,这个算法很难,并不是很直观,理解困难的很大原因是算法构造的如此精妙,以至于让人迷失了究竟算法的目的在何处,当然,现在很多库集成的FFT高效且稳定,不过深入学习一下FFT的原理我认为还是有必要的。
详细的原理就不说了,直接上python实现的代码
python由于支持了复数运算,所以和书中的伪代码基本差不多,无须做更多修改,计算结果和numpy中自带的FFT函数完全一致,当然,此算法只能应用于输入时2的幂次的时候。
其中第一个函数是递归版本的FFT,第二个则是迭代版本的FFT。
def recursiveFFT(a): n = len(a) if n == 1: return a wm = np.exp((2*np.pi*1j)/n) #旋转因子 #wm = np.cos(2*np.pi/n)+1j*np.sin(2*np.pi/n) # 欧拉展开后的旋转因子,与上式相同 w = 1 a0 = a[[x for x in range(0,n) if x%2 == 0]] # 提取出下标为偶数的系数 a1 = a[[x for x in range(0,n) if x%2 == 1]] # 提取出下标为奇数的系数 y0 = recursiveFFT(a0) # 递归调用 y1 = recursiveFFT(a1) y = np.empty(n,dtype=complex) #注意这里类型必须声明为complex型,否则运算时会自动舍去虚部 for k in range(0,n/2): # 归一 #t = w*y1[k] y[k] = y0[k]+(w*y1[k]) y[k+n/2] = y0[k] - w*y1[k] w = w*wm return y def rev(x,num): n = int(np.log2(num)) # 求出最大的二进制位数 ans = [] while x!= 0: ans.append(x%2) x /= 2 while len(ans)!= n: ans.append(0) # 空位补零 reval = 0 for index,i in enumerate(ans): reval += np.power(2,n-index-1)*i #二进制转十进制 return reval def bitReserveCopy(a): n = len(a) newA = np.empty(n,dtype=complex) # 此处必须声明类型为complex,否则下面的FFT运算会自动舍去虚部 for k in range(0,n): newA[rev(k,n)] = a[k] # 二进制逆序排列 return newA def MyFFT(a): A = bitReserveCopy(a) n = len(a) for s in range(1,int(np.log2(n))+1): # 遍历树的所有层 m = np.power(2,s) # m很关键,代表了接下来n次单位根中的n,也表示接下来遍历新序列时的步长 wm = np.exp((2*np.pi*1j)/m) for k in range(0,n,m): w = 1 for j in range(0,m/2): t = w*A[k+j+m/2] # 蝶形运算 u = A[k+j] A[k+j] = u+t A[k+j+m/2] = u-t w = w*wm return A x = np.random.rand(8) print x my1 = recursiveFFT(x) print my1 my2 = MyFFT(x) print my2 y = np.fft.fft(x) print y