繁体   English   中英

FFT和FFT的逆

[英]FFT and inverse of FFT

我是一名从事电信项目的计算机程序员。
在我们的项目中,我必须将一系列复数转换为傅里叶变换。因此我需要一个有效的C89标准FFT代码。
我使用以下代码,它运作良好:

    short FFT(short int dir,long m,double *x,double *y)
{
   long n,i,i1,j,k,i2,l,l1,l2;
   double c1,c2,tx,ty,t1,t2,u1,u2,z;

   /* Calculate the number of points */
   n = 1;
   for (i=0;i<m;i++) 
      n *= 2;

   /* Do the bit reversal */
   i2 = n >> 1;
   j = 0;
   for (i=0;i<n-1;i++) {
      if (i < j) {
         tx = x[i];
         ty = y[i];
         x[i] = x[j];
         y[i] = y[j];
         x[j] = tx;
         y[j] = ty;
      }
      k = i2;
      while (k <= j) {
         j -= k;
         k >>= 1;
      }
      j += k;
   }

   /* Compute the FFT */
   c1 = -1.0; 
   c2 = 0.0;
   l2 = 1;
   for (l=0;l<m;l++) {
      l1 = l2;
      l2 <<= 1;
      u1 = 1.0; 
      u2 = 0.0;
      for (j=0;j<l1;j++) {
         for (i=j;i<n;i+=l2) {
            i1 = i + l1;
            t1 = u1 * x[i1] - u2 * y[i1];
            t2 = u1 * y[i1] + u2 * x[i1];
            x[i1] = x[i] - t1; 
            y[i1] = y[i] - t2;
            x[i] += t1;
            y[i] += t2;
         }
         z =  u1 * c1 - u2 * c2;
         u2 = u1 * c2 + u2 * c1;
         u1 = z;
      }
      c2 = sqrt((1.0 - c1) / 2.0);
      if (dir == 1) 
         c2 = -c2;
      c1 = sqrt((1.0 + c1) / 2.0);
   }

   /* Scaling for forward transform */
   if (dir == 1) {
      for (i=0;i<n;i++) {
         x[i] /= n;
         y[i] /= n;
      }
   }

   return(true);
}

但是这段代码只支持大小为2^m数组。就像CLRS书籍代码一样。
我们应该转换的数组不在这个范围内,加零将是昂贵的,所以我正在寻找另一种解决方案,帮助我输入任何大小。
IT++matlab那样的东西。 但是正如我们希望它在纯C中使用它们是不可能的。而且, IT++代码在我检查时被阻止了

如果您正在开发任何大众市场计算平台(带有Windows或OS X,iOS等的英特尔),那么供应商或制造商就会提供高性能的FFT实现。

否则,您应该评估FFTW

为2的幂以外的大小编写高性能FFT是一项复杂的任务。

如果您打算使用自己的实现,那么,仅关于两种功能:

您显示的实现在FFT期间计算sqrt 大多数高性能FFT实现会提前计算常量并将它们存储在表中。

缩放包含除法运算,在x[i] /= ny[i] /= n 编译器可能将这些实现为除法指令。 分区通常是对通用处理器的慢速指令。 计算scale = 1. / n一次并乘以scale而不是除以n会更好。

更好的是完全省略比例。 变换通常在没有比例的情况下有用,或者可以从单个变换中省略比例,并且仅作为聚合比例应用一次。 (例如,不是进行两次缩放操作,一次在正向FFT中,一次在逆FFT中,而是将缩放操作保留在FFT例程之外,并且在正向FFT和反向FFT之后只执行一次。)

如果可以接受以频率反转顺序排列频域数据,则可以省略位反转置换。

如果保持位反转置换,则可以对其进行优化。 执行此操作的技术取决于平台。 一些平台具有反转整数位的指令(例如,ARM具有rbit )。 如果您的平台没有,您可能希望将位反转索引保留在表中,或者比当前代码更快地研究计算它们的方法。

如果同时保持位反转置换和缩放,则应考虑同时执行它们。 置换使用大量内存运动,缩放使用处理器的算术单元。 大多数现代处理器可以同时执行这两种处理,因此您可以从重叠操作中获得一些好处。

您当前的代码使用基数为2的蝴蝶。 Radix-4通常更好,因为它可以通过改变i来实现,只需改变使用哪个数据并将一些加法改为减法,反之亦然。

如果您的阵列长度接近处理器上的第一级内存缓存的大小,则部分FFT实现将破坏缓存并显着减慢速度,除非您设计适当的代码来处理此问题(通常通过将数组的部分临时复制到缓冲区中) )。

如果您的目标处理器具有SIMD功能,您绝对应该使用FFT中的功能; 它们极大地加速了FFT性能。

以上内容应该告诉您,编写高效的FFT是一项复杂的任务。 除非您想花费大量精力,否则最好使用FFTW或其他现有实现。

在您的实现中,我担心这段代码:

     z =  u1 * c1 - u2 * c2;
     u2 = u1 * c2 + u2 * c1;
     u1 = z;

l1很大时,(u1,u2)会受到许多累积的舍入误差的影响。 您可能会得到一个不准确的转换。

我赞同FFTW的建议,但我相信它是高度特定于平台的。 (大多数FFT库都是。)[ 编辑 :不,它实际上是直接的C89。 这正是你所说的你想要的。]

Wikipedia FFT页面列出了一系列适用于奇怪大小的输入数组的算法。 我不知道它们是如何工作的,但我相信一般的想法是你使用Rader的算法Bluestein算法进行素数大小的输入,而Cooley-Tukey则将复合大小的变换用于一堆大小的变换。

对于FFTW的替代方案,请检查我的mix-radix FFT,它也处理非2 ^ N FFT长度。 C源可从http://www.corix.dk/Mix-FFT/mix-fft.html获得

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM