1 快速傅立换变换的简介
1.1 傅里叶变换的不足
对于一个长度为 M MM 的信号序列来讲,如果我们要进行傅里叶变换,根据公式:
1.2 快速傅里叶变换
4点的FFT快速算法信号流图如下所示:
我们可以从信号流图的左侧观察到原序列发生了变换,即变化后的序列索引对应的元素与变化前不一致,要想实现此变换也是比较简单的,只需要将原位置元素的索引的二进制左右调换后重新赋予新索引对应的元素即可,例如:
f ( 0 ) f(0)f(0)排序后为f ( 0 ) f(0)f(0),0 00的二进制为00 0000,左右调换后为00 0000,所以不变。
f ( 1 ) f(1)f(1)排序后为f ( 2 ) f(2)f(2),1 11的二进制为01 0101,左右调换后为10 1010,所以变为2。
f ( 2 ) f(2)f(2)排序后为f ( 1 ) f(1)f(1),2 22的二进制为10 1010,左右调换后为01 0101,所以变为1。
f ( 3 ) f(3)f(3)排序}后为f ( 3 ) f(3)f(3),3 33的二进制为11 1111,左右调换后为11 1111,所以不变。
2 快速傅里叶变换的实现
声明:本代码的主体是从一位博主处copy来的,本想注明出处,但是因未及时收藏导致后续竟找不到此博主的相关信息,对此我深表遗憾。本人在原博主代码的基础上加以修改(增加了反傅里叶变换功能、序列长度不为2的幂次方时对序列进行拓展的功能),并伴以详细的注释,以飨大家。此外,由于本人能力问题,代码还存在诸多不完美之处,例如:1、将序列填充至快速傅里叶变换长度之后,需要手动定义后续复数数组的长度以进行傅里叶变换;2、在实现功能的过程中,函数有些繁杂,且某些函数内部没有进行优化,还望诸位看客多多见谅。
2.1一些变量的说明:
2.2代码实现
1 #include <list>
2 #include <cmath>
3 #include <string>
4 #include <vector>
5 #include <iostream>
6
7 const int PI = 3.1415926;
8 using namespace std;
9
10 //定义复数结构
11 struct Complex
12 {
13 double imaginary;
14 double real;
15 };
16
17 //进行FFT的数据,此处默认长度为2的幂次方
18 //double Datapre[] = {1, 2, 6, 4, 48, 6, 7, 8, 58, 10, 11, 96, 13, 2, 75, 16};
19 double Datapre[] = {100, 2, 56, 4, 48, 6, 45, 8, 58, 10};
20
21 //复数乘法函数
22 Complex ComplexMulti(Complex One, Complex Two);
23
24 //将输入的任意数组填充,以满足快速傅里叶变换
25 void Data_Expand(double *input, vector<double> &output, const int length);
26
27 //将输入的实数数组转换为复数数组
28 void Real_Complex(vector<double> &input, Complex *output, const int length);
29
30 //快速傅里叶变换函数
31 void FFT(Complex *input, Complex *StoreResult, const int length);
32
33 //快速傅里叶反变换
34 void FFT_Inverse(Complex *arrayinput, Complex *arrayoutput, const int length);
35
36 int main()
37 {
38 //获取填充后的Data;
39 vector<double> Data;
40 Data_Expand(Datapre, Data, 10);
41
42 //打印填充后的序列
43 for (auto data : Data)
44 {
45 cout << data << endl;
46 }
47
48 //因为下面的复数结构未采用动态数组结构,所以需要根据实际情况制定数组的大小
49 //将输入数组转化为复数数组
50 Complex array1D[16];
51
52 //存储傅里叶变换的结果
53 Complex Result[16];
54
55 //存储反傅里叶变换的结果
56 Complex Result_Inverse[16];
57
58 Real_Complex(Data, array1D, 16);
59 FFT(array1D, Result, 16);
60 FFT_Inverse(Result, Result_Inverse, 16);
61 //基于范围的循环,利用auto自动判断数组内元素的数据类型
62 for (auto data : Result_Inverse)
63 {
64 //输出傅里叶变换后的幅值
65 cout << data.real << "\t" << data.imaginary << endl;
66 }
67
68 return 0;
69 }
70
71 Complex ComplexMulti(Complex One, Complex Two)
72 {
73 //Temp用来存储复数相乘的结果
74 Complex Temp;
75 Temp.imaginary = One.imaginary * Two.real + One.real * Two.imaginary;
76 Temp.real = One.real * Two.real - One.imaginary * Two.imaginary;
77 return Temp;
78 }
79
80 //此处的length为原序列的长度
81 void Data_Expand(double *input, vector<double> &output, const int length)
82 {
83 int i = 1, flag = 1;
84 while (i < length)
85 {
86 i = i * 2;
87 }
88
89 //获取符合快速傅里叶变换的长度
90 int length_Best = i;
91
92 if (length_Best != length)
93 {
94 flag = 0;
95 }
96
97 if (flag)
98 {
99 //把原序列填充到Vector中
100 for (int j = 0; j < length; ++j)
101 {
102 output.push_back(input[j]);
103 }
104 }
105
106 else
107 {
108 //把原序列填充到Vector中
109 for (int j = 0; j < length; ++j)
110 {
111 output.push_back(input[j]);
112 }
113 //把需要拓展的部分进行填充
114 for (int j = 0; j < length_Best - length; j++)
115 {
116 output.push_back(0);
117 }
118 }
119 }
120
121 //此处的length为填充后序列的长度
122 void Real_Complex(vector<double> &input, Complex *output, const int length)
123 {
124 for (int i = 0; i < length; i++)
125 {
126 output[i].real = input[i];
127 output[i].imaginary = 0;
128 }
129 }
130
131 //FFT变换函数
132 //input: input array
133 //StoreResult: Complex array of output
134 //length: the length of input array
135 void FFT(Complex *input, Complex *StoreResult, const int length)
136 {
137
138 //获取序列长度在二进制下的位数
139 int BitNum = log2(length);
140
141 //存放每个索引的二进制数,重复使用,每次用完需清零
142 list<int> Bit;
143
144 //暂时存放重新排列过后的输入序列
145 vector<double> DataTemp1;
146 vector<double> DataTemp2;
147
148 //遍历序列的每个索引
149 for (int i = 0; i < length; ++i)
150 {
151 //flag用来将索引转化为二进制
152 //index用来存放倒叙索引
153 //flag2用来将二值化的索引倒序
154 int flag = 1, index = 0, flag2 = 0;
155
156 //遍历某个索引二进制下的每一位
157 for (int j = 0; j < BitNum; ++j)
158 {
159 //十进制转化为长度一致的二进制数,&位运算符作用于位,并逐位执行操作
160 //从最低位(右侧)开始比较
161 //example:
162 //a = 011, b1 = 001, b2 = 010 ,b3 = 100
163 //a & b1 = 001, a & b2 = 010, a & b3 = 000
164 int x = (i & flag) > 0 ? 1 : 0;
165
166 //把x从Bit的前端压进
167 Bit.push_front(x);
168
169 //等价于flag = flag << 1,把flag的值左移一位的值赋给flag
170 flag <<= 1;
171 }
172
173 //将原数组的索引倒序,通过it访问Bit的每一位
174 for (auto it : Bit)
175 {
176 //example:其相当于把二进制数从左到右设为2^0,2^1,2^2
177 //Bit = 011
178 //1: index = 0 + 0 * pow(2, 0) = 0
179 //2: index = 0 + 1 * pow(2, 1) = 2
180 //3: index = 2 + 1 * pow(2, 2) = 6 = 110
181 index += it * pow(2, flag2++);
182 }
183
184 //把Data[index]从DataTemp的后端压进,其实现了原序列的数据的位置调换
185 //变换前:f(0), f(1), f(2), f(3), f(4), f(5), f(6), f(7)
186 //变换后:f(0), f(4), f(2), f(6), f(1), f(5), f(3), f(7)
187 DataTemp1.push_back(input[index].real);
188 DataTemp2.push_back(input[index].imaginary);
189
190 //清空Bit
191 Bit.clear();
192 }
193
194 for (int i = 0; i < length; i++)
195 {
196 //将数据转移到复数结构里,StoreResult[i]的索引与原序列的索引是不一样的,一定要注意
197 StoreResult[i].real = DataTemp1[i];
198 StoreResult[i].imaginary = DataTemp2[i];
199 }
200
201 //需要运算的级数
202 int Level = log2(length);
203
204 Complex Temp, up, down;
205
206 //进行蝶形运算
207 for (int i = 1; i <= Level; i++)
208 {
209 //定义旋转因子
210 Complex Factor;
211
212 //没有交叉的蝶形结的距离(不要去想索引)
213 //其距离表示的是两个彼此不交叉的蝶型结在数组内的距离
214 int BowknotDis = 2 << (i - 1);
215
216 //同一蝶形计算中两个数字的距离(旋转因子的个数)
217 //此处距离也表示的是两个数据在数组内的距离(不要去想索引)
218 int CalDis = BowknotDis / 2;
219
220 for (int j = 0; j < CalDis; j++)
221 {
222 //每一级蝶形运算中有CalDis个不同旋转因子
223 //计算每一级(i)所需要的旋转因子
224 Factor.real = cos(2 * PI / pow(2, i) * j);
225 Factor.imaginary = -sin(2 * PI / pow(2, i) * j);
226
227 //遍历每一级的每个结
228 for (int k = j; k < length - 1; k += BowknotDis)
229 {
230 //StoreResult[k]表示蝶形运算左上的元素
231 //StoreResult[k + CalDis]表示蝶形运算左下的元素
232 //Temp表示蝶形运算右侧输出结构的后半部分
233 Temp = ComplexMulti(Factor, StoreResult[k + CalDis]);
234
235 //up表示蝶形运算右上的元素
236 up.imaginary = StoreResult[k].imaginary + Temp.imaginary;
237 up.real = StoreResult[k].real + Temp.real;
238
239 //down表示蝶形运算右下的元素
240 down.imaginary = StoreResult[k].imaginary - Temp.imaginary;
241 down.real = StoreResult[k].real - Temp.real;
242
243 //将蝶形运算输出的结果装载入StoreResult
244 StoreResult[k] = up;
245 StoreResult[k + CalDis] = down;
246 }
247 }
248 }
249 }
250
251 void FFT_Inverse(Complex *arrayinput, Complex *arrayoutput, const int length)
252 {
253 //对傅里叶变换后的复数数组求共轭
254 for (int i = 0; i < length; i++)
255 {
256 arrayinput[i].imaginary = -arrayinput[i].imaginary;
257 }
258
259 //一维快速傅里叶变换
260 FFT(arrayinput, arrayoutput, length);
261
262 //时域信号求共轭,并进行归一化
263 for (int i = 0; i < length; i++)
264 {
265 arrayoutput[i].real = arrayoutput[i].real / length;
266 arrayoutput[i].imaginary = -arrayoutput[i].imaginary / length;
267 }
268 }
2.3程序的输出
3 小结