MINIBLOG

Blog Note Tags Links About
Home Search
May 11, 2026
miniyuan

插值法(二维插值,快速 Fourier 变换)


二维插值

问题定义

给定:二元函数 u(x,y)u(x, y)u(x,y) 在网格节点处的离散值

ui,j=u(xi,yj),i=0,1,⋯ ,n,  j=0,1,⋯ ,mu_{i,j} = u(x_i, y_j), \quad i = 0,1,\cdots,n,\; j = 0,1,\cdots,mui,j​=u(xi​,yj​),i=0,1,⋯,n,j=0,1,⋯,m

求:关于 xxx 为 nnn 次、yyy 为 mmm 次的二元多项式

Pn,m(x,y)=∑i=0n∑j=0maij xiyjP_{n,m}(x,y) = \sum_{i=0}^{n}\sum_{j=0}^{m} a_{ij}\, x^i y^jPn,m​(x,y)=i=0∑n​j=0∑m​aij​xiyj

满足插值条件:

Pn,m(xi,yj)=ui,j=u(xi,yj),i=0,⋯ ,n,  j=0,⋯ ,mP_{n,m}(x_i, y_j) = u_{i,j} = u(x_i, y_j), \quad i=0,\cdots,n,\; j=0,\cdots,mPn,m​(xi​,yj​)=ui,j​=u(xi​,yj​),i=0,⋯,n,j=0,⋯,m

其中:

  • Pn,m(x,y)P_{n,m}(x,y)Pn,m​(x,y) 称为插值多项式
  • (xi,yj)(x_i, y_j)(xi​,yj​) 称为插值节点

二维 Lagrange 插值

一维 Lagrange 插值回顾:

φn(x)=∑i=0nyi li(x),li(xj)={1,i=j0,i≠j\varphi_n(x) = \sum_{i=0}^{n} y_i\, l_i(x), \quad l_i(x_j) = \begin{cases}1, & i=j \\ 0, & i\neq j\end{cases}φn​(x)=i=0∑n​yi​li​(x),li​(xj​)={1,0,​i=ji=j​

二维推广:构造插值形式

Pn,m(x,y)=∑i=0n∑j=0mui,j li,j(x,y),li,j(xs,yt)={1,i=s,  j=t0,i≠s  or  j≠tP_{n,m}(x,y) = \sum_{i=0}^{n}\sum_{j=0}^{m} u_{i,j}\, l_{i,j}(x,y), \quad l_{i,j}(x_s, y_t) = \begin{cases}1, & i=s,\; j=t \\ 0, & i\neq s \;\text{or}\; j\neq t\end{cases}Pn,m​(x,y)=i=0∑n​j=0∑m​ui,j​li,j​(x,y),li,j​(xs​,yt​)={1,0,​i=s,j=ti=sorj=t​

分离变量法构造基函数:

li,j(x,y)=li(x) l~j(y)l_{i,j}(x,y) = l_i(x)\,\tilde{l}_j(y)li,j​(x,y)=li​(x)l~j​(y)

其中 li(x)l_i(x)li​(x) 与 l~j(y)\tilde{l}_j(y)l~j​(y) 分别为基于以下一维节点的插值基函数:

Δx:a=x0<x1<⋯<xn=b,Δy:c=y0<y1<⋯<ym=d\Delta_x: a = x_0 < x_1 < \cdots < x_n = b, \quad \Delta_y: c = y_0 < y_1 < \cdots < y_m = dΔx​:a=x0​<x1​<⋯<xn​=b,Δy​:c=y0​<y1​<⋯<ym​=d

插值区域:

D={(x,y)∣a≤x≤b,  c≤y≤d}D = \{(x,y) \mid a \le x \le b,\; c \le y \le d\}D={(x,y)∣a≤x≤b,c≤y≤d}

剖分节点集合:

Δ=Δx×Δy={(xi,yj)∣0≤i≤n,  0≤j≤m}\Delta = \Delta_x \times \Delta_y = \{(x_i, y_j) \mid 0 \le i \le n,\; 0 \le j \le m\}Δ=Δx​×Δy​={(xi​,yj​)∣0≤i≤n,0≤j≤m}

注:二维 Lagrange 插值要求节点排列在矩形网格上,对散点数据不直接适用。

分片双线性插值

问题:高次 Lagrange 多项式会引发 Runge 现象,二维情形同样不能幸免。

改进方法:采用分片双线性插值,是一维分段线性插值的直接推广。

P1,1(x,y)=∑i=0n∑j=0mui,j φi,j(x,y),φi,j(x,y)=li(x) l~j(y)P_{1,1}(x,y) = \sum_{i=0}^{n}\sum_{j=0}^{m} u_{i,j}\, \varphi_{i,j}(x,y), \quad \varphi_{i,j}(x,y) = l_i(x)\,\tilde{l}_j(y)P1,1​(x,y)=i=0∑n​j=0∑m​ui,j​φi,j​(x,y),φi,j​(x,y)=li​(x)l~j​(y)

其中 li(x)l_i(x)li​(x)、l~j(y)\tilde{l}_j(y)l~j​(y) 为分段线性基函数。

进一步改进:分片双三次 Hermite 插值,也即在节点处同时插值函数值和偏导数,改善导数连续性。

方法次数优点缺点
全局 Lagrangen×mn \times mn×m精确过所有节点高次时 Runge 现象严重
分片双线性1×11 \times 11×1简单、稳定节点处导数不连续
分片双三次 Hermite3×33 \times 33×3导数连续,光滑需提供偏导数数据

DFT

问题背景

问题定义:设 f(x)f(x)f(x) 在 [0,2π][0, 2\pi][0,2π] 上 N+1N+1N+1 个等距节点

xl=2πlN,l=0,1,⋯ ,Nx_l = \frac{2\pi l}{N}, \quad l = 0, 1, \cdots, Nxl​=N2πl​,l=0,1,⋯,N

处的值为 f(xl)≡flf(x_l) \equiv f_lf(xl​)≡fl​,且满足周期性条件 f0=fNf_0 = f_Nf0​=fN​,求

P(x)=∑k=0N−1ck eikxP(x) = \sum_{k=0}^{N-1} c_k\, e^{\mathrm{i}kx}P(x)=k=0∑N−1​ck​eikx

使得以下插值条件成立:

P(xl)=f(xl)=fl,l=0,1,⋯ ,NP(x_l) = f(x_l) = f_l, \quad l = 0,1,\cdots,NP(xl​)=f(xl​)=fl​,l=0,1,⋯,N

注:采用指数形式,自动满足周期性。

DFT 公式

记 NNN 次单位根:

ωN=ei2πN\omega_N = e^{\mathrm{i}\frac{2\pi}{N}}ωN​=eiN2π​

则 eik2lπN=ωNkle^{\mathrm{i}k\frac{2l\pi}{N}} = \omega_N^{kl}eikN2lπ​=ωNkl​。

将插值条件代入,得线性方程组:

A c=f\mathbf{A}\,\boldsymbol{c} = \boldsymbol{f}Ac=f

其中矩阵 A\mathbf{A}A 为 Vandermonde 型复数矩阵:

A=[111⋯11ωNωN2⋯ωNN−1⋮⋮⋮⋱⋮1ωNN−1ωN2(N−1)⋯ωN(N−1)(N−1)]=[a0a1⋯aN−1]\mathbf{A} = \begin{bmatrix} 1 & 1 & 1 & \cdots & 1 \\ 1 & \omega_N & \omega_N^{2} & \cdots & \omega_N^{N-1} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & \omega_N^{N-1} & \omega_N^{2(N-1)} & \cdots & \omega_N^{(N-1)(N-1)} \end{bmatrix} = \begin{bmatrix} \boldsymbol{a}_0 & \boldsymbol{a}_1 & \cdots & \boldsymbol{a}_{N-1} \end{bmatrix}A=​11⋮1​1ωN​⋮ωNN−1​​1ωN2​⋮ωN2(N−1)​​⋯⋯⋱⋯​1ωNN−1​⋮ωN(N−1)(N−1)​​​=[a0​​a1​​⋯​aN−1​​] c=[c0c1⋯cN−1]T,f=[f0f1⋯fN−1]T\boldsymbol{c} = \begin{bmatrix}c_0 & c_1 & \cdots & c_{N-1}\end{bmatrix}^{\mathrm{T}}, \quad \boldsymbol{f} = \begin{bmatrix}f_0 & f_1 & \cdots & f_{N-1}\end{bmatrix}^{\mathrm{T}}c=[c0​​c1​​⋯​cN−1​​]T,f=[f0​​f1​​⋯​fN−1​​]T

显然有以下列向量的正交性:

(aj, ak)≡∑l=0N−1ωNjl ωNkl‾=∑l=0N−1ωN(j−k)l={N,j=k0,j≠k(\boldsymbol{a}_j,\, \boldsymbol{a}_k) \equiv \sum_{l=0}^{N-1} \omega_N^{jl}\,\overline{\omega_N^{kl}} = \sum_{l=0}^{N-1} \omega_N^{(j-k)l} = \begin{cases} N, & j=k \\ 0, & j\neq k \end{cases}(aj​,ak​)≡l=0∑N−1​ωNjl​ωNkl​​=l=0∑N−1​ωN(j−k)l​={N,0,​j=kj=k​

由正交性,用 aˉkT\bar{\boldsymbol{a}}_k^{\mathrm{T}}aˉkT​ 左乘方程组两边,立即得到 DFT 正变换公式:

ck=1N∑l=0N−1fl ωN−kl,k=0,1,⋯ ,N−1c_k = \frac{1}{N}\sum_{l=0}^{N-1} f_l\, \omega_N^{-kl}, \quad k=0,1,\cdots,N-1ck​=N1​l=0∑N−1​fl​ωN−kl​,k=0,1,⋯,N−1

由矩阵方程即得 DFT 反变换公式:

fl=∑k=0N−1ck ωNkl,l=0,1,⋯ ,N−1f_l = \sum_{k=0}^{N-1} c_k\, \omega_N^{kl}, \quad l=0,1,\cdots,N-1fl​=k=0∑N−1​ck​ωNkl​,l=0,1,⋯,N−1

算法实现

DFT 正变换与反变换时间复杂度均为 O(N2)\mathcal{O}(N^2)O(N2)。

import numpy as np

def dft_naive(f):
    """朴素 DFT,时间复杂度 O(N^2)"""
    N = len(f)
    c = np.zeros(N, dtype=complex)
    for k in range(N):
        for l in range(N):
            c[k] += f[l] * np.exp(-1j * k * 2 * np.pi * l / N)
        c[k] /= N
    return c

def idft_naive(c):
    """朴素 IDFT,时间复杂度 O(N^2)"""
    N = len(c)
    f = np.zeros(N, dtype=complex)
    for l in range(N):
        for k in range(N):
            f[l] += c[k] * np.exp(1j * k * 2 * np.pi * l / N)
    return f

FFT

核心思想

基本策略:

将 NNN 个点的 DFT 问题分解为 2 个 N/2N/2N/2 个点的 DFT 问题,重复此过程,直至变成 N/2N/2N/2 个 2 点 DFT 问题。

前提:N=2mN = 2^mN=2m,也即 NNN 为 2 的整数次幂。

定义单位根:

ωN=e−i2πN\omega_N = e^{-\mathrm{i}\frac{2\pi}{N}}ωN​=e−iN2π​

重写 DFT(令 al≡fl/Na_l \equiv f_l/Nal​≡fl​/N):

ck=1N∑l=0N−1fl e−ik2lπN=∑l=0N−1al ωNkl,k=0,1,⋯ ,N−1c_k = \frac{1}{N}\sum_{l=0}^{N-1} f_l\, e^{-\mathrm{i}k\frac{2l\pi}{N}} = \sum_{l=0}^{N-1} a_l\, \omega_N^{kl}, \quad k=0,1,\cdots,N-1ck​=N1​l=0∑N−1​fl​e−ikN2lπ​=l=0∑N−1​al​ωNkl​,k=0,1,⋯,N−1

数学基础

ωNk+N/2=−ωNk\omega_N^{k+N/2} = -\omega_N^kωNk+N/2​=−ωNk​ ωNk+N=ωNk\omega_N^{k+N} = \omega_N^kωNk+N​=ωNk​

且当 NNN 为偶数时(k=0,1,⋯ ,N/2−1k = 0,1,\cdots,N/2-1k=0,1,⋯,N/2−1):

(ωNk)2=ωN/2k(\omega_N^k)^2 = \omega_{N/2}^k(ωNk​)2=ωN/2k​ (ωNk+N/2)2=ωN/2k(\omega_N^{k+N/2})^2 = \omega_{N/2}^k(ωNk+N/2​)2=ωN/2k​ ωNN/2=−1\omega_N^{N/2} = -1ωNN/2​=−1

奇偶分解推导

对 k=0,1,⋯ ,N/2−1k = 0, 1, \cdots, N/2-1k=0,1,⋯,N/2−1,将求和按偶数项(l=0,2,⋯l=0,2,\cdotsl=0,2,⋯)和奇数项(l=1,3,⋯l=1,3,\cdotsl=1,3,⋯)分开:

ck=∑l=0,2,⋯,N−2al (ωN/2k)l/2⏟偶数子问题 Ek+ωNk∑l=1,3,⋯,N−1al (ωN/2k)(l−1)/2⏟奇数子问题 Okc_k = \underbrace{\sum_{\substack{l=0,2,\cdots,N-2}} a_l\,(\omega_{N/2}^k)^{l/2}}_{\text{偶数子问题 }E_k} + \omega_N^k \underbrace{\sum_{\substack{l=1,3,\cdots,N-1}} a_l\,(\omega_{N/2}^k)^{(l-1)/2}}_{\text{奇数子问题 }O_k}ck​=偶数子问题 Ek​l=0,2,⋯,N−2​∑​al​(ωN/2k​)l/2​​+ωNk​奇数子问题 Ok​l=1,3,⋯,N−1​∑​al​(ωN/2k​)(l−1)/2​​

利用 ωNk+N/2=−ωNk\omega_N^{k+N/2} = -\omega_N^kωNk+N/2​=−ωNk​,得到:

ck+N/2=Ek−ωNk Okc_{k+N/2} = E_k - \omega_N^k\, O_kck+N/2​=Ek​−ωNk​Ok​

也即:

ck=Ek+ωNk Ok,ck+N/2=Ek−ωNk Ok,k=0,⋯ ,N2−1c_k = E_k + \omega_N^k\, O_k, \quad c_{k+N/2} = E_k - \omega_N^k\, O_k, \quad k=0,\cdots,\tfrac{N}{2}-1ck​=Ek​+ωNk​Ok​,ck+N/2​=Ek​−ωNk​Ok​,k=0,⋯,2N​−1

几何意义:每次分治将 NNN 点问题分为两个 N/2N/2N/2 点问题,每层只需 O(N)O(N)O(N) 的合并工作,共 log⁡2N\log_2 Nlog2​N 层,总复杂度 O(Nlog⁡N)O(N\log N)O(NlogN)。

算法实现

标准递归:

# 标准递归版
def fft_recursive(a):
    """递归 FFT,要求 N 为 2 的幂,时间复杂度 O(N log N)"""
    N = len(a)
    if N == 1:
        return a                                   # 基础情形:1点DFT即自身
    
    E = fft_recursive(a[0::2])                    # 偶数下标子问题
    O = fft_recursive(a[1::2])                    # 奇数下标子问题
    
    omega = np.exp(-2j * np.pi * np.arange(N // 2) / N)   # 旋转因子
    return np.concatenate([E + omega * O, E - omega * O])  # 蝶形合并

原位迭代:

# 原位迭代版
def fft_iterative(a):
    """迭代 FFT(原位),避免递归开销,内存高效"""
    N = len(a)
    m = int(np.log2(N))
    
    # 步骤1:位逆序置换
    a = a.copy().astype(complex)
    j = 0
    for i in range(1, N):
        bit = N >> 1
        while j & bit:
            j ^= bit
            bit >>= 1
        j ^= bit
        if i < j:
            a[i], a[j] = a[j], a[i]              # 交换位逆序对
    
    # 步骤2:逐层蝶形运算(原位覆盖)
    length = 2
    while length <= N:
        omega_len = np.exp(-2j * np.pi / length)  # 当前层单位根
        for i in range(0, N, length):              # 枚举每个蝶形组
            omega = 1
            for k in range(length // 2):           # 蝶形运算
                u = a[i + k]
                v = omega * a[i + k + length // 2]
                a[i + k] = u + v                   # 上支路
                a[i + k + length // 2] = u - v     # 下支路
                omega *= omega_len
        length <<= 1
    return a

向量化原位迭代:

# 向量化版
def fft_vectorized(a):
    """向量化蝶形,减少 Python 循环"""
    N = len(a)
    a = a.copy().astype(complex)
    # 位逆序
    bits = int(np.log2(N))
    indices = np.arange(N)
    rev = sum(((indices >> i) & 1) << (bits - 1 - i) for i in range(bits))
    a = a[rev]
    # 逐层向量化蝶形
    length = 2
    while length <= N:
        half = length // 2
        k = np.arange(half)
        omega = np.exp(-2j * np.pi * k / length)   # 旋转因子(向量)
        for i in range(0, N, length):
            u = a[i:i+half]
            v = omega * a[i+half:i+length]
            a[i:i+half]      = u + v               # 上支路(向量化)
            a[i+half:i+length] = u - v             # 下支路(向量化)
        length <<= 1
    return a

基于 FFT 的大数乘法

核心思想

关键观察:

一个 n+1n+1n+1 位十进制数 anan−1⋯a0‾\overline{a_n a_{n-1}\cdots a_0}an​an−1​⋯a0​​ 等于多项式:

A(x)=a0+a1x+⋯+anxnA(x) = a_0 + a_1 x + \cdots + a_n x^nA(x)=a0​+a1​x+⋯+an​xn

在 x=10x=10x=10 处的求值,即 A(10)=anan−1⋯a0‾A(10) = \overline{a_n a_{n-1}\cdots a_0}A(10)=an​an−1​⋯a0​​。

因此,两个大数相乘 ⇔\Leftrightarrow⇔ 两个多项式相乘:

C(x)=A(x)⋅B(x),deg⁡C=n+mC(x) = A(x)\cdot B(x), \quad \deg C = n+mC(x)=A(x)⋅B(x),degC=n+m

得到 C(x)C(x)C(x) 的系数后,再用秦九韶算法以 O(n+m)O(n+m)O(n+m) 计算 C(10)C(10)C(10),即得乘积。

算法流程

总时间复杂度: O(Nlog⁡N)O(N\log N)O(NlogN),其中 N>2nN > 2nN>2n。

输入: n+1n+1n+1 位大数 AAA,m+1m+1m+1 位大数 BBB(n≥mn \ge mn≥m)
输出: 乘积 A×BA \times BA×B

步骤操作时间复杂度
1将 AAA、BBB 的各位数字作为多项式系数,零填充至长度 NNN(取大于 2n2n2n 的最小 2 的幂次)—
2FFT(A)→\text{FFT}(A) \toFFT(A)→ 频域 FAF_AFA​;FFT(B)→\text{FFT}(B) \toFFT(B)→ 频域 FBF_BFB​O(Nlog⁡N)O(N\log N)O(NlogN)
3逐点相乘:H[k]=FA[k]⋅FB[k]H[k] = F_A[k] \cdot F_B[k]H[k]=FA​[k]⋅FB​[k],k=0,…,N−1k=0,\dots,N-1k=0,…,N−1O(N)O(N)O(N)
4IFFT(H)→\text{IFFT}(H) \toIFFT(H)→ 时域 CCC(多项式 C(x)C(x)C(x) 的系数)O(Nlog⁡N)O(N\log N)O(NlogN)
5对 CCC 的系数做进位处理(系数可能 >9> 9>9)—
6用秦九韶算法计算 C(10)C(10)C(10)O(n+m)O(n+m)O(n+m)

复杂度汇总

算法时间复杂度空间复杂度适用条件
二维 Lagrange 插值O((nm)2)O((nm)^2)O((nm)2)O(nm)O(nm)O(nm)矩形网格,低次
分片双线性插值O(nm)O(nm)O(nm)O(nm)O(nm)O(nm)矩形网格,任意规模
DFT(朴素)O(N2)O(N^2)O(N2)O(N)O(N)O(N)任意 NNN
FFT(Cooley-Tukey)O(Nlog⁡N)O(N\log N)O(NlogN)O(N)O(N)O(N)N=2mN = 2^mN=2m
基于 FFT 的大数乘法O(Nlog⁡N)O(N\log N)O(NlogN)O(N)O(N)O(N)N>2max⁡(n,m)N > 2\max(n,m)N>2max(n,m)
目录
  • 二维插值
    • 问题定义
    • 二维 Lagrange 插值
    • 分片双线性插值
  • DFT
    • 问题背景
    • DFT 公式
    • 算法实现
  • FFT
    • 核心思想
    • 数学基础
    • 奇偶分解推导
    • 算法实现
  • 基于 FFT 的大数乘法
    • 核心思想
    • 算法流程
  • 复杂度汇总
© 2026 miniyuan. All rights reserved.
Go to miniyuan's GitHub repo