傅立叶变换最开始是在大学时学过的,当初的印象是它跟音频图像有关系,各种时领频域的名词让人很容易混淆,而且教材解说得很生涩,学过之后大都忘得一干二净。偶然看见知乎上有一篇文章详细生动地具体描述了傅立叶变换的含义,看过后令人印象深刻。傅立叶变换具体内容都在这个链接里面,在这篇文章里面就不再复述,这里只是说它的应用和代码实现,为了保证运算速度代码用的是C语言实现。
现实生活中很多信息或者说是信号大都不是连续的,而是离散的。因此这篇文章说的傅立叶变换只针对离散傅立叶变换。
傅立叶变换(DFT)公式如下所示:(简书上不能使用 MathJax 编辑公式 ,有点遗憾,只能截图)
x(n)为输入函数,x(k) 由实数和虚数组成( x(k) ( 0 < k < N -1) 是x(n) 中任意某一个点) X(k)则为傅立叶变换后的函数
该公式的算法复杂度是O(N^2)
为了方便用代码写出来可以将这个公式拆分:
设
然后DFT公式可转换为:
由公式三可以写出DFT公式
/**
离散傅立叶变换
@param x 原始实数
@param y 原始虚数
@param a 变换后实数
@param b 变换后虚数
@param N 变换的个数
@param sign 为 -1时 表示进行逆变换
*/
void dft(double x[],double y[],double *a, double *b, int N,int sign){
float q ,W,d,c,s;
q = 2 * M_PI/N;
for (int k = 0; k < N ; k++) {
a[k] = b[k] = 0;
W = q * k;
for (int n = 0; n < N; n++) {
d = W * n;
c = cos(d);
s = sin(d) * sign;
a[k] += x[n]*c + y[n]*s;
b[k] += y[n]*c - x[n]*s;
}
}
if(sign == -1){
c = 1.0/N;
for (int k = 0; k < N ; k++) {
a[k] = c * a[k];
b[k] = c * b[k];
}
}
}
通常来说,一般应用傅立叶变换处理的数据量是比较大的,复杂度O(N^2)这个方法无法保证程序高效率运行。因而出现了快速傅立叶变换(FFT)。
FFT是在DFT的基础上进行的快速变换:
x(n)可以根据奇偶数拆分:
结合公式4和DFT公式得出
由于权系数W有对称性
因而有以下公式:
将x(n)不断按照上面方式拆分直至无法拆分为止,这样就有如下图形算法的出现
上图算法称为蝶形算法,图中 W(k,N)为旋转因子
每个蝶形算拆分如下:
FFT算法复杂度是O(N*log2N)
FFT算法的前提是N必须保证是N的2次幂,如果不够则补零直到条件成立为止。
公式看起来麻烦其实理解后也很简单。算法介绍完毕就开始写代码。
快速傅立叶变换(FFT)
/**
离散快速傅立叶变换
@param x 原始实数 函数结束后保存变换后的实数数值
@param y 原始虚数 函数结束后保存变换后的虚数数值
@param N 变换的个数
@param sign 为 -1时 表示进行逆变换
*/
void FFT(double x[],double y[], int N,int sign){
int i,j,k,L,m = 0,n1,n2;
double c,c1,e,s,s1,t,tr,ti;
double M = log2(N);
if(floor(M) != ceil(M)){
double *a = (double *)malloc(sizeof(double) * N);
double *b = (double *)malloc(sizeof(double) * N);
dft(x, y, a, b, N, 1);
for (int i = 0; i < N; i++) {
x[i] = a[i];
y[i] = b[i];
}
free(a);
free(b);
}else{
m = M;
n1 = N - 1;
for (j = 0, i = 0;i < n1;i++){
if(i < j){
tr = x[j];
ti = y[j];
x[j] = x[i];
y[j] = y[i];
x[i] = tr;
y[i] = ti;
}
k = N/2;
while (k < (j+1)) {
j = j - k;
k = k/2;
}
j = j + k;
}
n1 = 1;
for (L = 1;L <= m;L++){
n1 = 2 * n1;
n2 = n1/2;
e = M_PI/n2;
c = 1.0;
s = 0.0;
c1 = cos(e);
s1 = -sign * sin(e);
for (j = 0; j < n2; j++) { // [0..1) [0..2) [0..4)
for (i = j; i < N; i += n1) {
k = i + n2;
tr = c * x[k] - s * y[k];
ti = c * y[k] + s * x[k];
x[k] = x[i] - tr;
y[k] = y[i] - ti;
x[i] = x[i] + tr;
y[i] = y[i] + ti;
}
t = c;
c = c*c1 - s * s1;
s = t * s1 + s * c1;
}
}
if(sign == -1){
for (i = 0; i < N; i++) {
x[i] /= (N * 1.0);
y[i] /= (N * 1.0);
}
}
}
}
以上就是所有内容,下一篇文章就讲讲下傅立叶变换在开发中的应用。该歇会了。。。
博客原文:click here