1)
蝶形变换:普通的FFT算法称为基2的FFT算法,这种算法的核心是蝶形变换 长度为n=2^k1的变换共需要做 k1 * n/2 次蝶形变换,(如上图所示)若需变换数据表示为一个复数数组c[],则每次蝶形变换有2个输入 c[i],c[i+s],两个输出:c[i],c[i+s],s成为翅间距。 每个变换的基本算法是:
t=wr * c[i+s];
c[i+s]=c[i]-t;
c[i]=c[i]+t;
前面说过,长度为n=2^k1的变换共需要做 k1 * n/2次变换,实际的程序是一个3层循环,共需要k1*k2*(k3/2)次变换(k2*k3/2=n/2)。前面的wr是w的整数次方,w=e^(- 2*PI/k3) (k3=2,4,8,16...n,PI是圆周率),也成为旋转因子,例如n=32的变换需要log2(32)=5趟变换:
第1趟变换需要16*1次变换,翅间距是1, 若w=e^(-2*PI/2), 则wr=w^1
第2趟变换需要8*2次变换, 翅间距是2, 若w=e^(-2*PI/4), 则wr=w^1,w^2
第3趟变换需要4*2次变换, 翅间距是4, 若w=e^(-2*PI/8), 则wr=w^1,w^2,w^3,w^4
第4趟变换需要2*8次变换, 翅间距是8, 若w=e^(-2*PI/16),则wr=w^1,w^2,w^3,w^4,w^5,w^6,w^7,w^8
第5趟变换需要16*1次变换,翅间距是16, 若w=e^(-2*PI/32),则wr=w^1,w^2,w^3,w^4,w^5...w^15,w^16
void fft(struct complex c_IO[],int m) { int L, B, j, k, p, q; for(k = 0; k < SAMPLECOUNT; k++) { g_out[k] = c_IO[k]; } for(L = 1.0; L <= m; L++) { B=(int)pow(2.0, L-1); for(j=0;j<=B-1;j++) { p=j*(int)pow(2.0, m-L); q=(int)pow(2.0, L); for(k=j;k<=SAMPLECOUNT-1;k=k+q) { temp[k]=complex_add(g_out[k],complex_mult(W_n_k(SAMPLECOUNT,p) ,g_out[k+B] ) ); temp[k+B]=complex_remove(g_out[k],complex_mult(W_n_k(SAMPLECOUNT,p) ,g_out[k+B] ) ); g_out[k]=temp[k]; g_out[k+B]=temp[k+B]; } } } }
//************************************************************************* // * // * 函数名称: // * FFT() // * // * 参数: // * complex<double> * TD- 指向时域数组的指针 // * complex<double> * FD- 指向频域数组的指针 // * r-2的幂数,即迭代次数 // * // * 返回值: // * 无。 // * // * 说明: // * 该函数用来实现快速付立叶变换。 // * // ************************************************************************/ void FFT( complex<double> *TD, complex<double> *FD, complex<double> *X1, complex<double> *X2, complex<double> *Omega, int r) { // 付立叶变换点数 long count; // 循环变量 int i,j,k; // 中间变量 int bfsize,p; // 角度 double angle; complex<double> *X; // 计算付立叶变换点数 count = 1 << r; // 分配运算所需存储器 //Omega = new complex<double>[count / 2]; //X1 = new complex<double>[count]; //X2 = new complex<double>[count]; // 计算加权系数 for(i = 0; i < count / 2; i++) { angle = -i * 3.1415926 * 2 / count; Omega[i] = complex<double>(cos(angle), sin(angle)); } // 将时域点写入X1 memcpy(X1, TD, sizeof(complex<double>) * count); // 采用蝶形算法进行快速付立叶变换 for(k = 0; k < r; k++) { for(j = 0; j < 1 << k; j++) { bfsize = 1 << (r-k); for(i = 0; i < bfsize / 2; i++) { p = j * bfsize; X2[i + p] = X1[i + p] + X1[i + p + bfsize / 2]; X2[i + p + bfsize / 2] = (X1[i + p] - X1[i + p + bfsize / 2]) * Omega[i * (1<<k)]; } } X = X1; X1 = X2; X2 = X; } // 重新排序 for(j = 0; j < count; j++) { p = 0; for(i = 0; i < r; i++) { if (j&(1<<i)) { p+=1<<(r-i-1); } } FD[j]=X1[p]; } // 释放内存 //delete Omega; //delete X1; //delete X2; }
2)复数数组排序,在基2的蝶形变换中,复数数组需要重新排序,c[i] 要放置到数组c的第 reverse(c[i]) 的位置,m=reverse(n) 函数的算法是这样的:
若 n的 k位2进制的为b[], b[k-1],B[k-2],...b[2],b[1],b[0],( b[i] 等于1或者0,b[0]为最低bit). 则m=reverse(n)的2进制的为 b[0],b[1],b[2],b[3],...b[k-1] (b[k-1]为最低bit).
2.更复杂的变换算法:基2的蝶形变换算法不止一种,它可分为2类,一类为基2时分傅立叶变换,另一类为基2频分傅立叶变换。上例的变为基2时分算法,在每一趟变换中,翅间距依次变大,第一趟为2,最后一趟为n/2,数组重排在变换之前进行,基2频分算法正好相反,翅间距依次缩小,起于n/2,止于2,数组重排在蝶形变换之后进行。 在<傅立叶变换>一书中,提到3种基2时分变换,3种基2频分变换。上述算法称为基2时分FFT第二种算法。我在看你的这个程序的同时,还看到朱志刚写的一个FFT程序,这个程序的算法是基2时分FFT第一种算法,它比经典的算法更复杂,需要对wr数组进行逆序排列。
3.更复杂的FFT算法,除了基2 的FFT算法外,还有更加复杂的基4算法,基8算法,甚至基3,基5算法,纯粹的基4算法只能计算长度为4^k的变换,但它比基2的算法速度更高。为了提高速度,很多FFT算法使用混合基算法。如我看到的2个效率很高程序均使用了混合基算法。第一个程序是来自:http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html,它使用了基2,基4,甚至基8混合算法,共提供了6同样功能的算法。可对长度为2^k的序列做FFT,这个程序的写的很复杂,我现在尚不能完全看懂。另一个程序来自:http://hjem.get2net.dk/jjn/fft.htm。相对于前者,这个程序相对简单一点。它使用了基2,基3,基4,基5,基8,基10 混合算法,几乎可以计算任意长度的FFT。具体的,当序列长度n为2,3,5,7,11,13,17,19,23,29,31,37等小素数时,或者n的最大素因数小于等于37时,可计算这个序列的FFT。