1、直接计算DFT的问题
设x(n)为N点有限长序列,其DFT为:
一般来说,x(n)和都是复数,X(k)也是复数,。因此每计算一个X(k)值,需要N次复数乘法以及(N-1)次复数加法。而X(k)一共有N个点,所以完成整个DFT运算总共需要次复数乘法及N(N-1)次复数加法。N=4时,有:
这样,(1)利用这些特性,使DFT运算中有些项可以合并;
(2)利用的对称性和周期性,可以将长序列的DFT分解为短序列的DFT。前面已经说过,DFT的运算量与成正比,所以N越小越有利。
快速傅立叶变换就是在这种思路下发展起来的。下面将详细介绍。
3、FFT(快速傅里叶)算法
FFT算法的基本思想:
利用DFT系数的特性,合并DFT运算中的某些项; 把长序列DFT→短序列DFT,从而减少运算量。
FFT算法分类: 时间抽选法 DIT: Decimation-In-Time
频率抽选法 DIF: Decimation-In-Frequency
4、按时间抽选的基2-FFT算法
1、算法原理
设输入序列长度为N=(M为正整数,将该序列按时间顺序的奇偶分解为越来越短的子序列,称为基2按时间抽取的FFT算法。也称为Coolkey-Tukey算法。其中基2表示:N=,M为整数.若不满足这个条件,可以人为地加上若干零值(加零补长)使其达到 N=。
2、算法步骤
(1)分组,变量置换
先将x(n)按n的奇偶分为两组,作变量置换:
当n=偶数时,令n=2r;
当n=奇数时,令n=2r+1;
得到:
(2)带入DFT中
3、算法比较
DIT―FFT算法与直接计算DFT运算量的比较
4、运算规律及编程思想
1) 原位运算 (亦称同址计算)
FFT的每级(列)计算都是由N个复数数据(输入)两两构成一个蝶型(共N/2个蝶形)运算而得到另外N个复数数据(输出)。当数据输入到存储器以后,每一组运算的结果,仍然存放在这同一组存储器中直到最后输出。
2)旋转因子的变化规律
如上所述,N点DIT―FFT运算流图中,每级都有N/2个蝶形。每个蝶形都要乘以因子,称其为旋转因子,p称为旋转因子的指数。观察FFT运算流图发现,第L级共有个不同的旋转因子。N==8时的各级旋转因子表示如下:
3)编程思想及流程图
4)码位倒序
由N=8蝶形图看出:原位计算时,FFT输出的X(k)的次序正好是顺序排列的,即X(0)…X(7),但输入x(n)都不能按自然顺序存入到存储单元中,而是按x(0),x(4),x(2),x(6) ,x(1),x(5),x(3),x(7)的顺序存入存储单元,即为乱序输入,顺序输出。
这种顺序看起来相当杂乱,然而它是有规律的。即码位倒读规则。以N=8为例:
看出:码位倒读后的顺序刚好是数据送入计算机内的顺序。
分析:
对于数N,在其二进制最高位加1,等于加N/2。
若已知某个反序号为J,为求下一个反序号,可先判J的最高位:
1) 若为0,则把该位变成1(即加N/2)就得到下
一个反序号,
2) 若为1,则需判断次高位:
① 若次高位为0,则把最高位变0(相当减去
N/2)后,再把次高位变1(即加N/4)。
② 若次高位为1,则需判断次次高位……
5、算法实现
/************FFT***********/
#include
#include
#include
#define N 1000
typedef struct//复数的定义
{
double real;//复数的实部
double img;//复数的虚部
}complex;
void fft(); /*快速傅里叶变换*/
void ifft(); /*快速傅里叶逆变换*/
void initW();//快速核变换
void change();//将x(n)倒置
void add(complex ,complex ,complex *); /*复数加法*/
void mul(complex ,complex ,complex *); /*复数乘法*/
void sub(complex ,complex ,complex *); /*复数减法*/
void divi(complex ,complex ,complex *);/*复数除法*/
void output(); /*输出结果*/
complex x[N], *W; /*输出序列的值*/
int size_x=0; /*输入序列的长度,只限2的N次方*/
double PI;
int main()
{
int i,method;
system("cls");
PI=atan(1)*4; /*pi等于4乘以1.0的正切值*/
printf("Please input the size of x:\n"); /*输入序列的长度*/
scanf("%d",&size_x);
printf("Please input the data in x[N]:(such as:5 6)\n"); /*输入序列对应的值*/
for(i=0;i0 )
{
j=j<<1; //左移符号,把相关值的二进制位向左移动,右边空出的全部补0
j|=(k&1); //&按位“与”运算符 ;|“按位或”
k=k>>1; //“>>”代表右移运算符
}
if(j>i)
{
temp=x[i];
x[i]=x[j];
x[j]=temp;
}
}
}
void output() /*输出结果*/
{
int i;
printf("The result are as follows\n");
for(i=0;i=0.0001)
printf("+%.4fj\n",x[i].img);
else if(fabs(x[i].img)<0.0001)
printf("\n");
else
printf("%.4fj\n",x[i].img);
}
}
void add(complex a,complex b,complex *c) //复数相加
{
c->real=a.real+b.real;
c->img=a.img+b.img;
}
void mul(complex a,complex b,complex *c) //复数相乘
{
c->real=a.real*b.real-a.img*b.img;
c->img=a.real*b.img+a.img*b.real;
}
void sub(complex a,complex b,complex *c) //复数相减
{
c->real=a.real-b.real;
c->img=a.img-b.img;
}
void divi(complex a,complex b,complex *c) //复数相除
{
c->real=( a.real*b.real+a.img*b.img)/(b.real*b.real+b.img*b.img);
c->img=( a.img*b.real-a.real*b.img)/(b.real*b.real+b.img*b.img);
}