通过矩阵从整体角度搞懂快速傅里叶变换原理
离散傅里叶变换公式
公式
f[k]=∑n=0N−1g[n]e−i(2π/N)kn,其中(0<=n<N)f[k]=\sum_{n=0}^{N-1}g[n]e^{-i(2\pi/N)kn}, 其中(0<=n<N) f[k]=n=0∑N−1g[n]e−i(2π/N)kn,其中(0<=n<N)
逆变换公式
g[n]=1N∑k=0N−1f[k]ei(2π/N)kn,其中(0<=k<N)g[n]=\frac{1}{N}\sum_{k=0}^{N-1}f[k]e^{i(2\pi/N)kn}, 其中(0<=k<N) g[n]=N1k=0∑N−1f[k]ei(2π/N)kn,其中(0<=k<N)
快速傅里叶变换
从以上公式看,如果直接按照公式来求离散傅里叶变换,其时间复杂度是O(N^2)
快速傅里叶变换就是一种能在O(n*log(n))时间复杂度内进行傅里叶变换及其逆变换的算法
离散傅里叶变换公式矩阵表示
令
G=[g[0]g[1]⋮g[n−1]]F=[f[0]f[1]⋮f[n−1]]G= \begin{bmatrix} g[0] \\ g[1] \\ \vdots \\ g[n-1] \end{bmatrix} \\\ \\\ F= \begin{bmatrix} f[0] \\ f[1] \\ \vdots \\ f[n-1] \end{bmatrix} \\\ G=g[0]g[1]⋮g[n−1] F=f[0]f[1]⋮f[n−1]
E[i]=[e−i(2π/N)∗0∗0e−i(2π/N)∗0∗1⋯e−i(2π/N)∗0∗(n−1)e−i(2π/N)∗1∗095⋯e−i(2π/N)∗1∗(n−1)⋮⋮⋱⋱⋮⋮⋱e−i(2π/N)∗(n−1)∗(n−1)]E[i]=\begin{bmatrix} e^{-i(2\pi/N)*0*0} & e^{-i(2\pi/N)*0*1} & \cdots & e^{-i(2\pi/N)*0*(n-1)} \\ e^{-i(2\pi/N)*1*0} & 95 & \cdots & e^{-i(2\pi/N)*1*(n-1)} \\ \vdots & \vdots & \ddots & \ddots \\ \vdots & \vdots & \ddots & e^{-i(2\pi/N)*(n-1)*(n-1)} \\ \end{bmatrix} E[i]=e−i(2π/N)∗0∗0e−i(2π/N)∗1∗0⋮⋮e−i(2π/N)∗0∗195⋮⋮⋯⋯⋱⋱e−i(2π/N)∗0∗(n−1)e−i(2π/N)∗1∗(n−1)⋱e−i(2π/N)∗(n−1)∗(n−1)
则离散傅里叶公式可改写为
F=E[i]∗GF=E[i]*G F=E[i]∗G
逆变换可改写为
G=1NE[−i]∗FG=\frac{1}{N}E[-i]*F G=N1E[−i]∗F
注意: E[i]里的 i 是一个变量,i 即正虚数单位,代表正变换,-i 代表逆变换,下同。
递归求解
令
E[i][n]=[e−i(2π/N)∗k∗0e−i(2π/N)∗k∗1…e−i(2π/N)∗k∗(n−1)]E[i][n]0n=[e−i(2π/N)∗k∗0e−i(2π/N)∗k∗2…e−i(2π/N)∗k∗(n−2)]E[i][n]1n=[e−i(2π/N)∗k∗1e−i(2π/N)∗k∗3…e−i(2π/N)∗k∗(n−1)]E[i][n]=\begin{bmatrix} e^{-i(2\pi/N)*k*0} & e^{-i(2\pi/N)*k*1} & \dots & e^{-i(2\pi/N)*k*(n-1)}\\ \end{bmatrix} \\\ \\\ E[i][n]^{0n}=\begin{bmatrix} e^{-i(2\pi/N)*k*0} & e^{-i(2\pi/N)*k*2} & \dots & e^{-i(2\pi/N)*k*(n-2)}\\ \end{bmatrix} \\\ \\\ E[i][n]^{1n}=\begin{bmatrix} e^{-i(2\pi/N)*k*1} & e^{-i(2\pi/N)*k*3} & \dots & e^{-i(2\pi/N)*k*(n-1)}\\ \end{bmatrix} \\ E[i][n]=[e−i(2π/N)∗k∗0e−i(2π/N)∗k∗1…e−i(2π/N)∗k∗(n−1)] E[i][n]0n=[e−i(2π/N)∗k∗0e−i(2π/N)∗k∗2…e−i(2π/N)∗k∗(n−2)] E[i][n]1n=[e−i(2π/N)∗k∗1e−i(2π/N)∗k∗3…e−i(2π/N)∗k∗(n−1)]
则
E[i]=[E[0][n]0nE[0][n]1n⋮⋮E[n−1][n]0nE[n−1][n]1n]\\ E[i]= \begin{bmatrix} E[0][n]^{0n} & E[0][n]^{1n} \\ \vdots & \vdots \\ E[n-1][n]^{0n} & E[n-1][n]^{1n} \\ \end{bmatrix} \\ E[i]=E[0][n]0n⋮E[n−1][n]0nE[0][n]1n⋮E[n−1][n]1n
将E[i]矩阵竖着再切一刀,平分两半,用数学语言描述就是,
令
E00(n/2)=[E[0][n]0n⋮E[n/2−1][n]0n]E01(n/2)=[E[0][n]1n⋮E[n/2−1][n]1n]E10(n/2)=[E[n/2][n]0n⋮E[n−1][n]0n]E11(n/2)=[E[n/2][n]1n⋮E[n−1][n]1n]\\ E_{00(n/2)}= \begin{bmatrix} E[0][n]^{0n} \\ \vdots \\ E[n/2-1][n]^{0n}\\ \end{bmatrix} \\\ \\\ E_{01(n/2)}= \begin{bmatrix} E[0][n]^{1n} \\ \vdots \\ E[n/2-1][n]^{1n}\\ \end{bmatrix} \\\ \\\ E_{10(n/2)}= \begin{bmatrix} E[n/2][n]^{0n} \\ \vdots \\ E[n-1][n]^{0n}\\ \end{bmatrix} \\\ \\\ E_{11(n/2)}= \begin{bmatrix} E[n/2][n]^{1n} \\ \vdots \\ E[n-1][n]^{1n}\\ \end{bmatrix} \\\\ E00(n/2)=E[0][n]0n⋮E[n/2−1][n]0n E01(n/2)=E[0][n]1n⋮E[n/2−1][n]1n E10(n/2)=E[n/2][n]0n⋮E[n−1][n]0n E11(n/2)=E[n/2][n]1n⋮E[n−1][n]1n
于是
E[i]=[E00(n/2)E01(n/2)E10(n/2)E11(n/2)]E[i]=\begin{bmatrix} E_{00(n/2)} & E_{01(n/2)} \\ E_{10(n/2)} & E_{11(n/2)} \\ \end{bmatrix} E[i]=[E00(n/2)E10(n/2)E01(n/2)E11(n/2)]
再令
w[k]=e−i(2π/N)∗kw[k]=e^{-i(2\pi/N)*k} w[k]=e−i(2π/N)∗k
W0(n/2)=[w[0]00…00w[1]0…000w[2]…0⋮⋮⋮⋮0000…w[n/2−1]]W1(n/2)=[w[n/2]00…00w[n/2+1]0…000w[n/2+2]…0⋮⋮⋮⋮0000…w[n−1]]W^{0(n/2)}=\begin{bmatrix} w[0] & 0 & 0 & \dots & 0 \\ 0 & w[1] & 0 & \dots & 0 \\ 0 & 0 & w[2] & \dots & 0 \\ \vdots & \vdots & \vdots & \vdots & 0 \\ 0 & 0 & 0 & \dots & w[n/2-1] \\ \end{bmatrix} \\\ \\\ W^{1(n/2)}=\begin{bmatrix} w[n/2] & 0 & 0 & \dots & 0 \\ 0 & w[n/2+1] & 0 & \dots & 0 \\ 0 & 0 & w[n/2+2] & \dots & 0 \\ \vdots & \vdots & \vdots & \vdots & 0 \\ 0 & 0 & 0 & \dots & w[n-1] \\ \end{bmatrix} W0(n/2)=w[0]00⋮00w[1]0⋮000w[2]⋮0………⋮…0000w[n/2−1] W1(n/2)=w[n/2]00⋮00w[n/2+1]0⋮000w[n/2+2]⋮0………⋮…0000w[n−1]
于是
E[i]=[E00(n/2)E01(n/2)E10(n/2)E11(n/2)]=[E00(n/2)W0(n/2)∗E00(n/2)E10(n/2)W1(n/2)∗E10(n/2)]=[E00(n/2)W0(n/2)∗E00(n/2)−E00(n/2)−W1(n/2)∗E00(n/2)]=[E00(n/2)W0(n/2)∗E00(n/2)−E00(n/2)W0(n/2)∗E00(n/2)]E[i] =\begin{bmatrix} E_{00(n/2)} & E_{01(n/2)} \\ E_{10(n/2)} & E_{11(n/2)} \\ \end{bmatrix} \\\ \\\ =\begin{bmatrix} E_{00(n/2)} & W^{0(n/2)}*E_{00(n/2)} \\ E_{10(n/2)} & W^{1(n/2)}*E_{10(n/2)} \\ \end{bmatrix} \\\ \\\ =\begin{bmatrix} E_{00(n/2)} & W^{0(n/2)}*E_{00(n/2)} \\ -E_{00(n/2)} & -W^{1(n/2)}*E_{00(n/2)} \\ \end{bmatrix} \\\ \\\ =\begin{bmatrix} E_{00(n/2)} & W^{0(n/2)}*E_{00(n/2)} \\ -E_{00(n/2)} & W^{0(n/2)}*E_{00(n/2)} \\ \end{bmatrix} E[i]=[E00(n/2)E10(n/2)E01(n/2)E11(n/2)] =[E00(n/2)E10(n/2)W0(n/2)∗E00(n/2)W1(n/2)∗E10(n/2)] =[E00(n/2)−E00(n/2)W0(n/2)∗E00(n/2)−W1(n/2)∗E00(n/2)] =[E00(n/2)−E00(n/2)W0(n/2)∗E00(n/2)W0(n/2)∗E00(n/2)]
上面这一步就是整个转化的核心了。
再往后,令
G0(n/2)=[g[0]g[2]⋮g[n−2]]G1(n/2)=[g[1]g[3]⋮g[n−1]]G^{0(n/2)}= \begin{bmatrix} g[0] \\ g[2] \\ \vdots \\ g[n-2] \end{bmatrix} \\\ \\\ G^{1(n/2)}= \begin{bmatrix} g[1] \\ g[3] \\ \vdots \\ g[n-1] \end{bmatrix} \\\ G0(n/2)=g[0]g[2]⋮g[n−2] G1(n/2)=g[1]g[3]⋮g[n−1]
则
F=GE[i]F=[E00(n/2)W0(n/2)∗E00(n/2)−E00(n/2)W0(n/2)∗E00(n/2)]∗[G0(n/2)G1(n/2)]F=GE[i] \\\ F = \begin{bmatrix} E_{00(n/2)} & W^{0(n/2)}*E_{00(n/2)} \\ -E_{00(n/2)} & W^{0(n/2)}*E_{00(n/2)} \\ \end{bmatrix} * \begin{bmatrix} G^{0(n/2)} \\ G^{1(n/2)} \\ \end{bmatrix} \\ \\\\ F=GE[i] F=[E00(n/2)−E00(n/2)W0(n/2)∗E00(n/2)W0(n/2)∗E00(n/2)]∗[G0(n/2)G1(n/2)]
到这一步,可以看到,我们已经成功将一个 n 阶的离散傅里叶变换降为了 n/2 阶,到这里就实现了O(n*logn)时间复杂度的快速傅里叶算法。
逆变换的和正变换的大同小异,就不在赘述了。
如果想要更简洁的形式,更深刻的理解离散傅里叶变换,可以进一步化简。
令
M=E00(n/2)∗G0(n/2)N=E00(n/2)∗G1(n/2)Z=W0(n/2)M=E_{00(n/2)}*G^{0(n/2)} \\\\ N=E_{00(n/2)}*G^{1(n/2)} \\\\ Z=W^{0(n/2)} \\\\ M=E00(n/2)∗G0(n/2)N=E00(n/2)∗G1(n/2)Z=W0(n/2)
则
F=[M+Z∗N−M+Z∗N]F = \begin{bmatrix} M+Z*N \\ -M+Z*N \\ \end{bmatrix} F=[M+Z∗N−M+Z∗N]
最后,上代码
public class FFT {/*** 傅里叶变换* @param x* @return*/public static Complex[] fft(Complex[] x){return fftTrans(x, true);}/*** 逆傅里叶变换* @param x* @return*/public static Complex[] ifft(Complex[] x) {int N = x.length;Complex[] y = fftTrans(x, false);for (int n = 0; n < N; n++) {y[n] = y[n].divides(N);}return y;}public static Complex[] fftTrans(Complex[] x, boolean dire) {int N = x.length;Complex[] y = new Complex[N];if (N == 1) {y[0] = x[0];return y;}Complex[] x0 = new Complex[N/2];for (int n = 0; n < N; n += 2) {x0[n/2] = x[n];}Complex[] X0 = fftTrans(x0, dire);Complex[] x1 = new Complex[N/2];for (int n = 1; n < N; n += 2) {x1[n/2] = x[n];}Complex[] X1 = fftTrans(x1, dire);for (int k = 0; k < N / 2; k++) {int ci = -1;if (!dire) {ci=-ci;}Complex wnk = getWnk(N, k, ci);Complex wnkX1 = wnk.multiply(X1[k]);y[k] = X0[k].plus(wnkX1);y[k+N/2] = X0[k].minus(wnkX1);}return y;}private static Complex getWnk(int N, int k, int n) {double T = 2 * Math.PI / N;double kth = T * k * n;Complex wk = new Complex(Math.cos(kth), Math.sin(kth));return wk;}}
相关文章:
通过矩阵从整体角度搞懂快速傅里叶变换原理
离散傅里叶变换公式 公式 f[k]∑n0N−1g[n]e−i(2π/N)kn,其中(0<n<N)f[k]\sum_{n0}^{N-1}g[n]e^{-i(2\pi/N)kn}, 其中(0<n<N) f[k]n0∑N−1g[n]e−i(2π/N)kn,其中(0<n<N) 逆变换公式 g[n]1N∑k0N−1f[k]ei(2π/N)kn,其中(0<k<N)g[n]\frac{1}{N}\…...
【C++从0到1】25、C++中嵌套使用循环
C从0到1全系列教程 1、实例代码 #include <iostream> // 包含头文件。 using namespace std; // 指定缺省的命名空间。int main() {// 超女分4个小组,每个小组有3名超女,在控制台显示每个超女的小组编号和组内编号。// 用一个循环…...
FastDFS与Nginx结合搭建文件服务器,并内网穿透实现公网访问
文章目录前言1. 本地搭建FastDFS文件系统1.1 环境安装1.2 安装libfastcommon1.3 安装FastDFS1.4 配置Tracker1.5 配置Storage1.6 测试上传下载1.7 与Nginx整合1.8 安装Nginx1.9 配置Nginx2. 局域网测试访问FastDFS3. 安装cpolar内网穿透4. 配置公网访问地址5. 固定公网地址5.1 …...
密集场景下的行人跟踪替代算法,头部跟踪算法 | CVPR 2021
一个不知名大学生,江湖人称菜狗 original author: Jacky LiEmail : 3435673055qq.com Time of completion:2023.4.8 Last edited: 2023.4.8 目录 摘要 主要内容 结果 这篇文章是CVPR 2021 的最新论文,文章的标题: 文章的主要内…...
Matlab与ROS(1/2)---服务端和客户端数据通信(五)
0. 简介 在前几讲我们讲了Matlab中的Message以及Topic的相关知识。而ROS主要支持的通信机制还有服务这一类。服务通过允许请求以及响应的通信方式,来给整个系统完成更紧密的耦合。服务客户端向服务服务器发送请求消息并等待响应。服务器将使用请求中的数据构造响应…...
数字化转型的避坑指南:细说数字化转型十二大坑
随着信息技术的快速发展,数字化转型已经成为许多企业发展的必经之路。然而,数字化转型过程中也存在许多坑,如果不谨慎处理,就可能导致企业陷入困境。本文将细说数字化转型的十二大坑,并提供相应的避坑指南。 1、不了解…...
pt05Encapsulationinherit
Encapsulation &inherit 封装继承 封装 向类外提供必要的功能,隐藏实现的细节, 代码可读性更高优势:简化编程,使用者不必了解具体的实现细节,只需要调用对外提供的功能。私有成员:作用:无需向类外提供…...
面向对象编程(基础)9:封装性(encapsulation)
目录 9.1 为什么需要封装? 而“高内聚,低耦合”的体现之一: 9.2 何为封装性? 9.3 Java如何实现数据封装 9.4 封装性的体现 9.4.1 成员变量/属性私有化 实现步骤: 成员变量封装的好处: 9.4.2 私有化…...
fate-serving-server增加取数逻辑并源码编译
1.什么是fate-serving-server? FATE-Serving 是一个高性能、工业化的联邦学习模型服务系统,专为生产环境而设计,主要用于在线推理。 2.fate-serving-server源码编译 下载fate-serving-serving项目(GitHub - FederatedAI/FATE-Serving: A scalable, h…...
循环队列、双端队列 C和C++
队列 目录 概念 实现方式 顺序队列 循环队列 队列的数组实现 用循环链表实现队列 STL 之 queue 实现队列 STL 之 dequeue 实现双端队列 概念 队列是一种特殊的线性表,它只允许在表的前端(称为队头,front)进行删除操作…...
正则表达式(语法+例子)
文章目录一、介绍二、语法1、匹配字符2、表示数量的字符3、边界字符4、其他字符5、转义字符三、例子1、邮箱2、用逗号分隔的数字集合1,23、允许一位小数4、20yy-mm-dd日期格式5、手机号6、匹配html、xml标签一、介绍 正则表达式(Regular Expression)&am…...
Properties和IO流集合的方法
方法名说明void load(InputStream inStream)从输入字节流读取属性列表(键和元素)void load(Reader reader)从输入字符流读取属性列表(键和元素对)void store(OutputStream out,String comments)将此属性列表(键和元素对…...
python 生成器、迭代器、动态新增属性及方法
目录 一、生成器 1、生成器定义 2、生成器存在的意义 3、创建生成器方式一(生成器表达式) 4. 创建生成器方式二(生成器函数) 1. 生成器函数 2. 生成器函数的工作原理 5. 总结 1. 什么是生成器 2. 生成器特点 二、迭代器…...
Java处理JSON
Java处理json有很多种方法,在这里总结一下。 1 Jackson Spring MVC 默认采用Jackson解析Json,出于最小依赖的考虑,也许Json解析第一选择就应该是Jackson。 1.1 引入的包 Jackson核心模块由三部分组成:jackson-core、jackson-a…...
58-Map和Set练习-LeetCode692前k个高频单词
题目 给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。 示例 1: 输入: words ["i", "love", …...
线程生命周期及五种状态
文章目录一、线程生命周期及五种状态1、New(初始化状态)2、Runnable(就绪状态)3、Running(运行状态)4、Blocked(阻塞状态)5、Terminated(终止状态)二、线程基本方法1、线程等待(wait)2、线程睡眠(sleep)3、…...
OBCP第八章 OB运维、监控与异常处理-灾难恢复
灾难恢复是指当数据库中的数据在被有意或无意破坏后复原数据库所需要执行的活动 回收站:回收站在原理上说就是一个数据字典表,放置用户删除的数据库对象信息。用户删除的东西被放入回收站后,其实仍然占据着物理空间,除非您手动进…...
亚马逊云科技Serverless Data:数字经济下的创新动能
Serverless时代已经到来!企业的技术架构,总是伴随着不断增长的数据与日趋复杂的业务持续演进。如何通过构建更易用的技术架构来聚焦在业务本身,而不必在底层基础设施的管理上投入过多的精力,是数据驱动型企业需要思考的重要议题。…...
【Ruby学习笔记】15.Ruby 异常
Ruby 异常 异常和执行总是被联系在一起。如果您打开一个不存在的文件,且没有恰当地处理这种情况,那么您的程序则被认为是低质量的。 如果异常发生,则程序停止。异常用于处理各种类型的错误,这些错误可能在程序执行期间发生&…...
聊聊MySQL主从延迟
文章目录 MySQL 的高可用是如何实现的呢?二、什么是主备延迟?三、主备延迟常见原因1、备库机器配置差2、备库干私活3、大事务四、主库不可用,主备切换有哪些策略?1、可靠优先2、可用优先实验一实验二3、结论MySQL 的高可用是如何实现的呢? 高可用性(high availability,缩…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
