🌊 快速傅里叶变换 (FFT)
将信号从时域转换到频域,O(n log n) 高效计算,数字信号处理的核心算法
📊 DFT
离散傅里叶变换,将离散序列转换为频域表示。计算复杂度 O(n²)。
⚡ FFT
快速傅里叶变换,利用对称性加速 DFT。计算复杂度 O(n log n)。
🦋 蝶形运算
FFT 的基本计算单元,完成两个复数的加权加减运算。
🔄 逆 FFT
将频域信号转换回时域,实现信号的完整变换循环。
将长序列 DFT 分解为短序列 DFT 计算
利用周期性和对称性减少重复计算
基本计算单元,高效并行化
时域信号 ↔ 频域频谱
⚡ DFT vs FFT 性能对比
计算量对比
直接计算 DFT 需要 O(n²) 次复数乘法,而 FFT 仅需 O(n log n) 次。
| 方法 | 时间复杂度 | 1000 点计算量 | 100 万点计算量 | 实时处理 |
|---|---|---|---|---|
| 直接 DFT | O(n²) | 100 万次 | 10¹²次 | ❌ 不可能 |
| FFT | O(n log n) | 约 1 万次 | 约 2000 万次 | ✅ 可实时 |
📊 1. 离散傅里叶变换 (DFT)
DFT 定义
对于长度为 N 的离散序列 x[n],其 DFT 定义为:
X[k] = Σ_{n=0}^{N-1} x[n] · e^(-j2πkn/N), k = 0, 1, ..., N-1
旋转因子:
W_N = e^(-j2π/N) = cos(2π/N) - j·sin(2π/N)
简化形式:
X[k] = Σ_{n=0}^{N-1} x[n] · W_N^{kn}
旋转因子的性质
| 性质 | 公式 | 说明 |
|---|---|---|
| 周期性 | W_N^{k+N} = W_N^k | 周期为 N |
| 对称性 | W_N^{k+N/2} = -W_N^k | 共轭对称 |
| 可约性 | W_N^{kn} = W_{N/d}^{k'n} | 因子分解 |
| 逆变换 | (W_N^{kn})* = W_N^{-kn} | 共轭关系 |
⚡ 2. FFT 原理:分治策略
基 2 时间抽取 (DIT) FFT
假设序列长度 N 是 2 的幂(N=2^M),将输入序列按奇偶分组:
X[k] = Σ_{n=0}^{N-1} x[n] · W_N^{kn}
= Σ_{r=0}^{N/2-1} x[2r] · W_N^{k(2r)} + Σ_{r=0}^{N/2-1} x[2r+1] · W_N^{k(2r+1)}
= Σ_{r=0}^{N/2-1} x[2r] · W_{N/2}^{kr} + W_N^k · Σ_{r=0}^{N/2-1} x[2r+1] · W_{N/2}^{kr}
完整推导:
设偶序列 E[k] = DFT{x[2r]},奇序列 O[k] = DFT{x[2r+1]}
X[k] = E[k] + W_N^k · O[k]
X[k + N/2] = E[k] - W_N^k · O[k]
🦋 3. 蝶形运算
蝶形单元
蝶形运算是 FFT 的基本计算单元,一次蝶形运算完成两个复数的加权和计算。
🦋 蝶形运算图示
A
B
A' = A + W·B
B' = A - W·B
A + W·B
A - W·B
8 点 FFT 蝶形图(3 级运算):
第 1 级 第 2 级 第 3 级
x[0] ────●────────────────────────●────────────────────────●──────── X[0]
│ \ / │ / │
│ \ W₈⁰ / │ W₄⁰ / │
x[4] ────●───\──────────●──────●───────\──────●──────●──────── X[4]
│ \ / │ │ \ / │ │
│ \ W₈² / │ │ W₄¹ \/ │ │
x[2] ────●─────\──●──────●─────●──────────/ \───●─────●──────── X[2]
│ / \ │ │ / \ │ │
│ / W₈¹ \ │ │ W₄⁰ / \ │ │
x[6] ────●─/───────────\──●─────●─────/───────────\●─────●──────── X[6]
各级旋转因子
| 级数 | 旋转因子 | 蝶形跨度 | 蝶形数量 |
|---|---|---|---|
| 第 1 级 | W₈^0, W₈^2 | 1 | 4 |
| 第 2 级 | W₄^0, W₄^1 | 2 | 4 |
| 第 3 级 | W₂^0, W₂^1 | 4 | 4 |
💻 4. 代码实现
Python 实现基 2 DIT FFT
import numpy as np
import cmath
def fft(x):
"""快速傅里叶变换 - 基 2 时间抽取算法(递归版)"""
n = len(x)
if n == 1:
return x
if n % 2 != 0:
raise ValueError("序列长度必须是 2 的幂")
# 偶序列和奇序列
even = fft(x[::2])
odd = fft(x[1::2])
# 合并结果
X = [0j] * n
for k in range(n // 2):
angle = -2 * np.pi * k / n
w = complex(np.cos(angle), np.sin(angle))
X[k] = even[k] + w * odd[k]
X[k + n // 2] = even[k] - w * odd[k]
return X
def ifft(X):
"""快速逆傅里叶变换"""
n = len(X)
x_conj = [complex(c.real, -c.imag) for c in X]
x_fft = fft(x_conj)
return [complex(c.real / n, -c.imag / n) for c in x_fft]
def fft_iterative(x):
"""迭代版 FFT - 使用位逆序重排"""
n = len(x)
X = x.copy()
# 位逆序重排
j = 0
for i in range(1, n):
bit = n >> 1
while j & bit:
j ^= bit
bit >>= 1
j ^= bit
if i < j:
X[i], X[j] = X[j], X[i]
# 逐级蝶形运算
length = 2
while length <= n:
half = length // 2
for i in range(0, n, length):
for j in range(half):
angle = -2 * np.pi * j / length
w = complex(np.cos(angle), np.sin(angle))
u = X[i + j]
v = w * X[i + j + half]
X[i + j] = u + v
X[i + j + half] = u - v
length *= 2
return X
# 测试:50Hz + 120Hz 正弦波
fs = 1000 # 采样频率
t = np.arange(0, 1, 1/fs)
signal = np.sin(2 * np.pi * 50 * t) + 0.5 * np.sin(2 * np.pi * 120 * t)
X = fft(signal[:256])
freq = np.fft.fftfreq(len(signal), 1/fs)
print(f"信号长度:{len(signal)}")
print(f"频率分辨率:{fs / len(signal):.2f} Hz")
peak_freq = freq[np.argmax(np.abs(X[1:len(X)//2])) + 1]
print(f"峰值频率:{peak_freq:.1f} Hz")class FFT {
constructor() {
this.sinTable = [];
this.cosTable = [];
}
fft(x) {
const n = x.length;
if (n === 1) return x;
if (n % 2 !== 0) throw new Error("序列长度必须是 2 的幂");
const even = this.fft(x.filter((_, i) => i % 2 === 0));
const odd = this.fft(x.filter((_, i) => i % 2 === 1));
const X = new Array(n).fill(0);
for (let k = 0; k < n / 2; k++) {
const angle = -2 * Math.PI * k / n;
const w = {
real: Math.cos(angle),
imag: Math.sin(angle)
};
const oddTerm = {
real: w.real * odd[k].real - w.imag * odd[k].imag,
imag: w.real * odd[k].imag + w.imag * odd[k].real
};
X[k] = {
real: even[k].real + oddTerm.real,
imag: even[k].imag + oddTerm.imag
};
X[k + n / 2] = {
real: even[k].real - oddTerm.real,
imag: even[k].imag - oddTerm.imag
};
}
return X;
}
fftIterative(x) {
const n = x.length;
if ((n & (n - 1)) !== 0) throw new Error("序列长度必须是 2 的幂");
const X = x.map(c => ({ real: c.real !== undefined ? c.real : c, imag: 0 }));
// 位逆序重排
let j = 0;
for (let i = 1; i < n; i++) {
let bit = n >> 1;
while (j & bit) {
j ^= bit;
bit >>= 1;
}
j ^= bit;
if (i < j) {
[X[i], X[j]] = [X[j], X[i]];
}
}
// 逐级蝶形运算
for (let length = 2; length <= n; length *= 2) {
const half = length / 2;
for (let i = 0; i < n; i += length) {
for (let j = 0; j < half; j++) {
const angle = -2 * Math.PI * j / length;
const w = {
real: Math.cos(angle),
imag: Math.sin(angle)
};
const u = X[i + j];
const v = {
real: w.real * X[i + j + half].real - w.imag * X[i + j + half].imag,
imag: w.real * X[i + j + half].imag + w.imag * X[i + j + half].real
};
X[i + j] = { real: u.real + v.real, imag: u.imag + v.imag };
X[i + j + half] = { real: u.real - v.real, imag: u.imag - v.imag };
}
}
}
return X;
}
magnitudeSpectrum(X) {
return X.map(c => Math.sqrt(c.real * c.real + c.imag * c.imag));
}
}
// 测试
const fft = new FFT();
const signal = [];
for (let n = 0; n < 256; n++) {
const t = n / 1000;
signal.push({
real: Math.sin(2 * Math.PI * 50 * t) + 0.5 * Math.sin(2 * Math.PI * 120 * t),
imag: 0
});
}
const X = fft.fftIterative(signal);
const mags = fft.magnitudeSpectrum(X);
console.log("峰值频率:", mags.indexOf(Math.max(...mags.slice(1, 128))) * 1000 / 256, "Hz");package main
import (
"fmt"
"math"
)
type Complex struct {
real, imag float64
}
func (c Complex) Add(o Complex) Complex {
return Complex{c.real + o.real, c.imag + o.imag}
}
func (c Complex) Sub(o Complex) Complex {
return Complex{c.real - o.real, c.imag - o.imag}
}
func (c Complex) Mul(o Complex) Complex {
return Complex{
c.real*o.real - c.imag*o.imag,
c.real*o.imag + c.imag*o.real,
}
}
func FFT(x []Complex) []Complex {
n := len(x)
if n == 1 {
return x
}
var even, odd []Complex
for i := 0; i < n; i++ {
if i%2 == 0 {
even = append(even, x[i])
} else {
odd = append(odd, x[i])
}
}
evenFFT := FFT(even)
oddFFT := FFT(odd)
X := make([]Complex, n)
for k := 0; k < n/2; k++ {
angle := -2 * math.Pi * float64(k) / float64(n)
w := Complex{math.Cos(angle), math.Sin(angle)}
X[k] = evenFFT[k].Add(w.Mul(oddFFT[k]))
X[k+n/2] = evenFFT[k].Sub(w.Mul(oddFFT[k]))
}
return X
}
func main() {
n := 256
var signal []Complex
for i := 0; i < n; i++ {
t := float64(i) / 1000.0
val := math.Sin(2*math.Pi*50*t) + 0.5*math.Sin(2*math.Pi*120*t)
signal = append(signal, Complex{val, 0})
}
X := FFT(signal)
var maxMag float64
var peakIdx int
for i := 1; i < n/2; i++ {
mag := math.Sqrt(X[i].real*X[i].real + X[i].imag*X[i].imag)
if mag > maxMag {
maxMag = mag
peakIdx = i
}
}
freqRes := 1000.0 / float64(n)
fmt.Printf("频率分辨率:%.2f Hz\n", freqRes)
fmt.Printf("峰值频率:%.1f Hz\n", float64(peakIdx)*freqRes)
}🌐 5. 应用场景
📡 信号处理
- 频谱分析
- 滤波处理
- 信号检测
- 特征提取
📶 通信系统
- OFDM 调制 (LTE/5G/WiFi)
- QAM/PSK 调制解调
- 信道估计
- 滤波器组
🖼️ 图像处理
- JPEG 压缩 (DCT)
- 频域滤波
- 图像增强
- 模板匹配
🔢 数值计算
- 多项式乘法
- 大数乘法
- 卷积计算
- 微分方程求解
- 基 2 DIT:最常用,序列长度为 2 的幂
- 基 2 DIF:频率抽取,输出需要重排
- 基 4 FFT:更高基,减少级数
- 混合基:任意长度,更灵活
- Chirp-Z:任意长度,非 2 的幂
- 实数 FFT:输入为实数,效率更高