• C++ 快速傅里叶变换


    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 小结

  • 相关阅读:
    一笔画问题(搜索)
    Sum
    js获取时间日期
    [Hibernate 的left join]Path expected for join!错误
    关于firefox下js中动态组装select时指定option的selected属性的失效
    mooltools扩展之前已经定义好的方法和json数据
    HttpSession, ActionContext, ServletActionContext 区别
    japidcontroller自动绑定的数据类型
    ConcurrentModificationException
    Hibernate中使用COUNT DISTINCT
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/15918665.html
Copyright © 2020-2023  润新知