You are on page 1of 18

快速傅里叶变换原理及其实现

李维杰 06S051092

摘 要:傅里叶变换是信号处理中一项重要的数学工具。但是傅里叶变换大量的计算又阻碍
了这一工具的使用。因此,人们提出了很多方法修改傅里叶变换算法,以降低傅里叶变换的
时间复杂度。其中基 2 快速傅里叶变换是其中佼佼者。本文详细给出了基 2 快速傅里叶变换
对的原理和实现,并分别在一维和二维的情况下对快速傅里叶变换对作了对比研究。

关键词:信号处理;傅里叶变换;快速傅里叶变换

1. 引言

1822 年法国数学家和物理学家傅里叶发表了题为《热的解析理论》的论文,在这篇文章
中,傅里叶首先提出:一个函数可以分解为多个周期函数的和。若干年后,该文被翻译为英
文发表,在科学界和工程界引起了巨大的反响,也成就了傅里叶在历史上的地位。傅里叶出
生贫寒,经过自身的努力一步一步走向成功。早先,他那篇著名的文章交由时任巴黎科学院
院长的泊松审阅,泊松在苦心研究数月后,还是看不懂该文并将文章“枪毙”。后来,傅里
叶投靠拿破仑担任巴黎科学院院长后,通过某些方式将文章发表。但该文真正引起广泛注意
是在去世以后的事情。傅里叶最后做到地方大吏,在地方上一些事情上也是颇有建树。拿破
仑复辟的时候,想借助道于傅里叶管辖的伊泽尔省。傅里叶权衡利弊后,对拿破仑做了一次
“变换”,让拿破仑打消了借道的计划。由此可见,这位先贤是深谙“变换”之道。
从现代数学的眼光来看,傅里叶变换是一种特殊的积分变换。它能将满足一定条件的某
个函数表示成正弦基函数的线性组合或者积分。在不同的研究领域,傅里叶变换具有多种不
同的变体形式,如连续傅里叶变换和离散傅里叶变换。傅里叶变换属于调和分析的内容。“
任意”的函数通过一定的分解,都能够表示为正弦函数的线性组合的形式,而正弦函数在
物理上是被充分研究而相对简单的函数类。现代数学发现傅里叶变换具有非常好的性质:
性质 1. 傅里叶变换是线性算子,若赋予适当的范数,它还是酉算子;
性质 2. 傅里叶变换的逆变换容易求出,而且形式与正变换非常类似;
性质 3. 正弦基函数是微分运算的本征函数,从而使得线性微分方程的求解可以转化为
常系数的代数方程的求解。在线性时不变的物理系统内,频率是个不变的性质,从而系统对
于复杂激励的响应可以通过组合其对不同频率正弦信号的响应来获取;
性质 4. 著名的卷积定理指出:傅里叶变换可以化复杂的卷积运算为简单的乘积运算,
从而提供了计算卷积的一种简单手段。
正是由于上述的良好性质,傅里叶变换在物理学、数论、组合数学、信号处理、概率、统计、
密码学、声学、光学等领域都有着广泛的应用。
虽然傅里叶变换有巨大吸引力,但是傅里叶变换巨大的计算量同时也让人们望而止步。
纵是在电子计算机时代, 20 世纪 70 年代以前,巨大的计算量同样阻扰了傅里叶变换在科
2
学研究和工程技术上的应用。以一维傅里叶变换为例,完成一次傅里叶变化需要做 N 次乘
法和 N ( N -1) 次复数加法,时间复杂度是 O (n ) 。 1965 年 J. W. Cooley 和 J. W. Tukey 在
2

《Math Computer》发表文章《An algorithm for the machine calculation of complex Fourier series》

一文,提出快速傅里叶变换,将时间复杂降低到了 O (n log n) 。从此,制约傅里叶变换广泛

使用的瓶颈被冲破,让傅里叶变换的应用更加广泛和普及。
本文的目的就是详细的介绍快速傅里叶变换的原理以及快速傅里叶变换的 C 语言实现。
本文的结构如下:首先介绍傅里叶变换的基本知识。然后在文章的第 2 部分介绍快速傅里叶
变换的原理。在文章的第 3 部分,主要介绍快速傅里叶变换的 C 语言实现。最后,通过与傅
里叶变换的对比研究,验证实现的程序的正确性和有效性。

2. 快速傅里叶变换

定义 1:一维连续函数 f ( x) 的傅里叶变换 F (u ) 定义为:



F (u )   f ( x )e  j 2 ux dx (1)


其中, j  1 。
2

定义 2:给定 F (u ) ,可以通过傅里叶反变换得到函数 f ( x) 。傅里叶反变换定义为:



f ( x)   F (u )e j 2 ux du (2)


公式(1)和(2)就是傅里叶变换对。他们可以直接扩张到二维空间中。二维的傅里叶
变换对分别是:
 
F (u , v )    f ( x, y )e  j 2 (ux  vy ) dxdy (3)
 

 
f ( x, y )    F (u , v)e j 2 (ux  vy ) dudv (4)
 

本文只讨论离散函数,所以我们就不必讨论公式(1)、(2)、(3)和(4)。可知,
连续函数的积分对应离散函数的求和。由此我们可以得到离散傅里叶变换对的定义。

定义 3:离散函数 f ( x), x  0,1,..., N  1 ,其离散傅里叶变换定义为

N 1
1
F (u ) 
N
 f ( x )e
x 0
 j 2 ux / N
, u  0,1,..., N  1 (5)

定义 4:给定离散傅里叶变换 F (u ), u  0,1,..., N  1 ,其离散傅里叶反变换定义为:

N 1
f ( x)   F (u )e j 2 ux / N , x  0,1,..., N  1 (6)
u 0

同样,我们可以将一维的离散傅里叶变换扩展到二维空间中。二维离散傅里叶变换对
为:
N 1 M 1
1
F (u , v ) 
NM
  f ( x, y )e
x 0 y 0
 j 2 ( ux / N  vy / M )
, u  0,1,..., N  1, v  0,1,..., M  1

(7)
N 1 M 1
f ( x, y )    F (u, v)e j 2 ( ux / N  vy / M ) , x  0,1,..., N  1, y  0,1,..., M  1
u 0 v 0

(8)
为了表述上的简便,下文中用 DFT 记作是离散傅里叶变换, iDFT 记作是离散傅里叶

反 变 换 , 即 是 : F (u )  DFT[ f ( x)] 和 f ( x)  iDFT[F (u )] , F (u , v )  DFT[f ( x, y )] 和

f ( x, y )  iDFT[F (u , v)] 。由性质 1 可知,通过适当的放缩,可以将(5)和(7)右边的系

数消除。在下文中,我们假设在变换之前适当的放缩操作已经完成。

2.1 一维快速傅里叶变换及其反变换

定理 1:设 F (u )  DFT[ f ( x)] ,其中 0  x, u  N  1 ,


x, u 都是整数, N 是偶数。再

设 f 0 ( x)  f (2 x) , f1 ( x)  f (2 x  1) , 其 中 0  x  N / 2  1 , x 是 整 数 。 若

F0 (u )  DFT[ f 0 ( x)] , F1 (u )  DFT[ f1 ( x)] ,其中 0  x, u  N / 2  1 , x, u 都是整数,则

F (u )  F0 (u )  F1 (u )WNu (9)
N
F (u  )  F0 (u )  F1 (u )WNu (10)
2
2
j
其中 0  u  N / 2  1 , WNu  e
u
N 。

定理 1 的证明如下:
当 0  u  N / 2  1 时,
N N
1 1
N 1 2 2
F (u )   f ( x)WNxu   f (2 x)WN2 xu   f (2 x  1)WN(2 x 1)u
x 0 x0 x 0
N N
1 1
2 2
  f (2 x)WN2 xu   f (2 x  1)WN(2 x 1)u
x0 x 0
N N (11)
1 1
2 2
  f (2 x)WN2 xu  WNu  f (2 x  1)WN2 xu
x0 x 0
N N
1 1
2 2
  f 0 ( x)WN2 xu  WNu  f1 ( x )WN2 xu
x0 x 0
2
j u
2 N
j 2u
又知, WN2u  e N
e 2
 W Nu ,则由式(11)可得,
2
N N
1 1
2 2
F (u )   f 0 ( x)WN2 xu  WNu  f1 ( x)WN2 xu
x 0 x 0
N N
1 1
2 2
  f 0 ( x)W Nxu  WNu  f1 ( x)W Nxu (12)
x 0 2 x 0 2

 F0 (u )  W F (u ) u
N 1

由于
N 1 N N 1 N
N x (u  )
)   f ( x)WN 2   f ( x)WN 2 WNxu
x
F (u  (13)
2 x 0 x 0


N 2 N
x j x
WN 2  e N 2
 e  j x  (1) x

N 1
N
F (u  )   (1) x f ( x)WNxu
2 x 0
N N
1 1
2 2
=  ( 1) 2 x f (2 x)WN2 xu   (1) 2 x 1 f (2 x  1)WN(2 x 1) u
x 0 x 0
N
1
N
1 (14)
2 2
=  f (2 x)W 2 xu
N   f (2 x  1)W (2 x 1) u
N
x 0 x0
N N
1 1
2 2
=  f 0 ( x)W Nxu  WNu  f1 ( x)W Nxu
x 0 2 x 0 2


1
为了清楚地表述定理 所给出的由各子序列 DFT 计算原 DFT 序列的运算结构,按式
(9 )和( 10 )做一运算流程图,如图 1 所示。图 1 的意义如下:左边输入端点是子序列
DFT,右边输出端点是原序列的 DFT;各线段均有权值,权值默认是 1;除输入和输出端
点外,中间端点表示该端点上级端点的值乘以端点之间的权重后的累加和。

图 1 定理 1 运算流图
为了表示上的方便,可以对图 作一些简化,如图 2 所示。运算蝶中的圆圈称为蝶形结。
1
每个运算蝶的四个支路称为蝶翅,按所在位置称之为左前翅、左后翅、右前翅和右后翅。翅上
的标记称为翅权,其中右前翅等于左翅与其翅权之积的和,右后翅等于左翅与其翅权之积
的差。图 2 中的黑点表示运算蝶的翅尖,每格运算蝶有 4 的翅尖。右前翅与右后翅下标之差
N

2 ,这个量称为翅尖距。

图 2 定理 1 运算简化流图
图 2 明显比图 1 简洁得多,人们形象地把它想象为一只张开翅膀的蝴蝶(我一个很郁
闷,为什么不把它想象为蜻蜓呢?第一个做出想象的人影响后来人,也制约了后来人),
因而相应的把它描述的运算称为蝶式运算,把定理 1 称为蝶式运算定理,把简化后的运算
流程称为运算蝶。
根据定理 1,我们知道一次运算蝶中需要 1 次复数乘法,2 次复数加法(减法直接化为
N
加法)。为了计算偶数 N 点的 DFT,可以先计算两个 DFT,然后根据运算
2 点的子序列的
N
蝶得到最后的结果。同时我们知道,
2 个运算蝶计算完成后,才可以的到最后的全部结果。
N N N
如果字序列的长度 仍然是偶数,则可以将其划分为两个 的子序列。当
2 4 4 仍为偶

数的时候,可以继续分解下去。对于 N  2 ,其中 l 为整数,可以一直分解下去,直到子序


l

列的长度为 1 为止。对与长度为 1 的序列,其 DFT 就是自己本身,计算量为 0。

对于 N  2 的序列,第 i 次分解时,其中 1  i  l ,得到 2 个长度为 2 的子序列;为


l i l i

l 1
了完成运算,每次分解需 2 个运算蝶。每个运算蝶中需要 1 次复数乘法和 2 次复数加法,
l 1 N
则总的计算量为 l 2  log N 次复数乘法和 22l 1 l  N log N 次复数加法。乘法运算的复
2
N
杂度是加法的若干倍。因此,总时间复杂度是 O (c log N  N log N )  O ( N log N ) 。
2

定理 2:设 F (u )  DFT[ f ( x)] ,其中 0  x, u  N  1 ,


x, u 都是整数, N 是偶数。再

设 f 0 ( x)  f (2 x) , f1 ( x)  f (2 x  1) , 其 中 0  x  N / 2  1 , x 是 整 数 。 若

F0 (u )  DFT[ f 0 ( x)] , F1 (u )  DFT[ f1 ( x)] ,其中 0  x, u  N / 2  1 , x, u 都是整数,则

1 N
F0 (u )  ( F (u )  F (u  )) (15)
2 2
1 u N
F1 (u )  WN ( F (u )  F (u  )) (16)
2 2
2
j u
其中 0  u  N / 2  1 , WNu  e N 。

证明:
由式定理 1 可得,
1 N
F0 (u )  ( F (u )  F (u  )) (17)
2 2
1 1 N
F1 (u )  u
( F (u )  F (u  )) (18)又
2 WN 2
2
1 1 j u
 e N
 WN u
WNu j
2
u (19)
e N

则 式 ( 19 ) 代 入 式 ( 18 ) , 即 可 得 式 ( 16 ) 。

在傅里叶变换中,对于 N  2 的序列,在第 l 次分解后,可以得到 DFT 。假设已知


l

DFT,根据式(15)和(16),就可以得到第 l  1 次的子序列。如此,在 DFT 的第 1 次分


解,反向求解就可以得到原始序列。
同样,我们可以用运算蝶表示傅立叶反变换 iDFT 运算流程。iDFT 运算流程的方向刚好
与 DFT 运算流程方向相反。图 3 就是傅立叶反变换 iDFT 运算流程。图 3 与图 2 的区别就是:
左前翅等于右翅与其翅权之积的和乘以左前翅的翅权,左后翅等于右翅与其翅权之积的差
乘以左后翅的翅权。

图 3 iDFT 运算简化流图
由式(17)和(18)中,每个 iDFT 运算蝶中,需要 3 次复数乘法,2 次复数加法。则
N
总时间复杂度是 O (c3 log N  N log N )  O( N log N ) 。
2

2.2 二维快速傅里叶变换及其反变换

根据式(7)和(8),可以将二维离散傅里叶变换及其反变换转化为两次傅里叶变换
及其反变换。
N 1 M 1
1
F (u , v ) 
NM
  f ( x, y )e
x 0 y 0
 j 2 ( ux / N  vy / M )

M 1 N 1
1 1
=
M
 e j 2 vy / M
y 0 N
 f ( x, y )e
x 0
 j 2 ux / N
(20)
N 1 M 1
1 1
=
N
 e j 2 ux / N
x 0 M
 f ( x, y ) e
y 0
 j 2 vy / M
N 1 M 1
f ( x, y )    F (u, v)e j 2 ( ux / N  vy / M )
u 0 v 0
N 1 M 1
  e j 2 ux / N  F (u , v )e j 2 vy / M (21)
u 0 v0
M 1 N 1
  e j 2 vy / M  F (u, v)e j 2 ux / N
v 0 u 0

由式(20)和(21)可知,对于二维的 DFT,可以先求出 y 方向(所有行的)的傅里


叶变换,y 方向上有 N 个点,总共求 M 次;然后,求 x 方向(所有列的)的傅里叶变换,x
方向上有 M 个点,总共求 N 次。对于二维的 iDFT,可以先求出 x 方向(所有列的)的傅里
叶反变换,再求 y 方向上的傅里叶反变换。方向的选择可以对调,但最后的结果是一样的。
将快速傅里叶变换及其反变换算法按照同样的方法可以推广到二维情况下,在这里就不早
赘述了。
本节主要是讲述了快速傅里叶变换对的原理。在给出四个基本的一定后,通过定理的形
式给出快速傅里叶变换的理论依据。定理 1 和定理 2 是傅里叶变换对的核心思想。这两个定
理都是基于 2 的,在此基础上,有人提出基于 4 的傅里叶变换对。相比而言,基于 4 的傅立
叶变换对的速度更快。但是基于 2 的傅立叶变换对直观,而且容易实现,对于一般的实时性
不是很高的要求均可以满足。快速傅里叶变换算法的思想就是将原始的序列一级一级的划分,
重排序,再用迭代的方法计算出最后的结果。如果每次划分后都要求重排序,又增加运算量,
可能会导致得不偿失。能否只对原始序列排一次序就可以,实现“一劳永逸”呢?这就是在
下一节中,我们需要首先解决的问题,比特逆序重排。下一节主要介绍实现快速傅里叶变换
的实现。在解决比特逆序重排后,分别给出一维离散傅里叶快速变换对和二维离散傅里叶快
速变换对。

3. 快速傅里叶变换实现

为了具体说明快速傅里叶变换算法,下面举一个例子。

给定一个长度为 8 的序列 f ( x)  { f (0), f (1), f (2), f (3), f (4), f (5), f (6), f (7)} ,要

求给出 F (u ) 。

首 先 将 序 列 按 下 标 的 奇 偶 性 分 成 两 个 子 序 列 f 0 ( x)  { f (0), f (2), f (4), f (6)} 和

f1 ( x)  { f (1), f (3), f (5), f (7)} 。然后按照离散傅里叶变换求 F0 (u ) 和 F1 (u ) ,再根据式

(9)和(10)就可以计算得到 F (u ) 。

对 f 0 ( x) 和 f1 ( x) , 继 续 分 解 得 到 四 个 子 序 列 , 分 别 是 f 00 ( x)  { f (0), f (4)} 与

f 01 ( x)  { f (2), f (6)} 和 f10 ( x)  { f (1), f (5)} 与 f11 ( x )  { f (3), f (7)} 。重复前面的方法,

直接计算 F00 (u ) 与 F01 (u ) 和 F10 (u ) 与 F11 (u ) ,并用式(9)和(10)计算 F0 (u ) 和 F1 (u ) 。


最后,将 f 00 ( x ) 、f 01 ( x) 、f10 ( x ) 和 f11 ( x ) 再划分为 f 000 ( x)={f (0)} 与 f 001 ( x)={f (4)} 、

f 010 ( x)  { f (2)} 与 f 011 ( x)  { f (6)} 、 f100 ( x)  { f (1)} 与 f101 ( x)  { f (5)} 、 和

f110 ( x )  { f (3)} 与 f111 ( x)  { f (7)} 。 由 于 一 点 序 列 的 DFT 就 是 本 身 , 即 是

Fist (u )  f is ( x) ,其中 i, s, t  {0,1} 。再利用式( 9 )和( 10 )就可以计算得到 F00 (u ) 与

F01 (u ) 和 F10 (u ) 与 F11 (u ) ,依次类推就可以计算得到 F0 (u ) 和 F1 (u ) ,最后就可以得到

F (u ) 。更加形象的表示就如图 4 所示。

图 4 N=8 的离散快速傅里叶变换计算流图
从图 4 中可以看出,有些运算蝶相互交叉,有些是没有交叉的。交叉的运算蝶合称为蝶
群。例如:第一级中有 4 个蝶群,第二级有 2 个蝶群,第三级有 1 个蝶群。图 4 中的 m 表示
存储地址。一个蝶群所占空间的大小就是蝶群的长度,如:第 1 级中的蝶群长度是 2,第 2
级中的蝶群长度是 4,第 3 级中蝶群长度是 8。个蝶群中运算蝶的个数也不同,第 1 级蝶群
中的运算蝶的个数是 1,第 2 级蝶群中运算蝶的个数是 2,第 3 级蝶群中的运算蝶的个数是
4。显然,蝶群中运算蝶的个数等于蝶群长度的 1/ 2 。
根据上述讨论,我们可以得出如下的一个推论:

推论 1:给定长度为 N  2 ( l 为整数)的序列 f ( x) ,利用快速傅里叶变换求解其相


l

应的 F (u ) 时,

(1)共分为 l 级;

(2)对于第 i 级, i  1, 2,3,..., l ,蝶群的长度为 2 ,共 N / 2  2


i i l i
个蝶群;

(3)每个运算蝶的左边有两个输出,因此对于长度为 N  2 的序列,在每一级一共
l

l 1
有N /22 个运算蝶。
l 1
(4 )在第 i 级中,有 2
l i
个蝶群,各蝶群有 2 / 2l i  2i 1 个运算蝶;同一运算蝶中,
i1
同侧蝶尖的距离是 2 ;

i 1
(5)对于第 i 级的第 j 号蝶群,其中 j  0,1,..., 2  1 ,其蝶群地址为 j 2 。对该蝶
i

i 1
群中的第 r 号运算蝶,其中 r  0,1,..., 2  1 ,其运算蝶地址是 s  j 2i  r ,其翅权是

W2ri 。

对于傅里叶变换,设第 i 级的输入序列是 Fi 1 (u ) ,输出序列是 Fi (u ) ,则该级的各运

算蝶可以表示为:
 Fi ( s)  Fi 1 ( s )  W2ri Fi 1 ( s  2i 1 )
 i 1 i 1 (22)
 Fi ( s  2 )  Fi 1 ( s )  W2i Fi 1 ( s  2 )
r

式中 i, j , s, r 的取值及其关系与推论 1 中的结论一致。

对与傅里叶反变换,则第 i 级的输入序列是 Fi (u ) ,输出序列是 Fi 1 (u ) ,该级的各运

算蝶可表示为:
 1
 Fi 1 ( s )  ( Fi ( s )  Fi ( s  2i 1 ))
2
 (23)
 W i r
Fi 1 ( s  2i 1 )  2 ( Fi ( s )  Fi ( s  2i 1 ))
 2

式中 i, j , s, r 的取值及其关系同样与推论 1 中的结论一致。

有上可知,傅里叶变换及其反变换的区别在于:第一,傅里叶变化的级数 i 从 1 到 l ;
傅里叶反变换的级数 i 从 l 到 1;第二,如图 4 所示,在进行傅里叶变换之前需要对原序列
进行重排序;傅里叶反变换是得到第 1 级数据后再进行重排,最后才能得到原始序列。

3.1 比特逆重排

下面我们主要讨论最后一个重要的问题,序列的重新排序。图 4 中的第 1、2 和 3 列就是


重排的一个实例。对原始序列重排好像无从下手,没有什么规律可循。可是仔细观察后可以

发现,其中是有规律的。观察图 4 中的第 2 列 f ist ( x) 的下标 ist ,可以发现,若把它当作 3

位的二进制数,则其值和存储地址 m 是完全相同的;另外, ist 的次序颠倒后得到的 tsi

刚好与重排后得到的序列的下标相等。这种规律具有普遍性,即 f ( x) 在重排后,其下标 n

和地址 m 之间,就二进制数表示形式而言,存在次序互逆的关系,通常把这种规律称为比
特逆序。
直接位求比特逆序数的效率不是很高。在本文中,我们采用的是另外一中方法,推移法。
设 n 为一十进制数,且 n  0,1,..., 2  2 。不失一般性, n 可以表示为
l

(n)10  (bl 1...bk 1 01...1) 2 (24)

即是 n 的二进制数中的第一个 0 出现在第 k 位。则


(n  1)10  (bl 1...bk 110...0) 2 (25)

即 n 的二进制数中的第一个 1 出现在第 k 位。

设 n 的逆序数是  (n) 。则

(  (n))10  (1...10bk 1...bl 1 ) 2 (26)

同理, n  1 的逆序数为
(  (n  1))10  (0...01bk 1...bl 1 ) 2 (27)

由式(26)和(27)可得
 (n  1)   (n)  (2l 1  2l  2  ...  2l  k )  2l  k 1 (28)

由于当 n  0 时,  (n)  0 。因此,根据式(28)可以依次计算出  (1),  (2),...,  (2  1) 。


l

根 据 上 面 的 讨 论 , 我 们 可 知 : 在 应 用 推 移 法 由  (n) 求 解  (n +1) 时 ,  (n) 与

2l 1 , 2l  2 ,... 比较,够减就减,得到差后继续比较,直到不够为止,最后加上 2l  k 1 ,就得

到了  (n +1) 。比特逆重排的算法如下:

算法 1:对给定的序列进行比特逆重排

输入:序列 f ( x ) 和其长度 N

输出:比特逆重排后的序列 f ( x)

步骤:
0. 开始;
1. 初始化 0 的逆序数 i , i  0 ;计算 2 并保存, m  N / 2 ;
l 1

2. 初始化序列的下标 j , j  0 ;

3. 计算数 j 的逆序数 i ,步骤如下:

并保存, s  m ;
l 1
3.1 计算 2

3.2 如果 i  m ,则够减,执行下一步;否则,转移到 3.5;


3.3 作减法, i  i  m ;
向右移一位, s  s / 2 ;
l 1
3.4 2

3.5 转移到 3.2;


3.5 作加法, i  i  m ;

4. 如果 j  i , f (i )  f ( j ) ;否则转移到 5;

5. 处理下一个数, j  j  1 ;

6. 如果 j  N ,则转移到 3;否则转移到 7;

7. 结束。

3.2 一维快速傅里叶变换对的实现

在讨论了比特重排和快速傅里叶变换对后,可以很明确的给出基 2 的快速傅里叶变换
对算法。
算法 2:求给定的序列的傅里叶变换

输入:序列 f ( x) 和其长度 N

输出:序列 f ( x) 的傅里叶变换 F (u )

步骤:
0. 开始;

1. 用 f ( x) 初始化 F (u ) ,对 0  i  N , F (i ).real  f (i ).real , F (i ).image  f (i ).image ;

2. 调用算法 1,对 F (u ) 进行比特逆重排;

3. 计算运算级数, CountLevel  log N ;

4. 初始化运算级标号 LevelIndex , LevelIndex  1 ;


5. 对 LevelIndex 级中的运算蝶进行计算,步骤如下:
LevelIndex 1
5.1 计算蝶群中运算蝶的个数 CountComputer , CountComputer  2 ;

5.2 计算蝶群的长度 LengthCluster , LengthCluster  2


LevelIndex

5.3 计算蝶群的个数 CountCluster , CountCluster  N / LengthCluster ;

5.4 初始化蝶群标号 ClusterIndex , ClusterIndex  0 ;


5.5 对 ClusterIndex 号蝶群中的运算蝶进行计算,步骤如下:
5.5.1 计算蝶群首地址 AddressCluster , AddressCluster  ClusterIndex * LengthCluster ;

5.5.2 初始化运算蝶的标号 ComputerIndex , ComputerIndex  0 ;


5.5.3 对第 ComputerIndex 号运算蝶作如下计算:

5.5.3.1 计算地址 s0 , s0  AddressCluster  IndexComputer ;

5.5.3.2 计算地址 s1 , s1  s0  2
IevelIndex -1

2
5.5.3.3 计算 W , W  exp( j LevelIndex
ComputerIndex) ;
2

5.5.3.4 计算临时变量 temp 0 , temp 0  F ( s0 )  WF ( s1 ) ;

5.5.3.5 计算临时变量 temp1 , temp1  F ( s0 )  WF ( s1 ) ;

5.5.3.6 F ( s0 )  temp 0 ;

5.5.3.7 F ( s1 )  temp1 ;

5.5.3.8 转移到下一个运算蝶, ComputerIndex  ComputerIndex  1 ;

5.5.3.9 如果 ComputerIndex  CountComputer ,则转移到 5.5.3.1 ;否

则转移到 5.5.4;
5.5.4 转移到下一个蝶群, ClusterIndex  ClusterIndex  1 ;
5.5.5 如果 ClusterIndex  CountCluster ,则转移到 5.5.1;否则转移到 5.6;
5.6 转移下一运算级, LevelIndex  LevelIndex  1 ;
5.7 如果 LevelIndex  CountLevel ,则转移到 5.1;否则转移到 6;
6. 结束。
快速傅里叶反变换的运算顺序刚好与快速傅里叶变换相反。同时在进行蝶式运算的时候,
运算公式需做改变,如式(23)。最后就是,快速傅里叶变换是重排后在运算,快速傅里叶
反变换是运算后再重排。快速傅里叶反变换算法如下:
算法 3:求给定的序列的傅里叶反变换

输入:序列 F (u ) 和其长度 N

输出:序列 F (u ) 的傅里叶反变换 f ( x)

步骤:
0. 开始;

1. 用 F (u ) 初始化 f ( x) ,对 0  i  N , f (i ).real  F (i ).real , f (i ).image  F (i ).image ;

2. 计算运算级数, CountLevel  log N ;

3. 初始化运算级标号 LevelIndex , LevelIndex  CountLevel ;


4. 对 LevelIndex 级中的运算蝶进行计算,步骤如下:
LevelIndex 1
4.1 计算蝶群中运算蝶的个数 CountComputer , CountComputer  2 ;
4.2 计算蝶群的长度 LengthCluster , LengthCluster  2
LevelIndex

4.3 计算蝶群的个数 CountCluster , CountCluster  N / LengthCluster ;

4.4 初始化蝶群标号 ClusterIndex , ClusterIndex  0 ;


4.5 对 ClusterIndex 号蝶群中的运算蝶进行计算,步骤如下:
4.5.1 计算蝶群首地址 AddressCluster , AddressCluster  ClusterIndex * LengthCluster ;

4.5.2 初始化运算蝶的标号 ComputerIndex , ComputerIndex  0 ;

4.5.3 对第 ComputerIndex 号运算蝶作如下计算:

4.5.3.1 计算地址 s0 , s0  AddressCluster  IndexComputer ;

4.5.3.2 计算地址 s1 , s1  s0  2
IevelIndex -1

2
4.5.3.3 计算 W , W  exp( j LevelIndex
ComputerIndex ) ;
2

4.5.3.4 计算临时变量 temp 0 , temp 0  F ( s0 )  WF ( s1 ) ;

4.5.3.5 计算临时变量 temp1 , temp1  F ( s0 )  WF ( s1 ) ;

4.5.3.6 F ( s0 )  temp 0 ;

4.5.3.7 F ( s1 )  temp1 ;

4.5.3.8 转移到下一个运算蝶, ComputerIndex  ComputerIndex  1 ;

4.5.3.9 如果 ComputerIndex  CountComputer ,则转移到 4.5.3.1 ;否

则转移到 4.5.4;
4.5.4 转移到下一个蝶群, ClusterIndex  ClusterIndex  1 ;
4.5.5 如果 ClusterIndex  CountCluster ,则转移到 5.5.1;否则转移到 5.6;
4.6 转移下一运算级, LevelIndex  LevelIndex  1 ;
4.7 如果 LevelIndex  0 ,则转移到 4.1;否则转移到 5;

5. 调用算法 1,对 f ( x) 进行比特逆重排;

6. 结束。

3.3 二维快速傅里叶变换对的实现

2.2 节详细的讨论了傅里叶变换从一维推广到二维的理论推导。在实现二维快速傅里叶
变换的时候,同样可以将二维变换分解为两个方向上的变换。在算法 2 和算法 3 的基础上,
同样可以给出二维傅里叶变换对的算法。在给出算法之前,为了方便理解,需要对几个符号
进行必要的说明:

(1)
x, y , u , v 表示序列表记,如: g ( x) 或者 g ( y ) ,表示一个序列;

(2) i, j 表示序列中元素位置标记,如: g (i ) 或者 g ( j ) ,表示一个序列的第 i 或者第


j 个元素。

算法 4:求给定二维序列的傅里叶变换
y
输入:序列 f ( x, y ) , x 方向上的长度 N 和 方向上的长度 M

输出:序列 f ( x, y ) 的傅里叶变换 F (u , v )

步骤:
0. 开始;

1. 对 f ( x, y ) 的每一列作傅里叶变换:

1.1 初始化列标记 i , i  0 ;

1.2 将第 i 列的数据缓存起来,对 0  j  M , g ( j )  f (i, j ) ;

1.3 调用算法 2,对 g ( x) 作傅里叶变换,得到其傅里叶变换 G (u ) ;

1.4 将 G (u ) 存储到 F (u , v ) 相应列中的位置,对 0  j  M , F (i, j )  G ( j ) ;

1.5 转移到下一列, i  i  1 ;
1.6 如果 i  N ,则转移到 1.2;否则转移到 2;

2. 对 F (u , v ) 的每行作傅里叶变换:

2.1 初始化行标记 j , j  0 ;

2.2 将第 j 行的数据缓存起来,对 0  i  N , g (i )  F (i, j ) ;

2.3 调用算法 2,对 g ( y ) 作傅里叶变换,得到其傅里叶变换 G (v ) ;

2.4 将 G (v ) 存储到 F (u , v ) 相应行中的位置,对 0  i  N , F (i, j )  G (i ) ;

2.5 转移到下一行, j  j  1 ;

2.6 如果 j  M ,则转移到 2.2;否则转移到 3;

3. 结束。
算法 5:求给定二维序列的傅里叶反变换

输入:序列 F (u , v ) , u 方向上的长度 N 和 v 方向上的长度 M


输出:序列 F (u , v ) 的傅里叶变换 f ( x, y )

步骤:
0. 开始;

1. 对 F (u , v ) 的每一列作傅里叶反变换:

1.1 初始化列标记 i , i  0 ;

1.2 将第 i 列的数据缓存起来,对 0  v  M , G (v )  F (i, v) ;

1.3 调用算法 3,对 G (v ) 作傅里叶反变换,得到其傅里叶反变换 g ( y ) ;

1.4 将 g ( y ) 存储到 f ( x, y ) 相应列中的位置,对 0  j  M , f (i, j )  g ( j ) ;

1.5 转移到下一列, i  i  1 ;
1.6 如果 i  N ,则转移到 1.2;否则转移到 2;

2. 对 F (u , v ) 的每行作傅里叶反变换:

2.1 初始化行标记 j , j  0 ;

2.2 将第 j 行的数据缓存起来,对 0  u  N , G (u )  f (u , j ) ;

2.3 调用算法 3,对 G (u ) 作傅里叶变换,得到其傅里叶反变换 g ( x) ;

2.4 将 g ( x) 存储到 f ( x, y ) 相应行中的位置,对 0  i  N , f (i, j )  g (i ) ;

2.5 转移到下一行, j  j  1 ;

2.6 如果 j  M ,则转移到 2.2;否则转移到 3;

3.结束。

本节主要讲叙了快速傅立叶变换对的算法实现。首先通过一个实际的例子,让大家有个
大概的认识。接着给出快速傅立叶变换对实现的理论推导和数学表达。最后,通过 5 个算法
给出了比特逆重排、一维傅里叶变换对和二维傅里叶变换对的具体实现步骤。5 个算法均用
伪代码给出,方便计算机语言的实现。下一节中用 c 语言实现本节中给出的算法,同时实现
一般的傅里叶变换对。通过两者之间的对比,分析快速傅里叶变换对的精度和效率。

4. 实验

任何算法首先需要解决的问题是算法的有效性,其次就是应该考虑算法的复杂度。同理,
同样需要对快速傅立叶变换对的有效性和时间复杂度进行验证。这一节主要的任务是验证快
速傅立叶变换对算法的有效性和在时间复杂度上的优势。
2
对一维傅里叶变换对的实验方案如下。设时域信号是 f ( x)  sin( x) ,其中 T 是采
T

样周期,实验中 T  500 。经过一维傅里叶变换后,得到的频域下的信号是 F (u ) ; F (u )

经 过 一 维 傅 里 叶 反 变 换 后 得 到 的 时 域 信 号 是 if ( x) 。 试 验 中 , 信 号 的 长 度 是 N ,

0  x, u  N , 且 x 和 u 均是整数。再设 F0 (u ) 是 f ( x ) 通过式(5)计算得到的频域信号。

快速一维傅里叶变换对的精度可以通过 f ( x ) 与 if ( x) 的距离,以及 F (u ) 与 F0 (u ) 的距离

表征。实验中,直接取欧氏距离作为测度,即是:
N 1
FFTError  F (u )  F0 (u ) =  | F (k )  F (k ) |
k= 0
0
2

(29)

N 1
iFFTError  f ( x)  if ( x) =  | f (t )  if (t ) |
t= 0
2

(30)

实 验 中 , 测 试 环 境 如 下 : CPU 是 Intel Pentium 4 主 频 为 2.66GHZ , 内 存 大 小 为


512MB,操作系统是 Windows XP。在实验中,通过改变 N 的值进行多次实验。实验结果如
下:

N FFTError iFFTError
1 16 0.000001 0.000000
2 32 0.000007 0.000000
3 64 0.000056 0.000000
4 128 0.000340 0.000000
5 256 0.000856 0.000000
6 512 0.006116 0.000000
7 1024 0.026522 0.000000

表 1 一维傅里叶变换对精度

从表 1 可知,一维傅里叶变换的误差随 N 增加而增加;而一维傅里叶反变换的误差是
常数,为 0.000000。理论上分析,快速傅里叶变换对和傅里叶变换对是等价的,两者的误差
应该均是 0.000000。由式(5)可知,傅里叶变换对中涉及到大量的欧拉级数计算。对于这类
计算,计算机给出的是一个近似值,与实际值是有误差的。随着计算次数的增加,误差累加
起来。这种误差累加表现在一维傅里叶变换来上就是误差随信号长度 N 增加而增加。对于傅
里叶反变换来说,由式(22)和(23)可知:与傅里叶变换计算过程一样,级数的计算只
是一个符号的区别,同时其他系数均是精确值。因此,一次的傅里叶变换的误差,经过一次
傅里叶反变换后刚好抵消。表现在一维傅里叶反变换的精度上就是误差为 0.000000。
二维傅里叶变换对的实验方案与一维傅里叶变换对的实验方案基本相同,除精度外还
2
需要计算时间复杂度。设时域信号是 f ( x, y )  sin( ( x  y )) ,其中 T 是采样周期,同样
T
T  500 。经过二维傅里叶变换后,得到的频域下的信号是 F (u , v ) ; F (u , v ) 经过一维傅里

叶反变换后得到的时域信号是 if ( x, y ) 。试验中, x 方向和 u 方向信号的长度是 N ,

0  x, u  N , 且 x 和 u 均是整数; y 方向和 v 方向信号的长度是 M , 0  y, v  M ,


y 和 v 均是整数。再设 F0 (u , v) 是 f ( x, y ) 通过式(7)计算得到的频域信号。最后,设 T

是快速二维傅里叶变换消耗的时间,设 T0 是二维傅里叶变换的时间消耗。快速二维傅里叶

变换对的精度可以通过 f ( x, y ) 与 if ( x, y ) 的距离,以及 F (u , v ) 与 F0 (u , v) 的距离表征。实

验中,直接取欧氏距离作为测度,即是:
M 1 N 1
FFTError  F (u, v )  F0 (u , v) =   | F ( j, k )  F ( j, k ) |
k 0 j 0
0
2

(31)

M-1 N 1
iFFTError  f ( x, y )  if ( x, y ) =  | f (s, t )  if (s, t ) |
t= 0 s= 0
2

(32)

实验中,测试环境与一维傅里叶变换试验环境一样。对于时间复杂度,通过直接调用
time.h 中的函数计时,度量算法的时间复杂度,单位是秒。实验结果如下:

N M FFTError iFFTError T T0

1 16  16 0.000029 0.000000 0 0
2 32  32 0.000470 0.000000 0 1
3 64  64 0.006369 0.000000 0 4
4 128  128 0.048274 0.000000 0 58
5 256  256 0.552975 0.000000 1 932
6 512  512 —— —— 1 ——

表 2 二维傅里叶变换对精度及时间复杂度

需要说明的就是,表 2 中 T 和 T0 出现 0,不是说消耗的时间是 0。由于时间消耗非常小,

超出了精度表示范围,直接忽略为 0。从表 2 中,可知二维傅里叶变换对的精度表现与一维


傅里叶变换对的精度表现一样;快速傅里叶变换的时间复杂度上的优势十分明显。原本我打
算将维数增加到 1024  1024 的,但是发现当维数增加到 512  512 的时候,很长时间也难
以完成测试任务,如是只好作罢。

5. 讨论

本文详细给出了基 2 快速傅里叶变换对的原理和实现,并分别在一维和二维的情况下
对快速傅里叶变换对作了对比研究。文章在介绍了傅里叶变换后,给出了一维和二维傅里叶
变换对的定义一级快速傅里叶变换对的原理;接着以算法的形式给出了快速傅里叶变换的
实现步骤;最后,通过实验,验证了本文给出的算法的正确性和有效性。
在推导傅里叶反变换的时候,很多文献中均是从式(6)和(8)直接进行推导。本文抛
弃了这一常规方法,直接从傅里叶变换的反方向推导。这一方法理论依据就是二元一次方程
组的求解。在试验中,这一方法也得到了验证,证明了这一方法的正确性。

参考文献

[1] 冷建华. 傅里叶变换. 北京:清华大学出版社,2004 年 6 月


[2] J W Cooley,J W Tukey.An algorithm for the machine computation of complex Fourier
series.Mathematics of Computation,Apr 1965,19(4):297-301