几种快速傅里叶变换(FFT)的C++实现

        DFT的的正变换和反变换分别为(1)和(2)式。假设有N个数据,则计算一个频率点需要N次复数乘法和N-1次复数加法,整个DFT需要N*N次复数乘法和N(N-1)次复数加法;由于一次的复数乘法需要进行4次的实数乘法和2次的复数加法,一次的复数加法需要两次的实数加法,因此整个DFT需要4*N*N次的实数乘法和2*N(N-1)+2*N*N≈4*N*N次的复数加法。当N比较大时,所需的计算工作量相当大,例如N=1024时大约需要400万次乘法运算,对于实时信号处理来说,将对计算设备提出非常苛刻的要求,于是就提出如何能够减少计算DFT的运算量的问题。
1965年,库力和图基在《计算数学》杂志上发表《机器计算傅立叶级数的一种算法》,此文是最早提出FFT算法的。此后关于DFT的快速算法称为人们研究的热点课题,也正是FFT算法的出现,才使得数字信号处理能够应用于实时场合并进一步推动数字信号处理的发展和应用。

 

大多数的FFT算法都是利用(3)式的周期性、共轭对称性、可压缩和扩展性的特点来压缩计算量。

1)、根据DFT定义进行计算的代码

// Data为输入数据指针,Log2N=log2(length),flag=-1时为正变换,flag=1时为反变换,变换结果同样由指针Data指向的空 间返回
void  dft(complex < double >* Data, int  Log2N, int  flag)
... {
        
int i,j,length;
        complex
<double> wn;
        length
=1<<Log2N;
        complex
<double>*temp=new complex<double>(length);
    
for(i=0;i<length;i++)
   
...{
               temp[i]
=0;
               
for(j=0;j<length;j++)
       
...{
                    wn
=complex<double>(cos(2.0*pi/length*i*j),sin(flag*2.0*pi/length*i*j));
                    temp[i]
+=Data[j]*wn;    
                }
           
       }

       
if(flag==1)
           
for(i=0;i<length;i++)
               Data[i]
=temp[i]/length;
        delete[] temp;
}
  

2)、基2时间抽选FFT

把时域的数字信号序列按照奇偶进行分组计算,可以进行如下的变换,从变换结果可以知道,一个长度为NDFT可以变换成长度为N/2的两个子序列的组合。依次类推,可以直到转为N/22点的傅立叶变化的组合。不过这时的输入应该为以2为基的倒位序。

由于经过多次的奇偶抽选,输入数据要按照基2倒序的原则进行重排,输出数据为正常顺序,倒序算法另外叙述。下面首先用递归的形式进行算法的描述,由于递归方法没有充分利用DIT2算法的优点---原位计算,因此递归形式只是为了描述的清晰。

void  dit2rec(complex < double >* InData,complex < double >* OutData, int  length, int  sign)
... {
   complex
<double>*EvenData=new complex<double>(length/2);
   complex
<double>*OddData  =new complex<double>(length/2);
   complex
<double>*EvenResult=new complex<double>(length/2);
   complex
<double>*OddResult=new complex<double>(length/2);
   
int i,j;
   
if(length==1)
   
...{
      OutData[
0]=InData[0]/N;
      
return;
   }

  
for(i=0;i<length/2;i++)
  
...{
    EvenData[i]
=InData[2*i];
    OddData[i]
=InData[2*i+1];
  }

  dit2rec(EvenData,EvenResult,length
/2,sign);
  dit2rec(OddData,EvenResult,length
/2,sign);
  
for(i=0;i<length/2;i++)
  
...{
    OutData[i]
=EvenData+OddData*complex<double>(cos(2*pi*i/length),sin(sign*2*pi*i/length));
    OutData[i
+length/2]=EvenData- OddData*complex<double>(cos(2*pi*i/length),sin(sign*2*pi*i/length));
  }

  delete[] EvenData,OddData,EvenResult,OddResult;
  
return;
}

非递归实现如下(现不考虑输入的倒序数问题):
void  dit2(complex < double >* Data, int  Log2N, int  sign)
... {
   
int i,j,k,step,length;
   complex
<double> wn,temp,deltawn;
   length
=1<<Log2N;
   
for(i=1;i<=Log2N;i++)
   
...{
      wn
=1;step=1<<i;deltawn=complex<double>(cos(2*pi/step),sin(sign*2.0*pi/step));
      
for(j=0;j<step/2;j++)
      
...{        
        
for(i=0;i<length/step;i++)
        
...{
           temp
=Data[i*step+step/2+j]*wn;
           Data[i
*step+step/2+j]=Data[i*step+j]-temp;
           Data[i
*step+j]=Data[i*step+j]+temp;
         }

        wn
=wn*deltawn;
      }

    }

    
if(sign==-1)
       
for(i=0;i<length;i++)
            Data[i]
/=length;
 }

 
i=1 时,也就是第一次循环并没有必要进行复数运算,因为 j 只能取 1 wn 为实数,这个时间可以节省。因此可以改进为 :
void  dit2(complex < double >* Data, int  Log2N, int  sign)
... {
   
int i,j,k,step,length;
   complex
<double> wn,temp,deltawn;
  length
=1<<Log2N;
   
for(i=0;i<length;i+=2)
   
...{
      temp
=Data[i];
      Data[i]
=Data[i]+Data[i+1];
      Data[i
+1]=temp-Data[i+1];
   }

   
for(i=2;i<=Log2N;i++)
   
...{
      wn
=1;step=1<<i;deltawn=complex<double>(cos(2.0*pi/step),sin(sign*2.0*pi/step));;
      
for(j=0;j<step/2;j++)
      
...{        
        
for(i=0;i<length/step;i++)
        
...{
           temp
=Data[i*step+step/2+j]*wn;
           Data[i
*step+step/2+j]=Data[i*step+j]-temp;
           Data[i
*step+j]=Data[i*step+j]+temp;
         }

         wn
=wn*deltawn;
      }

   }

   
if(sign==1)
   
for(i=0;i<length;i++)
     Data[i]
/=length;
}


3)、基2频率抽选FFT
// DIF2的递归版本实现:
void  dif2rec(complex < double >* InData,complex < double >* OutData, int  length, int  sign)
... {
   complex
<double>* LData=new complex<double>(length/2);
   complex
<double>* LResult=new complex<double>(length/2);
   complex
<double>* RData=new complex<double>(length/2);
   complex
<double>* RResult=new complex<double>(length/2);
   complex
<double> temp;
   
int i;
if(length==1)
   
...{
       OutData[
0]=InData[0];
       
return;
}

for(i=0;i<length/2;i++)
...{
   LData[i]
=InData[i];
   RData[i]
=InData[i+length/2];
}

for(i=0;i<length/2;i++)
...{
   temp
=LData[i];
   LData[i]
=LData[i]+RData[i];
   RData[i]
=(temp-RData[i])* complex<double>(cos(2*pi*i/length),sin(sign*2*pi*i/length))
}

dit2rec(LData,LResult,length
/2,sign);
dit2rec(RData,RResult,length
/2,sign);
   
for(i=0;i<length/2;i++)
   
...{
      OutData[
2*i]=LResult[i];
      OutData[
2*i+1]=RResult[i];
}

return;
}

// 非递归实现如下(现不考虑输入的倒序数问题):
void  dif2(complex < double >* InData, int  r, int  sign)
... {
int length=1<<r;
int i,j,k,step;
complex
<double> temp,wn;
for(i=1;i<=r;i++)
...{
   step
=1<<(r-i+1);
   wn
=1;
   
for(j=0;j<step/2;j++)
   
...{
      
for(k=0;k<step/2;k++)
      
...{
         temp
=InData[k*step+j];
         InData[k
*step+j]=InData[k*step+j]-InData[k*step+step/2+j];
         InData[k
*step+step/2+j]=(temp-InData[k*step+step/2+j])*wn;
}

wn
=wn*complex<double>(cos(2*pi/step*j),sin(sign*2*pi/step*j));
}

}

}

DIT一样,最外层的最后一个循环可以另外独立出来,因为最后一个循环没有必要进行复数运算,这样可以减少复数运算的次数。

基四时间抽选快速傅立叶算法

  • 6
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 傅里叶变换是常用的信号处理手段,可以将时域信号转换为频域信号进行分析。离散傅里叶变换是一种将离散序列的傅里叶变换的过程,而快速傅里叶变换是对离散傅里叶变换的一种优化方法,可以更快速地计算出结果。它们之间具有密切联系。 使用快速傅里叶变换可以更高效地计算傅里叶变换,它的时间复杂度为$O(n\log n)$(n为数据长度),而离散傅里叶变换的时间复杂度为$O(n^2)$。因此,在信号处理中,使用fft可以大幅度缩短计算时间,并且由于它的算法简洁明了,也便于程序实现快速傅里叶变换常被应用于很多领域,例如分析音频、图像、生物、金融等数据。同时,由于其高效性及广泛应用,很多编程语言如Python、Java和C++等都提供了内置的fft函数,方便程序员实现傅里叶变换。在使用fft时,需要注意输入的序列数量应为2的幂次方,这样可以更高效地运行算法,得到精确的傅里叶变换结果。 ### 回答2: 快速傅里叶变换(FFT)和离散傅里叶变换(DFT)都是将信号从时域转换到频域的数学工具,它们的联系在于FFT是DFT的一种更快捷的算法实现方式。具体来讲,FFT使用了分治策略,通过对输入的信号进行递归分解,将原本的N个点的DFT问题分解为多个$log_2(N)$个点的DFT问题,从而减小了计算量和时间复杂度。因此,FFT可以在计算速度上实现了数量级的提升。 使用FFT主要包括以下几步: 1. 将需要进行FFT变换的信号补零至2的幂次方,并将其分成奇偶序列; 2. 分别进行奇偶序列的FFT变换; 3. 利用蝴蝶运算将子问题的解合并得到整个信号的FFT变换结果。 4. 对得到的频域信号进行幅度谱或相位谱的计算和分析。 使用FFT可以有效地减少计算复杂度,并且在信号压缩、图像处理、音频处理、雷达信号处理、数据压缩等领域均得到了广泛的应用。 ### 回答3: 快速傅里叶变换(FFT)是离散傅里叶变换(DFT)的一种高效实现方式。FFT是一种变换算法,在O(n log n)的时间内计算出长度为n的离散傅里叶变换结果,而传统的DFT算法时间复杂度为O(n^2)。因此,FFT成为了数字信号处理领域中计算傅里叶变换最常用的算法之一。 使用FFT,需要注意以下几点: 1. FFT只能用于长度为2的整数幂的输入信号,如果输入信号长度不是这样,需要进行零填充或者剪裁操作。 2. 输入信号应为实数信号,如果是复数信号,则需要将实部和虚部分别传入FFT算法,同时在输出结果中也会分别得到实部和虚部的结果。 3. 对于时间序列,FFT可以用于计算频率域信息,例如,可以在频域中滤波、拆分信号等。 实际应用中,使用FFT可以在很多领域获得良好的效果,例如音乐信号处理、图像处理、自然语言处理等。不过,在使用FFT时需要注意选择使用的实现算法和相关配置,以确保获得正确的结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值