快速傅里叶变换

2023-05-16

FFT,即为快速傅氏变换,是离散傅氏变换的快速算法,它是根据离散傅氏变换的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的。它对傅氏变换的理论并没有新的发现,但是对于在计算机系统或者说数字系统中应用离散傅立叶变换,可以说是进了一大步。这是360百科中对于fft的一种概念解释。我们可以这么理解:FFT(Fast Fourier Transformation)就是“快速傅里叶变换”的意思,它是一种用来计算DFT(离散傅里叶变换)和IDFT(离散傅里叶反变换)的一种快速算法。这种算法运用了一种高深的数学方式、把原来复杂度为O(n2)的朴素多项式乘法转化为了O(nlogn)的算法。

那么这种快速傅里叶变换是如何实现的呢?由于离散信号是由很多点组成的,所以DFT的公式如公式1所示。而连续信号的傅里叶变换是在连续域中进行的运算,其积分运算公式如公式2所示。离散傅里叶变换将这个积分运算变为了累加运算,即使如此,实际运算量也依然非常可观。所以在实际的应用场景中,离散傅里叶变换的实用性较差。因此,FFT作为新的计算方式被广泛接受。其本质是将离散傅里叶变换通过它本身的奇、偶、虚、实的性质,对该公式进行推导改进。公式1中的k和t均为时域中的输入参数,X(k)以及F[f(t)]即为相对与x以及t的傅里叶变换。

                                      X(k) =\sum_{n=0}^{N-1}x_ne^{-j2\pi nk/N }                                                                                    公式1    

                                     F(w)=F[f(t)]=\int_{-\infty }^{\infty }f(t)e^{-jwt}dt                                                                  公式2                                                                                                          

首先我们对于离散傅里叶变换的公式进行转换推导。离散傅里叶变换实际可以表示为:

                                     X(k)=x_1 e^{-j2 \pi k/N}+x_2 e^{-j2 \pi k 2/N}+x_3 e^{-j2 \pi k 3/N}+\cdots x_n e^{-j2 \pi k 2k/N}             公式3

我们改变一种表示方式,离散傅里叶变换也可以表示为

                                     F(x)=A_0+A_1 x+A_2 x^2+A_3 x^3+\cdots +A_n x^n                                              公式4

对于这种公式,我们应该如何来进行求解呢。在这里我们不进行传统计算方式的介绍,直接按照fft的思想来进行解析推导。

首先,我们对于多项式进行分解,现在我们将该多项式分解为奇数次项和偶数次项的进行分类表示。而且为了表现的更清楚,我们先将该多项式的最高次项定为7,即有8个多项式进行累加,然后重新排列后的多项式为:

                                     F(x)=(A_0+A_2 x^2+A_4 x^4+A_6 x^6)+x(A_1+A_3 x^2+A_5 x^4+A_7 x^7)         公式5

为了表示的更清晰,我们分别给奇数次项和偶数次项重新起一个名字,即f(x)和g(x),这两个多项式依次表示为:

                                     f(x)=A_0+A_2 x^2+A_4 x^4+A_6 x^6                                                                    公式6

                                     g(x)=A_1+A_3 x^2+A_5 x^4+A_7 x^6                                                                    公式7

对该公式进行还原,我们又可以还原回原来的总公式。

                                     F(x)=f(x^2 )+xg(x^2)                                                                                      公式8

之前我们的思维一直停留在实数范畴内,现在我们引入一个复数域内的概念。我们叫它复根用ω 表示。如果w^k=1 ,那么我们称“ω 为1的k次复根”计做 w_{k}^{n} ,单位复根地表示,其中n就是一个序号数,我们把所有的负根按照复数域角度的大小逆时针排序从零开始编号。

如下图所示,即为一个四次复根的图形表示。

                                                                                         图 1 四次复根

根据该图我们可以推论,其实k次复根就相当于是将图中的圆周平均分成k个弧,弧与弧之间的端点就是k次复根。另外,从图中可以看出 w_{4}^{2} = -1 = i^2 = (w_{4}^{1})^2w_{4}^{0} 是这个圆与“Real”轴即实数轴正半轴的交点,所以无论k取多少,w_{k}^{0}  始终是1。我们只需要知道 w_{k}^{1},就能求出 w_{k}^{n},所以我们称 w_{k}^{1} 为“单位复根”。

在正常使用过程中,我们也能够w_{k}表示单位复根,w_{k}^{1} 表示的是“单位复根”的“1次方”也就是它本身,其他的就叫做k次单位复根的n次方。

关于单位复根,它还有一些特性,

  1. n次单位复根的值随指数变化而循环,w_{k}^{n+k} = w_{k}^{k}w_{k}^{n} = w_{k}^{n} 。
  2. 折半引理: w_{k}^{n} = w_{k/2}^{n/2},这个引理在fft的公式推导优化过程中会使用到,如图1中,如果将整个圆周划分为两个2个弧,则w_{2}^{1}的位置实际就是现在w_{4}^{2} 的位置,因此该引理成立。
  3. 消去定理:w_{k}^{n+k/2} = -w_{k}^{n},这个定理可以由上图中看出,复根转了半圈正好变成了-1。

现在我们将单位复根带入上述的公式中代替x的值,则原来的公式可以表示为:

                                     F(w_{k}^{n}) = f(w_{k}^{2n})+w_{k}g(w_{k}^{2n})                                                                              公式9

由于需要对n进行分类讨论,因为涉及到奇偶次项的运算,当0 <n <k/2 -1时 ,此时根据之前提到的复根的折半引理,我们可以将该公式推导为:

                                     F(w_{k}^{n}) = f(w_{k/2}^{n})+w_{k}g(w_{k/2}^{n})                                                                           公式10

当k/2<n +k/2<k -1时,上述的公式又可以变为

                                     F(w_{k}^{n+k/2}) = f(w_{k}^{2n+k})+w_{k}^{n+k/2}g(w_{k}^{2n+k})                                                        公式11

根据之前的消去定理以及折半定理,我们可以将公式优化为:

                                     F(w_{k}^{n+k/2}) = f(w_{k/2}^{n})-(w_{k/2}^{n})g(w_{k/2}^{n})                                                              公式12

所以,fft的计算公式可以总结为两个区域内的值表示,即0 <n <k/2 -1以及k/2<n +k/2<k -1的区间范围内所有Fx 的值都可以表示,另外,可以看出fx 以及 gx 就是 Fx 的一半,因此在程序计算过程中可以按照子问题的方式来递归求解。

以下即为根据上述推导处的最后两个公式来进行计算的fft代码表示。代码中涉及到的子函数均为复数间的计算。

void fft(complex *a,int n,int dft)//n表示我的多项位数
{
	int  i = 0, j = 0, k = 0;
	int step = 0;
	for(i=0;i<n;i++) if(i<rev[i]) Swap(a[i],a[rev[i]]);
	for(step=1;step<n;step<<=1)//模拟一个合并的过程
	{
		complex wn;
		Wn_i(n,1,&wn,dft);//计算当前单位复根
		for(j=0;j<n;j+=step<<1)
		{
			complex wnk;
			Wn_i(n,1,&wnk,dft);//计算当前单位复根
			for(k=j;k<j+step;k++)
			{//蝴蝶操作
				complex x=a[k];
				complex y;
				c_mul(a[k+step],wnk,&y);
				c_plus(x,y,&a[k]);
				c_sub(x,y,&a[k+step]);//后半个“step”中的ω一定和“前半个”中的成相反数
				c_mul(wnk,wn,&wnk);
			}
		}
	}
}

为了得到蝶形计算的参数序号,在进行fft计算之前先要进行参数重新排序,该函数的目的就是将每一位颠倒。如果暴力按位反转,总归不够优雅。所以,我们用一个类似DP动态规划的方法来实现这个功能。代码如下所示:

void get_rev(int bit)//bit表示二进制的位数
{  
    for(int i=0;i<(1<<bit);i++)//我么要对1~2^bit-1中的所有数做长度为bit的二进制翻转
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));//?!! SMG ?!!
}

根据dp的思想,每一个问题都可以由其子问题的解来进行求解。所以我们可以把一个二进制数看成两部分,它的前bit-1位是一部分,它的最后一位是一部分。全部bit位的数据求解看成是总问题,则前bit-1位可以看成是一个子问题。所以,要求解所有位数据的反转,我们可以直接利用前bit-1位数据的反转结果。因此,任意一个数的二进制反转就相当于是把它的最后一位当成首位,然后在后面接上它前bit-1位的二进制反转。而且在这个循环中我们能保证,在计算i的二进制反转之前,1 ~ i-1中的所有数的二进制反转都已经完成。 i的前bit-1位的数值其实就是i >>1的值,直接调用 i >>1的二进制反转的结果就相当于调用了i 的前bit-1位二进制反转的结果。

其实i >>1的反转与i的前bit-1位的反转是有一点出入的,因为我们的二进制反转始终以bit位为标准,所以i >>1会比i的前bit-1位多出一个前导零,而反转之后就会多出一个后缀零,所以i的前bit-1位的反转要去掉那个后缀零,此处我们通过将结果向右移位来实现,也就是rev[i>>1]>>1。

因此,我们只要把末尾乘上2^(bit-1)变成首位,此时该数据除了最高位有效之外,其余所有数位全都是向左移位产生的0,所以,再按位或(|)上rev[ i >>1]>>1就是我们要的答案了。

关于快速傅里叶变换,我的学习过程属于比较坎坷的一类,大学期间曾经学习过一段时间,但那时对于这些概念没有深入的学习,所以压根没有理解。即使后来再次遇到这些概念及公式也是一头雾水。现在由于工作过程中需要用到这些知识,所以便重新回来翻看。快速傅里叶变换作为工程上的一种工具,通过很巧妙的手段将公式重组简化,大大提高了多项式乘法以及离散傅里叶变换的计算速度。学习期间上网查阅过很多前辈总结的学习笔记,在此向各位提供学习心得的前辈表示衷心的感谢!

 

 

参考文献

https://blog.csdn.net/WADuan2/article/details/79529900

https://blog.csdn.net/tf18269639242/article/details/53024276

https://blog.csdn.net/f_zyj/article/details/76037583

https://wenku.baidu.com/view/5cacb2b8bd64783e09122b9a.html

https://blog.csdn.net/chenyujing1234/article/details/7419863

https://www.cnblogs.com/luoqingyu/p/5930181.html

https://blog.csdn.net/ggn_2015/article/details/68922404

https://blog.csdn.net/egean/article/details/53039248

https://blog.csdn.net/ggn_2015/article/details/68922404

https://blog.csdn.net/linwanglian1/article/details/56020221

https://www.cnblogs.com/RabbitHu/p/FFT.html

https://www.cnblogs.com/Lyush/articles/3219196.html

https://blog.csdn.net/zhaopeizhaopeipei/article/details/53908238

https://blog.csdn.net/shenziheng1/article/details/52891807

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

快速傅里叶变换 的相关文章

  • 三维视觉论文实战:DenseDepth2019--网络结构及demo

    目的 本篇博客的主要目的是记录测试DenseDepth的demo的过程 xff0c 包括 pytorch模型构建 和 keras模型参数转pytorch 两大部分 xff0c 当然最后还有一个实验模块 注明以下 xff0c 本篇博客为啥要构
  • GTAV:原始影像和深度图获取

    背景 GTAV是一个非常好的游戏 xff0c 目前也已经被广泛应用到深度学习之中了 本篇博客简单介绍一下如何采集数据 1 数据采集 1 代码修改 本篇博客的代码来源于GTAVisionExport 但是上述代码中 xff0c 存在些许问题
  • Blender2.8:Blender Python渲染降噪节点(Cycles)

    参考 https www bilibili com read cv9221189 背景 Blender的Cycles渲染引擎存在非常多的噪声 方法 一个比较好的思路是利用 Denoising Data 和降噪节点 参考文档里的是手动设置 x
  • Ubuntu:显存占用及处理

    问题 在进行深度学习时 xff0c 显存是一种非常宝贵的资源 但是即便在Ubuntu下 xff0c 各种各样的系统配置都会不自觉的占用一些显存 xff0c 导致深度学习难以为继 在本博客中 xff0c 主要搬运一些查询显存占用原因及处理方法
  • Jittor:Jittor三千问

    Jittor三千问 记录一下在使用Jittor时遇到的问题和对应的解决方案 xff0c 持续更新 非常感谢梁盾博士的回复 1 Jittor如何指定显卡 xff1f 在运行脚本时 xff0c 使用 CUDA VISIBLE DEVICES 6
  • 机器学习:补课目录

    补课目录 xff1a xff08 已经完成 xff09 吴恩达DeepLearning ai xff1a Deep Learning Specialization xff08 正在进行 xff09 林轩田 机器学习基石 xff08 正在进行
  • conda:离线环境安装

    Aanconda的离线环境安装的必要条件是有一台可以联网的电脑 在后文中 xff0c 分别称可以联网的电脑为On line xff0c 不可以联网的电脑为Off line 以下即为对应的操作步骤 1 On line 下载annconda安装
  • Ubuntu:pip install gdal

    方法 sudo apt get update 必须首先安装gdal的lib xff0c python只是针对该lib的调用 sudo apt install gdal bin libgdal dev pip安装的版本必须和gdal一致 pi
  • Git:使用笔记

    git局部配置 git config user name 34 username 34 git config user email 34 email 34 git带用户密码clone git clone https username pas
  • Pytorch:conda安装不同版本的cuda

    我不会是最后一个知道可以用conda安装不同版本的cuda的人吧 通常的pytorch安装流程是 xff1a 首先安装NVIDIA驱动 xff0c 然后安装对应版本的cuda和cudnn最后再安装cuda支持的pytorch版本 然而实际上
  • obsidian使用技巧

    背景 obsidian是一个非常牛逼的本地笔记工具 xff0c 极大的提高了本人的学习能力 xff0c 卷的更加厉害了 此处简要记录一下在使用过程中遇到问题和对应的解决方案 xff0c 至于具体的使用方法网上多的是就不介绍了 三方插件推荐
  • ubuntu:命令行查询文件(夹)大小

    背景 使用命令行查询文件文件 夹 大小 参考 https www cnblogs com zhengyiqun1992 p 11183819 html 使用方法 查看当前文件夹下文件大小 ll h 输出如下 xff0c 其中文件夹大小是错误
  • VSCode:remote-ssh多级跳转

    背景 vscode目前是非常流行的编程工具 xff0c 提供了大量的插件 xff0c 尤其是其中的remote ssh xff0c 能够提供远程ssh连接服务器 xff0c 居家办公两不误 然而比较麻烦的事情是 xff0c 通常服务器为了保
  • Jittor:Jittor1.3.1之离线安装

    背景 Jittor是一个非常牛逼的框架 xff0c 维护了大量的官方demo xff0c 非常容易上手 与其他方法相比 xff0c 采用了即时编译的流程 xff0c 因此在效率上往往更高 但是在使用Jittor的过程中 xff0c 也遇到了
  • 灵活的按键处理程序 FlexibleButton,C程序编写,无缝兼容任意的处理器,支持任意 OS 和 non-OS

    灵活的按键处理程序 FlexibleButton 前言概述获取测试DEMO 程序说明程序入口用户初始化代码事件处理代码 FlexibleButton 代码说明按键事件定义按键注册接口按键事件读取接口按键扫描接口 问题和建议 前言 正好工作中
  • http请求头header、请求体body、请求行介绍

    HttpServletRequest对象代表客户端的请求 当客户端通过http协议请求访问 服务器的时候 http请求头的所有信息都封装在这个对象中 xff0c 通过这个对象 xff0c 可以获取客户端请求的所有信息 http请求包含请求行
  • 理解字节序 大端字节序和小端字节序

    以下内容参考了 http www ruanyifeng com blog 2016 11 byte order html https blog csdn net yishengzhiai005 article details 3967252
  • opencvSharp 学习笔记(二)

    参考文章 xff1a https github com shimat opencvsharp samples tree master SamplesCS Samples 参考opencvsharp的官方sample xff0c 在vs201
  • C++局部对象的析构

    class A span class hljs keyword public span A Func span class hljs attribute span span class hljs attribute span A A spa
  • BIND中基数树的建立

    BIND9新引入了视图的概念 xff0c 简单的来讲就是能根据不同的来源IP来返回不同的数据 其中网段的存储 xff0c 网段的快速匹配都是用基数树来实现的 下面是BIND创建基数树的代码 BIND的IP地址结构 span class hl

随机推荐

  • HTTP协议解析及C/C++代码实现

    超文本传输协议 HTTP 是分布式 协作 超媒体信息系统的应用层协议 这是自 1990 年以来万维网数据通信的基础 HTTP 是一种通用且无状态的协议 xff0c 它可以用于其他目的 xff0c 也可以使用其请求方法 错误代码和标头的扩展
  • C语言发邮件(带账号密码认证),简单的libesmtp实例

    需要安装libesmtp开发环境 xff0c centos下可以用yum安装 yum install libesmtp devel 编译时加上 lesmtp选项 xff0c 账号密码等替换成自己的 gcc o mail mail c les
  • 怎样在Markdown中贴图片

    前言 Markdown真的是一个很优秀的文本标记语言 语法也很简单 熟悉之后基本上可以完全抛弃Word了 用来写文档 一些博客 再好不过了 可是Markdown还是有一个痛点 那就是不大好贴图片 贴图 怎么样在markdown中贴图就不多说
  • 【四】【vlc-android】播放控制交互与demux解复用层、媒体数据流拉取层的具体数据传递和控制流程源码分析

    1 VLC中有很多demux mux encoder decoder模块 xff0c 因此需要先了解这些模块的加载原理 xff0c 模块的加载原理基本一致 xff0c 因此举例分析MP4解复用模块如何加载完成的 xff0c 主要流程如下 x
  • vs2013 设置不显示输出窗口

    工具 选项 项目与解决方案 常规 取消 在生成开始时显示输出窗口 的勾选
  • @Param注解的用法解析

    实例一 64 Param注解单一属性 dao层示例 Public User selectUser 64 param userName String name 64 param userpassword String password xml
  • mybatis choose标签的使用

    有时候我们并不想应用所有的条件 xff0c 而只是想从多个选项中选择一个 而使用if标签时 xff0c 只要test中的表达式为 true xff0c 就会执行 if 标签中的条件 MyBatis 提供了 choose 元素 if标签是与
  • Socket长连接实现思路

    长连接的正确实现方式 1 不关闭流实现长连接 xff1f 流关闭了而不关闭Socket xff0c 还是无法达到长连接的效果的 xff0c 所以 xff0c 要长连接 xff0c 流必须不能关闭 xff01 那么 xff0c 是不是直接不关
  • com.jacob.com.ComFailException: VariantChangeType failed

    调用jacob组件出错 com jacob com ComFailException VariantChangeType failed 在C Windows System32 config systemprofile下创建文件夹Deskto
  • CRC8校验 java实现

    以下为CRC8的实现 span class hljs keyword package span server span class hljs javadoc CRC8相关计算 encode utf 8 span class hljs jav
  • Java list add方法和addAll方法效率

    结论是 在数据量较小时 add方法配合for循环遍历比addAll来得快 但是在大量数据时 addAll的方法的效率更高 list addAll 是浅拷贝 只是将内存中的地址进行了拷贝 指向了原先list的末尾做了拼接
  • STM32——USART1重映射

    前言 为了使不同器件封装的外设 IO 功能数量达到最优 xff0c 可以把一些复用功能重新映射到其他一些引脚上 STM32 中有很多内置外设的输入输出引脚都具有重映射 remap 的功能 我们知道每个内置外设都有若干个输入输出引脚 xff0
  • Pg数据库比较时间大小

    postgresql 比较两个时间差大于 N个小时 摘要 PG 中时间想减后为interval xff0c 比较两个时间大于某个小时或者分钟等可以直接通过interval来实现 example1 xff1a 判断两个时间差大于4个小时 se
  • import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; import java.util.Stac

    span class hljs keyword import span java util LinkedList span class hljs keyword import span java util Queue span class
  • 21-《电子入门趣谈》第四章_自己制作电路板-4.2洞洞板的介绍和经典案例使用教程

    好消息 xff1a 请在手机淘宝或闲鱼上搜索 电子入门趣谈 xff0c 有惊喜哦 我把全本电子入门趣谈的电子版 xff08 包括科技提升和理论升华部分 xff0c 共计50余万字 xff09 放到上面开始兜售啦 xff0c 如果您真的喜欢这
  • vlc-添加自定义的demuxer解复用插件----播放h264裸文件

    使用vlc3 0 6 在ubuntu 64bit上编译 xff0c vlc使用插件的方式组织对多种视频源的支持 xff0c 比如 avi mp4 mkv 等等 xff0c 这里想添加一个自己的demuxer xff0c 从一个h 264文件
  • 进程管理(五)--linux进程内核栈

    在进程创建时 xff0c 内核会为进程创建一系列数据结构 xff0c 其中最重要的就是上章学习的task struct结构 xff0c 它就是进程描述符 xff0c 表明进程在生命周期内的所有特征 同时 xff0c 内核为进程创建两个栈 x
  • [802.11]IEEE 802.11认证方式介绍

    一 802 11认证方式 802 11有开放系统认证 xff08 open system authentication xff09 和共享密钥认证 xff08 shared keyauthentication xff09 两种方式 1 1
  • 对‘std::xxx’未定义的引用

    出现一大串 对 std xxx 未定义的引用 的原因 xff1a 对于gcc后缀文件 xff0c 编译的时候可以用gcc g 43 43 xff0c 但是链接的时候要用g 43 43 xff0c 因为gcc和g 43 43 在编译的时候是相
  • 快速傅里叶变换

    FFT xff0c 即为快速傅氏变换 xff0c 是离散傅氏变换的快速算法 xff0c 它是根据离散傅氏变换的奇 偶 虚 实等特性 xff0c 对离散傅立叶变换的算法进行改进获得的 它对傅氏变换的理论并没有新的发现 xff0c 但是对于在计