使用C语言实现FFT算法(快速傅里叶变换)

文章目录

(一)FFT的基本原理
(二)FFT代码
(三)使用典型函数进行测试

(一)FFT的基本原理

  • FFT的总思路

      第一步,将长序列DFT分解成短序列的DFT(本来是N点的DFT,被分解成两个 N/2 的DFT)
      第二步,分别将 N/2 的DFT分解成 N/4 的DFT
      第三步,继续分。。。最后被分成 2点 的DFT
    
  • 注意事项

      只要开始的序列长度是2的正整数次方,就可以最终被分解成2点的DFT
      分解原则是:对输入进行偶奇分解,对输出进行前后分解(关于偶奇分解可以看上一篇雷德算法)
    
  • 前后分解

  • 蝶形运算(可以体现出偶奇分解和前后分解)

(二)FFT代码

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#define N 1024
#define PI 3.1415

/*复数结构体类型*/
typedef struct{
double realPart;
double imaginaryPart;
}complexNumber;

complexNumber x[N], W[N/2]; //序列数组以及旋转因子数组
 int size_x=0;               //输入序列的长度,只能为2的次幂 
void fastFourierOperation();//快速傅里叶变换算法 
void initRotationFactor();  //生成旋转因子数组 
void changePosition();      //偶奇变换算法 
void outputArray();         //遍历输出数组
void add(complexNumber ,complexNumber ,complexNumber*); //复数加法 
void mul(complexNumber ,complexNumber ,complexNumber*); //复数乘法 
void sub(complexNumber ,complexNumber ,complexNumber*); //复数减法 


int main()
{
	printf("请输入序列的长度:");              //输入序列的大小
	scanf("%d",&size_x);
	printf("\n请分别输入序列的实部和虚部\n");  //输入序列的实部和虚部
	for(int i=0;i<size_x;i++)
	{
		printf("请输入第%d个序列的实部:",i);
		scanf("%lf",&x[i].realPart);
		printf("请输入第%d个序列的虚部:",i);
		scanf("%lf",&x[i].imaginaryPart);
	}
	printf("\n输出原序列:\n");
	outputArray();
	initRotationFactor();  //生成旋转因子数组 
	fastFourierOperation();//调用快速傅里叶变换
	printf("\n输出FFT后的结果:\n");
	outputArray();         //遍历输出数组
	return 0;
}


/*快速傅里叶变换*/
void fastFourierOperation()
{
	int l=0;
	complexNumber up,down,product;
	changePosition();                  //偶奇变换算法
	for(int i=0;i<size_x/2;i++)        //一级蝶形运算
	{   
		l=1<<i;
		for(int j=0;j<size_x;j+= 2*l ) //一组蝶形运算
		{            
			for(int k=0;k<l;k++)       //一个蝶形运算
			{       
				mul(x[j+k+l],W[size_x*k/2/l],&product);
				add(x[j+k],product,&up);
				sub(x[j+k],product,&down);
				x[j+k]=up;
				x[j+k+l]=down;
			}
		}
	}
}


/*生成旋转因子数组*/
void initRotationFactor() 
{
	for(int i=0;i<size_x/2;i++)
	{
		W[i].realPart=cos(2*PI/size_x*i);//用欧拉公式计算旋转因子
		W[i].imaginaryPart=-1*sin(2*PI/size_x*i);
	}
}


/*偶奇变换算法*/
void changePosition()      
{
	int j=0,k;//待会儿进行运算时需要用到的变量 
	complexNumber temp;

	for (int i=0;i<size_x-1;i++) 
	{ 
		if(i<j) 
		{
			//若倒序数大于顺序数,进行变址(换位);否则不变,防止重复交换,变回原数
			temp=x[i];
			x[i]=x[j];
			x[j]=temp;
		}
		k=size_x/2;    //判断j的最高位是否为0
		while(j>=k) 
		{              //最高位为1
			j=j-k;
			k=k/2;
		}
		j=j+k;        //最高位为0
	}
  	printf("\n输出倒序后的结果:\n");
	outputArray();
}


/*遍历输出数组*/
void outputArray()
{
	for(int i=0;i<size_x;i++)
	{
		if(x[i].imaginaryPart<0){
			printf("%.4f-j%.4f\n",x[i].realPart,abs(x[i].imaginaryPart));
		}
		else{
			printf("%.4f+j%.4f\n",x[i].realPart,x[i].imaginaryPart);
		}
	}
}

/*复数加法的定义*/ 
void add(complexNumber a,complexNumber b,complexNumber *c)
{
	c->realPart=a.realPart+b.realPart;
	c->imaginaryPart=a.imaginaryPart+b.imaginaryPart;
}

/*复数乘法的定义*/ 
void mul(complexNumber a,complexNumber b,complexNumber *c)
{
	c->realPart=a.realPart*b.realPart - a.imaginaryPart*b.imaginaryPart;
	c->imaginaryPart=a.realPart*b.imaginaryPart + a.imaginaryPart*b.realPart;
}

/*复数减法的定义*/ 
void sub(complexNumber a,complexNumber b,complexNumber *c)
{
	c->realPart=a.realPart-b.realPart;
	c->imaginaryPart=a.imaginaryPart-b.imaginaryPart;
}

(三)使用典型函数进行测试

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#define N 8 

/*复数结构体类型*/
typedef struct{
double realPart;
double imaginaryPart;
}complexNumber;

complexNumber x[N], W[N/2]; //序列数组以及旋转因子数组
int size_x=N;
double PI=atan(1)*4;        //圆周率
void fastFourierOperation();//快速傅里叶变换算法 
void initRotationFactor();  //生成旋转因子数组 
void changePosition();      //偶奇变换算法 
void outputArray();         //遍历输出数组
void add(complexNumber ,complexNumber ,complexNumber*); //复数加法 
void mul(complexNumber ,complexNumber ,complexNumber*); //复数乘法 
void sub(complexNumber ,complexNumber ,complexNumber*); //复数减法 

/*长度为1024的冲激序列 */
void init1()                
{
	printf("Example:度为1024的冲激序列\n");
	x[0].realPart=1;
	x[0].imaginaryPart=0;
	for(int i=1;i<N;i++)
	{
		x[i].realPart=0;
		x[i].imaginaryPart=0;
	}
}

/*长度为1024的矩形序列 */
void init2()               
{               
	printf("Example:长度为1024的矩形序列\n"); 
	for(int i=0;i<N;i++)
	{
		x[i].realPart=1;
		x[i].imaginaryPart=0;
	}
}

/*长度为1024的sin[2πk/1024]序列 */
void init3()               
{             
	printf("Example:长度为1024的sin[2πk/1024]序列\n"); 
	for(int i=0;i<N;i++)
	{
		x[i].realPart=sin(2*PI*i/N);
		x[i].imaginaryPart=0;
	}
}

/*长度为1024的cos[2πk/1024]序列 */
void init4()              
{             
	printf("Example:长度为1024的cos[2πk/1024]序列\n");
	for(int i=0;i<N;i++)
	{
		x[i].realPart=cos(2*PI*i/N);
		x[i].imaginaryPart=0;
	}
}

int main()
{
    init3();               //调用不同的初始化函数,使用不同的例程进行测试 
	printf("=============================================================================================================================================================================================\n");
	printf("输出原序列:\n");
	outputArray();
	initRotationFactor();  //生成旋转因子数组 
	fastFourierOperation();//调用快速傅里叶变换
	printf("\n\n=============================================================================================================================================================================================\n");
	printf("输出FFT后的结果:\n");
	outputArray();         //遍历输出数组
	return 0;
}


/*快速傅里叶变换*/
void fastFourierOperation()
{
	int l=0;
	complexNumber up,down,product;
	changePosition();                            //偶奇变换算法
	for(int i=0;i<log(size_x)/log(2);i++)        //一级蝶形运算
	{   
		l=1<<i;
		for(int j=0;j<size_x;j+= 2*l )          //一组蝶形运算
		{            
			for(int k=0;k<l;k++)                //一个蝶形运算
			{       
				mul(x[j+k+l],W[size_x*k/2/l],&product);
				add(x[j+k],product,&up);
				sub(x[j+k],product,&down);
				x[j+k]=up;
				x[j+k+l]=down;
			}
		}
	}
}


/*生成旋转因子数组*/
void initRotationFactor() 
{
	for(int i=0;i<size_x/2;i++)
	{
		W[i].realPart=cos(2*PI/size_x*i);//用欧拉公式计算旋转因子
		W[i].imaginaryPart=-1*sin(2*PI/size_x*i);
	}
}


/*偶奇变换算法*/
void changePosition()      
{
	int j=0,k;//待会儿进行运算时需要用到的变量 
	complexNumber temp;
	for (int i=0;i<size_x-1;i++) 
	{ 
		if(i<j) 
		{
			//若倒序数大于顺序数,进行变址(换位);否则不变,防止重复交换,变回原数
			temp=x[i];
			x[i]=x[j];
			x[j]=temp;
		}
		k=size_x/2;    //判断j的最高位是否为0
		while(j>=k) 
		{              //最高位为1
			j=j-k;
			k=k/2;
		}
		j=j+k;        //最高位为0
	}
	printf("\n\n=============================================================================================================================================================================================\n");
	printf("输出倒序后的结果:\n");
	outputArray();
}


/*遍历输出数组*/
void outputArray()
{
	int num=0;
	for(int i=0;i<size_x;i++)
	{		
	
		if(x[i].imaginaryPart<0)
		{
			printf("%.4f-j%.4f     ",x[i].realPart,fabs(x[i].imaginaryPart));
		}
		else
		{
			printf("%.4f+j%.4f     ",x[i].realPart,x[i].imaginaryPart);
		}
		num++;
		if(num==10){
			printf("\n");
			num=0;
		}
	}
}


/*复数加法的定义*/ 
void add(complexNumber a,complexNumber b,complexNumber *c)
{
	c->realPart=a.realPart+b.realPart;
	c->imaginaryPart=a.imaginaryPart+b.imaginaryPart;
}

/*复数乘法的定义*/ 
void mul(complexNumber a,complexNumber b,complexNumber *c)
{
	c->realPart=a.realPart*b.realPart - a.imaginaryPart*b.imaginaryPart;
	c->imaginaryPart=a.realPart*b.imaginaryPart + a.imaginaryPart*b.realPart;
}

/*复数减法的定义*/ 
void sub(complexNumber a,complexNumber b,complexNumber *c)
{
	c->realPart=a.realPart-b.realPart;
	c->imaginaryPart=a.imaginaryPart-b.imaginaryPart;
}

部分运行效果如下:

  • 33
    点赞
  • 218
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
### 回答1: C语言实现FFT快速傅里叶变换快速傅里叶变换FFT)是计算机科学中一种常用的算法,用于将时域信号转换成频域信号。C语言可以很好地实现FFT算法。 首先,我们需要明确FFT算法的基本原理。FFT算法将一个长度为N的离散信号序列转换为具有相同长度N的频谱序列,通过对信号进行逐级划分并进行蝶形运算,最终得到频率分量的幅度和相位信息。 在C语言中,我们可以使用复数数组来表示信号序列和频谱序列。通过定义一个复数结构体,我们可以分别存储实部和虚部: ```c typedef struct { double real; double imag; } Complex; ``` 然后,我们可以实现FFT算法的核心部分,即蝶形运算。蝶形运算是FFT算法中的重要步骤,它将相邻的两个序列点进行复数乘法和加法运算,得到结果后重新排列序列,然后再进行下一级的蝶形运算。以下是一个简单的蝶形运算函数的实现: ```c void butterfly(Complex* x, int N, int k) { int j; Complex W, t; W.real = cos(2 * PI * k / N); W.imag = -sin(2 * PI * k / N); for (j = 0; j < N / 2; j++) { t.real = W.real * x[j + N / 2].real - W.imag * x[j + N / 2].imag; t.imag = W.real * x[j + N / 2].imag + W.imag * x[j + N / 2].real; x[j + N / 2].real = x[j].real - t.real; x[j + N / 2].imag = x[j].imag - t.imag; x[j].real += t.real; x[j].imag += t.imag; } } ``` 最后,我们可以编写一个FFT函数来实现完整的快速傅里叶变换。在该函数中,我们首先将输入序列进行倒位序排列,然后进行多级蝶形运算,最后得到频谱序列。 ```c void FFT(Complex* x, int N) { int i, j, k; //进行倒位序排列 j = 0; for (i = 0; i < N - 1; i++) { if (i < j) { Complex temp = x[i]; x[i] = x[j]; x[j] = temp; } k = N / 2; while (k <= j) { j -= k; k /= 2; } j += k; } //进行多级蝶形运算 for (i = 2; i <= N; i *= 2) { int m = i / 2; for (j = 0; j < N; j += i) { for (k = 0; k < m; k++) { butterfly(x + j + k, i, k); } } } } ``` 通过以上实现,我们可以在C语言中很方便地实现FFT算法。值得注意的是,在实践中,我们通常对FFT算法进行优化,例如使用了位翻转法和预计算旋转因子等技巧。 ### 回答2: C语言实现FFT快速傅里叶变换)需要用到复数运算和递归算法。以下是一个简单的C语言代码示例: ```c #include <stdio.h> #include <math.h> #include <complex.h> #define PI 3.14159265358979323846264338327950288 void fft(complex double *x, int N) { if (N <= 1) return; complex double even[N/2]; complex double odd[N/2]; // 分离奇偶位的元素 for (int i = 0; i < N/2; i++) { even[i] = x[2*i]; odd[i] = x[2*i+1]; } // 递归计算奇偶位的DFT fft(even, N/2); fft(odd, N/2); // 合并奇偶位的DFT for (int i = 0; i < N/2; i++) { complex double t = cexp(-2 * I * PI * i / N) * odd[i]; x[i] = even[i] + t; x[i + N/2] = even[i] - t; } } int main() { int N; printf("输入序列长度:"); scanf("%d", &N); complex double *x = malloc(N * sizeof(complex double)); printf("输入序列的实部和虚部:\n"); // 读取输入序列 for (int i = 0; i < N; i++) { double real, imag; scanf("%lf %lf", &real, &imag); x[i] = real + imag * I; } // 进行快速傅里叶变换 fft(x, N); // 打印结果 printf("快速傅里叶变换结果:\n"); for (int i = 0; i < N; i++) { printf("%.2f + %.2fj\n", creal(x[i]), cimag(x[i])); } free(x); // 释放内存 return 0; } ``` 以上代码实现了基于递归算法快速傅里叶变换。在主函数中,我们通过读取输入的实部和虚部构造了一个复数序列,并将其作为参数传递给fft函数进行变换。最后,打印出快速傅里叶变换的结果。 请注意,上述代码只是一个简单示例,可能需要进行错误处理、内存释放等改进。此外,还有其他实现FFT的方法,如迭代算法(非递归实现)和改进的Cooley-Tukey算法等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zheng_zq666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值