🌊 快速傅里叶变换 (FFT)

将信号从时域转换到频域,O(n log n) 高效计算,数字信号处理的核心算法

📊 DFT

离散傅里叶变换,将离散序列转换为频域表示。计算复杂度 O(n²)。

FFT

快速傅里叶变换,利用对称性加速 DFT。计算复杂度 O(n log n)。

🦋 蝶形运算

FFT 的基本计算单元,完成两个复数的加权加减运算。

🔄 逆 FFT

将频域信号转换回时域,实现信号的完整变换循环。

💡 FFT 核心思想
🎯 分治策略

将长序列 DFT 分解为短序列 DFT 计算

🔄 旋转因子

利用周期性和对称性减少重复计算

🦋 蝶形运算

基本计算单元,高效并行化

📈 时频转换

时域信号 ↔ 频域频谱

DFT vs FFT 性能对比

计算量对比

直接计算 DFT 需要 O(n²) 次复数乘法,而 FFT 仅需 O(n log n) 次。

10⁶ DFT
1000 点
10⁴ FFT
1000 点
10¹² DFT
100 万点
2×10⁷ FFT
100 万点
方法 时间复杂度 1000 点计算量 100 万点计算量 实时处理
直接 DFT O(n²) 100 万次 10¹²次 ❌ 不可能
FFT O(n log n) 约 1 万次 约 2000 万次 ✅ 可实时

📊 1. 离散傅里叶变换 (DFT)

DFT 定义

对于长度为 N 的离散序列 x[n],其 DFT 定义为:

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]
DFT 复杂度
O(n²)
FFT 复杂度
O(n log n)
加速比
n/log n

🦋 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)
  • 频域滤波
  • 图像增强
  • 模板匹配

🔢 数值计算

  • 多项式乘法
  • 大数乘法
  • 卷积计算
  • 微分方程求解
🎯 FFT 变体选择
  • 基 2 DIT:最常用,序列长度为 2 的幂
  • 基 2 DIF:频率抽取,输出需要重排
  • 基 4 FFT:更高基,减少级数
  • 混合基:任意长度,更灵活
  • Chirp-Z:任意长度,非 2 的幂
  • 实数 FFT:输入为实数,效率更高