快速傅里叶变换的相关定义、原理及其递归算法

    快速傅里叶变换FFT是离散傅里叶变换DFT的一种快速算法,实际上诸如Matlab等科学计算软件都已经实现了FFT,只需调用相应的接口即可。在ACM里,FFT的典型应用就是大数的乘法或者多项式的乘法。顺便,如果题目规模不是很大,有关大数的运算推荐使用Java语言,使用java.math.BigInteger包完成;包括高精度运算,可以使用BigDecimal包完成。任何情况下,会一门外语总是很重要的!

    傅里叶变换一般指的是连续傅里叶变换,其定义如下:


    傅里叶逆变换的定义如下:


    离散傅里叶变换与连续傅里叶变换“类似”,只不过把积分换成级数。DFT以及IDFT的定义式如下:






    令源序列x={0, 1, 3},则DFT的结果是X={4, exp(-i*2PI/3)+3exp(-i*4PI/4), exp(0i*4PI/3)+3exp(-i*8PI/3)}  ;

另外一个例子, 若令源序列为x={2, 4},则DFT的结果是X={6, -2}。

  

    FFT和卷积关系密切,连续卷积和离散卷积的定义分别如下:



    下面举一个简单的例子显示离散卷积的运算,令序列f={0,1,3},序列g={2,4},则其卷积的计算式如下:


所以,卷积的结果序列y={0, 2, 10, 12}


    卷积可以使用DFT完成,本质上这也是大数乘法、多项式乘法可以使用DFT完成的原理。当然在完成时,首先要将源序列的长度补齐到合适的数值。使用DFT完成卷积或者多项式乘法时,序列长度应该是2的幂,显然又不能小于最终结果的长度。上例中结果长度为4,恰好是2的幂,所以源序列补到4长度即可。于是 f = {0, 1, 3, 0},g = {2, 4, 0, 0}。NOTE:对大数乘法一定要注意结果长度的估算,本例如果表示大数乘法,则是一个3位数乘2位数,其结果最多是一个5位数,所以长度应补到8为佳。

    f和g的DFT分别是


     将其对应项相乘,得到


    再对该序列做一个离散傅里叶逆变换,可以得到{0, 2, 10, 12},与原始卷积的结果相同,在本质上这也是两个多项式乘法的结果。另一方面,如果考虑三位数310和两位数42的乘法,其结果是13020,与卷积的结果同样是吻合的。注意乘法运算需要考虑进位!

    所以,给定多项式a、b,求其乘积c,可以这样实现:

  1. 将a、b高位补零到合适长度
  2. 对a、b分别作DFT得到Fa和Fb
  3. Fa、Fb对应项相乘得到Fc
  4. 对Fc做离散傅里叶逆变换IDFT即为结果c

   很显然第3步为O(N),所以关键在第2、4步。而DFT和IDFT按照定义做,同样为O(N*N),没有任何改进。这个步骤之所以可行,就是因为存在FFT算法,可以达到O(NlogN)。

    为了方便书写,令WN=exp(-i*2PI/N),其中N是下标,而WN称之为单位复根。数学习惯上应该用希腊字母Ω的小写来表示单位复根。 单位复根有以下性质:

  1. WN^k = WN^(k+N)。很显然,因为WN的N次方就是1,这个其实是周期性
  2. WN^k = - WN^(k+N/2), 当N为偶数时。这也很显然,因为WN的N/2次方就是-1,这是对称性。
  3. WN^k = W(N/2)^(k/2),当N、k均为偶数。这个根据定义很容易推算,右边实际就是exp(-i*2PI/(N/2))^(k/2),显然等于左边
    此处仅以一个8点DFT来描述FFT的原理,如果想看到更一般性、更本质的描述及证明,可以去阅读《Introduction to Algorithms》。

    考虑一个8点DFT,其原始公式是:

将其奇偶项分开书写,可写成:

将奇数项提取出一个公因子,可写成(注意下标n的变化):



注意到单位复根的脚标是8,为偶数,根据单位复根的性质3,可以做一个比例变换,得到:




于是再对求和符号的下标n做一个变换,得到:



接下来,按照k值,将上式写成2个部分:


    注意上式中的第1个式子,该式的前后两个部分都是一个4点序列的DFT,所以该式实际上就是2个4点DFT之和。至于第2个式子,首先变化k将其改写为:


然后考虑到单位复根的性质1和性质2,上式可以改写为:


    所以最终一个8点DFT变为了:


 

    该式表明一个8点DFT转为了2个4点DFT,8点DFT的结果中的前4项是这两个4点DFT之和(带一个单位复根的因子),而后4项是其差。递归下去,4点DFT可以变为2个2点DFT,而2点DFT变为2个1点DFT,最后1点DFT就是其本身。

    所以,基于此原理,FFT的递归算法非常容易实现:



    当然这个递归算法完全是纯按原理实现的,考虑到程序实现,有好几个可以优化的地方。例如红色部分,完全可以进行优化,提高代码执行效率。


  • 9
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 快速傅里叶变换FFT)是一种高效的计算时间域信号的频域表示的算法。下面是基于C语言的快速傅里叶变换FFT)算法,注释说明了每个步骤的作用。 ```c #include <stdio.h> #include <complex.h> #include <math.h> // 前置声明函数 void fft(complex double x[], int N); void bit_reverse(complex double x[], int N); int main() { // 定义输入信号和长度 complex double x[] = {1, 1, 1, 1, 0, 0, 0, 0}; int N = sizeof(x) / sizeof(x[0]); // 执行FFT变换 fft(x, N); // 输出结果 for (int i = 0; i < N; i++) { printf("%f + %f i\n", creal(x[i]), cimag(x[i])); } return 0; } // 快速傅里叶变换算法 void fft(complex double x[], int N) { // 将输入信号按位反转 bit_reverse(x, N); for (int N_s = 2; N_s <= N; N_s *= 2) { // 计算旋转因子 complex double Wn = cexp(-2 * M_PI * I / N_s); // 迭代计算每个级别的蝶形运算 for (int k = 0; k < N; k += N_s) { complex double w = 1; for (int j = 0; j < N_s / 2; j++) { complex double t = w * x[k + j + N_s / 2]; complex double u = x[k + j]; // 蝶形运算 x[k + j] = u + t; x[k + j + N_s / 2] = u - t; w *= Wn; } } } } // 按位反转函数 void bit_reverse(complex double x[], int N) { int i, j, k; for (i = 1, j = N / 2; i < N - 1; i++) { if (i < j) { complex double temp = x[j]; x[j] = x[i]; x[i] = temp; } k = N / 2; while (j >= k) { j -= k; k /= 2; } j += k; } } ``` 该代码实现了一个简单的8点FFT算法。首先,需要定义一个复数数组`x[]`来存储输入信号,并指定输入信号的长度`N`。然后,通过调用`fft()`函数来执行FFT变换,并将结果存储在输入信号数组中。最后,使用循环输出变换后的信号结果。 在`fft()`函数中,首先调用`bit_reverse()`函数按位反转输入信号数组。然后,通过循环进行迭代计算,每次迭代都完成当前级别的蝶形运算,直到完成全部级别的计算。在蝶形运算过程中,使用旋转因子`Wn`来乘以输入信号数组的一部分,并进行加法和减法运算,得到新的输出结果。 `bit_reverse()`函数用于按位反转输入信号数组。通过循环将输入信号的位进行反转以实现这一目标。 请注意,这只是一个简单的示例代码,用于说明FFT算法的基本原理。在实际应用中,可能需要优化计算过程以提高性能,并处理更大的输入信号。 ### 回答2: 快速傅里叶变换FFT)是一种高效计算离散傅里叶变换(DFT)的算法,它能够将一个长度为N的序列转换为频域上的N个频率成分。下面是一个基于C语言的FFT算法的示例: ```c #include <stdio.h> #include <math.h> #define PI 3.14159265359 // 定义复数结构体 typedef struct { double real; // 实部 double imag; // 虚部 } Complex; // 计算FFT void fft(Complex* x, int N) { if (N <= 1) { return; } // 将输入序列拆分成奇偶部分 Complex* even = (Complex*) malloc(N/2 * sizeof(Complex)); Complex* odd = (Complex*) malloc(N/2 * sizeof(Complex)); for (int i = 0; i < N/2; i++) { even[i] = x[2 * i]; odd[i] = x[2 * i + 1]; } // 递归计算奇偶部分的FFT fft(even, N/2); fft(odd, N/2); // 合并奇偶部分的结果 for (int k = 0; k < N/2; k++) { double angle = -2*PI*k/N; Complex w = {cos(angle), sin(angle)}; Complex t = {w.real * odd[k].real - w.imag * odd[k].imag, w.real * odd[k].imag + w.imag * odd[k].real}; x[k] = {even[k].real + t.real, even[k].imag + t.imag}; x[k + N/2] = {even[k].real - t.real, even[k].imag - t.imag}; } free(even); free(odd); } int main() { // 输入序列与长度 Complex x[] = {{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0}}; int N = sizeof(x) / sizeof(x[0]); // 计算FFT fft(x, N); // 输出结果 for (int i = 0; i < N; i++) { printf("X[%d] = %f + %fi\n", i, x[i].real, x[i].imag); } return 0; } ``` 上述代码实现了一个FFT算法,并打印出计算结果。首先定义了一个复数结构体,然后使用递归方式计算FFT。在计算过程中,将输入序列拆分成奇偶部分,然后递归计算奇偶部分的FFT,最后合并奇偶部分的结果。 在主函数中,定义了一个输入序列x,并调用fft函数计算FFT。最后输出计算结果。 这段代码可以通过将输入序列修改为需要计算FFT的序列,然后运行程序来获得FFT的结果。 ### 回答3: 快速傅里叶变换FFT)算法是一种高效的计算离散傅里叶变换(DFT)的方法,广泛应用于信号处理、图像处理和音频处理等领域。下面是基于C语言实现的FFT算法,包含详细的注释: ```c #include <stdio.h> #include <math.h> // 计算FFT void fft(double real[], double imag[], int N) { int i, j, k; int m, n, L, Lk; double theta, wpr, wpi, wr, wi, tmpReal, tmpImag; // 确定计算层数L L = log2(N); // 通过蝶形运算计算FFT for (m = 1; m <= L; ++m) { // 计算蝶形运算的间隔 n = pow(2, m); Lk = N / n; // 计算旋转因子e^(-2*pi*i/N) theta = -2 * M_PI / n; wpr = cos(theta); wpi = sin(theta); // 循环遍历每个蝶形运算 for (k = 0; k < N; k += n) { wr = 1; wi = 0; // 进行蝶形运算 for (j = 0; j < Lk; ++j) { // 计算下标 i = k + j; // 计算蝶形运算 tmpReal = real[i + Lk] * wr - imag[i + Lk] * wi; tmpImag = imag[i + Lk] * wr + real[i + Lk] * wi; // 更新结果 real[i + Lk] = real[i] - tmpReal; imag[i + Lk] = imag[i] - tmpImag; real[i] += tmpReal; imag[i] += tmpImag; // 更新旋转因子 tmpReal = wr; wr = wr * wpr - wi * wpi; wi = wi * wpr + tmpReal * wpi; } } } } int main() { int N = 8; // 输入序列长度 double real[] = {1, 2, 3, 4, 0, 0, 0, 0}; // 实部 double imag[] = {0, 0, 0, 0, 0, 0, 0, 0}; // 虚部 int i; // 输出原始数据 printf("原始数据:\n"); for (i = 0; i < N; ++i) { printf("%.2f + %.2fi\n", real[i], imag[i]); } // 计算FFT fft(real, imag, N); // 输出FFT结果 printf("FFT结果:\n"); for (i = 0; i < N; ++i) { printf("%.2f + %.2fi\n", real[i], imag[i]); } return 0; } ``` 以上代码实现了一个简单的FFT算法示例,

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值