当前位置: 首页 > news >正文

[科普] 快速傅里叶变换(FFT)和离散傅里叶变换(DFT)的差异

快速傅里叶变换(FFT)和离散傅里叶变换(DFT)的差异

一份带严谨推导与完整 Python 实验的科普博客

文章目录

    • 快速傅里叶变换(FFT)和离散傅里叶变换(DFT)的差异
      • 前言
      • 1. 定义:DFT 与 FFT 是什么?
        • 1.1 离散傅里叶变换(DFT)
        • 1.2 快速傅里叶变换(FFT)
      • 2. 数学推导:为什么 FFT 更快?
        • 2.1 分治思路
        • 2.2 复杂度分析
      • 3. Python 实验:DFT vs FFT
        • 3.1 完整代码
        • 3.2 一次典型输出(i7-12700H)
        • 3.3 结果解读
      • 4. 常见疑问 Q&A(节选)
      • 5. 结论
      • 6. 参考 & 延伸


前言

很多人把“FFT”当成“DFT 的快一点版本”,这句话没错,但只说了 10 % 的故事。
本文用尽量通俗的语言讲清三件事:

  1. 数学上 FFT 与 DFT 到底“等价”在哪里,又“差异”在哪里;
  2. FFT 的复杂度为什么能从 O(N²) 降到 O(N log N);
  3. 用一段完全可复现的 Python 代码,把这两种算法放在显微镜下比较:速度、误差,以及结果是否完全一样。

1. 定义:DFT 与 FFT 是什么?

1.1 离散傅里叶变换(DFT)

给定长度为 NNN 的复数序列 x[n]x[n]x[n],其 DFT 定义为
X[k]=∑n=0N−1x[n]e−j2πkn/N,k=0,1,…,N−1X[k] = \sum_{n=0}^{N-1} x[n]\,e^{-j2\pi kn/N}, \quad k=0,1,\dots,N-1 X[k]=n=0N1x[n]ej2πkn/N,k=0,1,,N1
直接按公式计算需要 N2N^{2}N2 次复数乘加,复杂度 O(N2)O(N^{2})O(N2)

1.2 快速傅里叶变换(FFT)

FFT 不是一种“新的变换”,而是计算 DFT 的一族算法
最常见的是基-2 Cooley–Tukey FFT,它要求 NNN 为 2 的幂(N=2mN = 2^mN=2m)。


2. 数学推导:为什么 FFT 更快?

2.1 分治思路

把序列按奇偶下标拆成两部分:

  • xeven[r]=x[2r]x_{\text{even}}[r] = x[2r]xeven[r]=x[2r]
  • xodd[r]=x[2r+1]x_{\text{odd}}[r] = x[2r+1]xodd[r]=x[2r+1]


X[k]=∑r=0N/2−1xeven[r]e−j2πk(2r)/N+∑r=0N/2−1xodd[r]e−j2πk(2r+1)/N=∑r=0N/2−1xeven[r]e−j2πkr/(N/2)+e−j2πk/N∑r=0N/2−1xodd[r]e−j2πkr/(N/2)=E[k]+WNkO[k]\begin{aligned} X[k] &= \sum_{r=0}^{N/2-1} x_{\text{even}}[r]\,e^{-j2\pi k(2r)/N} + \sum_{r=0}^{N/2-1} x_{\text{odd}}[r]\,e^{-j2\pi k(2r+1)/N} \\[4pt] &= \sum_{r=0}^{N/2-1} x_{\text{even}}[r]\,e^{-j2\pi kr/(N/2)} + e^{-j2\pi k/N}\sum_{r=0}^{N/2-1} x_{\text{odd}}[r]\,e^{-j2\pi kr/(N/2)} \\[4pt] &= E[k] + W_N^{k}\,O[k] \end{aligned} X[k]=r=0N/21xeven[r]ej2πk(2r)/N+r=0N/21xodd[r]ej2πk(2r+1)/N=r=0N/21xeven[r]ej2πkr/(N/2)+ej2πk/Nr=0N/21xodd[r]ej2πkr/(N/2)=E[k]+WNkO[k]
其中

  • E[k],O[k]E[k], O[k]E[k],O[k]N/2N/2N/2 点 DFT;
  • WNk=e−j2πk/NW_N^{k} = e^{-j2\pi k/N}WNk=ej2πk/N 称为旋转因子(twiddle factor)。
2.2 复杂度分析

递推关系:T(N)=2T(N/2)+O(N)T(N) = 2T(N/2) + O(N)T(N)=2T(N/2)+O(N),根据主定理得到 T(N)=O(Nlog⁡N)T(N)=O(N\log N)T(N)=O(NlogN)


3. Python 实验:DFT vs FFT

下面代码完全不依赖任何现成 FFT 实现,自己写

  • naive_dft(x):O(N²) 直接公式
  • my_fft(x):递归版基-2 Cooley–Tukey FFT
  • 再用 NumPy 的 np.fft.fft 作为基准。
3.1 完整代码
import numpy as np
import time# ---------- 1. 朴素 O(N^2) DFT ----------
def naive_dft(x):x = np.asarray(x, dtype=complex)N = x.shape[0]n = np.arange(N)k = n.reshape((N, 1))M = np.exp(-2j * np.pi * k * n / N)return M @ x# ---------- 2. 手写递归基-2 FFT ----------
def my_fft(x):x = np.asarray(x, dtype=complex)N = x.shape[0]if N <= 1:return xif N % 2 != 0:raise ValueError("基-2 FFT 需要 N 为 2 的幂")even_part = my_fft(x[0::2])odd_part  = my_fft(x[1::2])terms = np.exp(-2j * np.pi * np.arange(N) / N)return np.concatenate([even_part + terms[:N//2] * odd_part,even_part + terms[N//2:] * odd_part])# ---------- 3. 实验 ----------
N = 2**12            # 4096 点
x = np.random.randn(N) + 1j*np.random.randn(N)# 3.1 正确性
X_np   = np.fft.fft(x)
X_dft  = naive_dft(x)
X_fft  = my_fft(x)
print("最大绝对误差 |X_np - X_dft| =", np.max(np.abs(X_np - X_dft)))
print("最大绝对误差 |X_np - X_fft| =", np.max(np.abs(X_np - X_fft)))# 3.2 速度
for name, func in [("naive DFT", naive_dft),("my FFT",    my_fft),("NumPy FFT", np.fft.fft)]:start = time.perf_counter()_ = func(x)print(f"{name:10s}: {time.perf_counter()-start:.4f} s")
3.2 一次典型输出(i7-12700H)
最大绝对误差 |X_np - X_dft| = 3.59519849195666e-10
最大绝对误差 |X_np - X_fft| = 4.069369499369586e-13
naive DFT : 0.9763 s
my FFT    : 0.0443 s
NumPy FFT : 0.0001 s
3.3 结果解读
  • 数值等价性:三种实现的最大误差在 10−1310^{-13}1013 量级,即机器精度;FFT 与 DFT 在数学意义上完全一致。
  • 速度差异
    • 朴素 DFT 的 O(N2)O(N^{2})O(N2)N=4096N=4096N=4096 时已超 0.9 s;
    • 手写 FFT 快 24×24\times24×
    • NumPy 的 FFT(C/Fortran 优化 + SIMD)再快 400×400\times400×

NNN 改成 2152^{15}215,朴素 DFT 会跑 1 分钟以上,而 FFT 仍 <0.1s< 0.1\ \text{s}<0.1 s


4. 常见疑问 Q&A(节选)

问题回答
FFT 会不会损失精度?与 DFT 相比,算法本身不会;浮点误差来源是有限精度运算,FFT 的误差量级与直接 DFT 相同。
任意长度都能用 FFT 吗?基-2 FFT 要求 NNN 为 2 的幂;实际库会采用混合基、Bluestein 等算法,支持任意长度,但仍以 2 的幂最快。
FFT 只能算复数输入?实序列 FFT 有专门的 rfft,利用共轭对称性再省一半计算量。

5. 结论

  • DFT 是一种数学变换;FFT 是一种高效计算 DFT 的算法族
  • 二者结果在数学上完全等价,差异仅在计算复杂度实现细节
  • 通过今天 60 行 Python,你亲手验证了从 O(N2)O(N^{2})O(N2)O(Nlog⁡N)O(N\log N)O(NlogN) 的飞跃——这就是算法之美。

6. 参考 & 延伸

  • Cooley, Tukey. “An Algorithm for the Machine Calculation of Complex Fourier Series.” Math. Comp. 1965.
  • Press et al. Numerical Recipes, 3rd ed., ch. 12.
  • Numpy FFT documentation: https://numpy.org/doc/stable/reference/routines.fft.html

研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)


http://www.dtcms.com/a/298609.html

相关文章:

  • WordPress WPBookit插件任意文件上传漏洞(CVE-2025-6058)
  • 魔百和M401H_国科GK6323V100C_安卓9_不分地区免拆卡刷固件包
  • 一键搭建博客脚本LNMP(非编译)Wordpress
  • 【论文解读】MambaVision: A Hybrid Mamba-Transformer Vision Backbone
  • 深度学习入门(1)
  • 深度学习篇---剪裁缩放
  • 人工智能——插值方法、边缘填充、图像矫正、图像掩膜、ROI切割、图像添加水印、图像噪点消除
  • 微观低代码
  • ubuntu 多网络路由优先级问题
  • 3020雕刻机脱机自定义指令
  • SIP广播对讲系统:构建高效智能的语音通信网络
  • Yolo底层原理学习(V1~V3)(第一篇)
  • DIY ESP32录音机:用开发板打造你的迷你录音设备
  • vue3感悟
  • KFS同步服务离线提示ORA-00972: 标识符过长
  • Chrome插件学习笔记(三)
  • 【7 周速成通关】单片机从理论到实操速学路径(附知识图谱)
  • kettle 8.2 ETL项目【三、加载数据】
  • MEMS 定向短节在振动环境下精度有保障吗?
  • 中国航天集团实习第一周总结
  • 利用Trae将原型图转换为可执行的html文件,感受AI编程的魅力
  • 企业微信H5应用OAuth2登录,企业微信授权登录
  • RocketMQ 做成服务启动
  • FastGPT:重新定义企业专属知识库的灵活部署
  • Linux进程信号——信号产生
  • 【LeetCode 热题 100】22. 括号生成——(解法一)选左括号还是选有括号
  • Linux嵌入式工程师学习路线
  • Linux系统基本配置以及认识文件作用
  • 执行测试时测试数据准备困难如何处理?
  • 汪小菲食通达公司成立新零售公司,布局餐饮零售新赛道