这次接着上次说, 花了点时间把按时间抽取基-2的FFT思路用C++代码实现了下. 可以利用递归的思路来实现.
而且, 就像之前理解的FFT那样, 直接在一个序列的存储空间中进行操作, 这里存在vector中. 要实现这个特性, 调用时必须传入当前处理的那一段序列的序号范围, 所以第一次调用FFT时又套了个壳(myFFT). 有遗憾的是没有实现旋转因子的复用, 也就是重复出现的旋转因子都进行了重复计算.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include "mylib\mycomplex.h" #include <iostream> #include <vector> #include <cmath> using namespace std;using namespace mylib;vector<Complex> FFT (vector<Complex> &xn, int begin, int end) { int length = end - begin + 1 ; if (length > 2 ) { for (int i = begin + 1 ; i <= begin + length / 2 ; i++) { xn.insert (xn.end () - (xn.size () - end - 1 ), xn[i]); xn.erase (xn.begin () + i); } FFT (xn, begin, begin + length / 2 - 1 ); FFT (xn, begin + length / 2 , end); } Complex temp; for (int i = 0 ; i <= length / 2 - 1 ; i++) { temp = xn[begin + i]; xn[begin + i + length / 2 ] = xn[begin + i + length / 2 ] * Complex (cos (2 * i * PI / length), -sin (2 * i * PI / length)); xn[begin + i] = temp + xn[begin + i + length / 2 ]; xn[begin + i + length / 2 ] = temp - xn[begin + i + length / 2 ]; } return xn; } vector<Complex> myFFT (vector<Complex> &xn) { FFT (xn, 0 , xn.size () - 1 ); return xn; } int main (void ) { vector<Complex> list = { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }; myFFT (list); for (auto it = list.begin (); it < list.end (); it++) { cout << *it; } cout << endl; return 0 ; }
运算结果:1 (8.000 +0. j)(0 +0. j)(0 +0. j)(0 +0. j)(0 +0. j)(0 +0. j)(0 +0. j)(0 +0. j)
另外我在网上看到一个原生C接口的FFT函数库FFTW (the Fastest Fourier Transform in the West), 据说是世界上FFT的最快实现. Tutorial在这里 .
下面是一个使用示例.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include "fftw3.h" #include <iostream> #pragma comment(lib, "libfftw3-3.lib" ) #define N 8 using namespace std;int main (void ) { int i; fftw_complex *in, *out; fftw_plan p; in = (fftw_complex*)fftw_malloc (sizeof (fftw_complex) * N); out = (fftw_complex*)fftw_malloc (sizeof (fftw_complex) * N); for (i = 0 ; i < N; i++) { in[i][0 ] = 1 ; in[i][1 ] = 0 ; } p = fftw_plan_dft_1d (N, in, out, FFTW_FORWARD, FFTW_ESTIMATE); fftw_execute (p); for (i = 0 ; i < N; i++) { cout << "(" << in[i][0 ] << ", " << in[i][1 ] << "j)" ; } cout << endl; for (i = 0 ; i < N; i++) { cout << "(" << out[i][0 ] << ", " << out[i][1 ] << "j)" ; } cout << endl; fftw_destroy_plan (p); fftw_cleanup (); fftw_free (in); fftw_free (out); return 0 ; }
运算结果:1 2 (1 , 0 j)(1 , 0 j)(1 , 0 j)(1 , 0 j)(1 , 0 j)(1 , 0 j)(1 , 0 j)(1 , 0 j) (8 , 0 j)(0 , 0 j)(0 , 0 j)(0 , 0 j)(0 , 0 j)(0 , 0 j)(0 , 0 j)(0 , 0 j)
结果与我实现的FFT一样, 不过效率…
使用FFTW除需要先配置一下环境外, 还需要知道几个点:
需添加#include "fftw3.h"
, 这不用说了吧.
最好使用FFTW中实现的fftw_complex
复数变量, 当如果添加了#include <complex>
, 将会使用<complex>
文件中定义的复数变量.
fftw_complex
复数变量底层实现默认是个数组double[2]
, 依次存放了实虚部.
另一个比较重要的是fftw_plan
变量, 他就像是个机器, 把输入输出序列、序列长度以及其他一些参数传入, 使用fftw_execute(p)
函数启动FFT运算.
最后不能忘了清理资源, 务必使用fftw_free
函数释放资源.
FFTW除了能够处理一维序列的DFT, 还可以处理二维序列及其他功能, 参见Tutorial. . . . . . . . . . 終わり