二维插值
问题定义
给定 :二元函数 u ( x , y ) u(x, y) u ( x , y ) 在网格节点处的离散值
u i , j = u ( x i , y j ) , i = 0 , 1 , ⋯ , n , j = 0 , 1 , ⋯ , m u_{i,j} = u(x_i, y_j), \quad i = 0,1,\cdots,n,\; j = 0,1,\cdots,m u i , j = u ( x i , y j ) , i = 0 , 1 , ⋯ , n , j = 0 , 1 , ⋯ , m
求 :关于 x x x 为 n n n 次、y y y 为 m m m 次的二元多项式
P n , m ( x , y ) = ∑ i = 0 n ∑ j = 0 m a i j x i y j P_{n,m}(x,y) = \sum_{i=0}^{n}\sum_{j=0}^{m} a_{ij}\, x^i y^j P n , m ( x , y ) = i = 0 ∑ n j = 0 ∑ m a ij x i y j
满足插值条件:
P n , m ( x i , y j ) = u i , j = u ( x i , y j ) , i = 0 , ⋯ , n , j = 0 , ⋯ , m P_{n,m}(x_i, y_j) = u_{i,j} = u(x_i, y_j), \quad i=0,\cdots,n,\; j=0,\cdots,m P n , m ( x i , y j ) = u i , j = u ( x i , y j ) , i = 0 , ⋯ , n , j = 0 , ⋯ , m
其中:
P n , m ( x , y ) P_{n,m}(x,y) P n , m ( x , y ) 称为插值多项式
( x i , y j ) (x_i, y_j) ( x i , y j ) 称为插值节点
二维 Lagrange 插值
一维 Lagrange 插值回顾:
φ n ( x ) = ∑ i = 0 n y i l i ( x ) , l i ( x j ) = { 1 , i = j 0 , 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 y i l i ( x ) , l i ( x j ) = { 1 , 0 , i = j i = j
二维推广 :构造插值形式
P n , m ( x , y ) = ∑ i = 0 n ∑ j = 0 m u i , j l i , j ( x , y ) , l i , j ( x s , y t ) = { 1 , i = s , j = t 0 , i ≠ s or j ≠ t P_{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} P n , m ( x , y ) = i = 0 ∑ n j = 0 ∑ m u i , j l i , j ( x , y ) , l i , j ( x s , y t ) = { 1 , 0 , i = s , j = t i = s or j = t
分离变量法 构造基函数:
l i , j ( x , y ) = l i ( x ) l ~ j ( y ) l_{i,j}(x,y) = l_i(x)\,\tilde{l}_j(y) l i , j ( x , y ) = l i ( x ) l ~ j ( y )
其中 l i ( x ) l_i(x) l i ( x ) 与 l ~ j ( y ) \tilde{l}_j(y) l ~ j ( y ) 分别为基于以下一维节点的插值基函数:
Δ x : a = x 0 < x 1 < ⋯ < x n = b , Δ y : c = y 0 < y 1 < ⋯ < y m = 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 = x 0 < x 1 < ⋯ < x n = b , Δ y : c = y 0 < y 1 < ⋯ < y m = 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 = { ( x i , y j ) ∣ 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 = {( x i , y j ) ∣ 0 ≤ i ≤ n , 0 ≤ j ≤ m }
注 :二维 Lagrange 插值要求节点排列在矩形网格上,对散点数据不直接适用。
分片双线性插值
问题 :高次 Lagrange 多项式会引发 Runge 现象 ,二维情形同样不能幸免。
改进方法 :采用分片双线性插值 ,是一维分段线性插值的直接推广。
P 1 , 1 ( x , y ) = ∑ i = 0 n ∑ j = 0 m u i , j φ i , j ( x , y ) , φ i , j ( x , y ) = l i ( 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) P 1 , 1 ( x , y ) = i = 0 ∑ n j = 0 ∑ m u i , j φ i , j ( x , y ) , φ i , j ( x , y ) = l i ( x ) l ~ j ( y )
其中 l i ( x ) l_i(x) l i ( x ) 、l ~ j ( y ) \tilde{l}_j(y) l ~ j ( y ) 为分段线性基函数。
进一步改进 :分片双三次 Hermite 插值 ,也即在节点处同时插值函数值和偏导数,改善导数连续性。
方法 次数 优点 缺点 全局 Lagrange n × m n \times m n × m 精确过所有节点 高次时 Runge 现象严重 分片双线性 1 × 1 1 \times 1 1 × 1 简单、稳定 节点处导数不连续 分片双三次 Hermite 3 × 3 3 \times 3 3 × 3 导数连续,光滑 需提供偏导数数据
DFT
问题背景
问题定义 :设 f ( x ) f(x) f ( x ) 在 [ 0 , 2 π ] [0, 2\pi] [ 0 , 2 π ] 上 N + 1 N+1 N + 1 个等距节点
x l = 2 π l N , l = 0 , 1 , ⋯ , N x_l = \frac{2\pi l}{N}, \quad l = 0, 1, \cdots, N x l = N 2 π l , l = 0 , 1 , ⋯ , N
处的值为 f ( x l ) ≡ f l f(x_l) \equiv f_l f ( x l ) ≡ f l ,且满足周期性条件 f 0 = f N f_0 = f_N f 0 = f N ,求
P ( x ) = ∑ k = 0 N − 1 c k e i k x P(x) = \sum_{k=0}^{N-1} c_k\, e^{\mathrm{i}kx} P ( x ) = k = 0 ∑ N − 1 c k e i k x
使得以下插值条件成立:
P ( x l ) = f ( x l ) = f l , l = 0 , 1 , ⋯ , N P(x_l) = f(x_l) = f_l, \quad l = 0,1,\cdots,N P ( x l ) = f ( x l ) = f l , l = 0 , 1 , ⋯ , N
注 :采用指数形式,自动满足周期性。
DFT 公式
记 N N N 次单位根:
ω N = e i 2 π N \omega_N = e^{\mathrm{i}\frac{2\pi}{N}} ω N = e i N 2 π
则 e i k 2 l π N = ω N k l e^{\mathrm{i}k\frac{2l\pi}{N}} = \omega_N^{kl} e i k N 2 l π = ω N k l 。
将插值条件代入,得线性方程组:
A c = f \mathbf{A}\,\boldsymbol{c} = \boldsymbol{f} A c = f
其中矩阵 A \mathbf{A} A 为 Vandermonde 型复数矩阵:
A = [ 1 1 1 ⋯ 1 1 ω N ω N 2 ⋯ ω N N − 1 ⋮ ⋮ ⋮ ⋱ ⋮ 1 ω N N − 1 ω N 2 ( N − 1 ) ⋯ ω N ( N − 1 ) ( N − 1 ) ] = [ a 0 a 1 ⋯ a N − 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 = 1 1 ⋮ 1 1 ω N ⋮ ω N N − 1 1 ω N 2 ⋮ ω N 2 ( N − 1 ) ⋯ ⋯ ⋱ ⋯ 1 ω N N − 1 ⋮ ω N ( N − 1 ) ( N − 1 ) = [ a 0 a 1 ⋯ a N − 1 ]
c = [ c 0 c 1 ⋯ c N − 1 ] T , f = [ f 0 f 1 ⋯ f N − 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 = [ c 0 c 1 ⋯ c N − 1 ] T , f = [ f 0 f 1 ⋯ f N − 1 ] T
显然有以下列向量的正交性 :
( a j , a k ) ≡ ∑ l = 0 N − 1 ω N j l ω N k l ‾ = ∑ l = 0 N − 1 ω N ( j − k ) l = { N , j = k 0 , 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} ( a j , a k ) ≡ l = 0 ∑ N − 1 ω N j l ω N k l = l = 0 ∑ N − 1 ω N ( j − k ) l = { N , 0 , j = k j = k
由正交性,用 a ˉ k T \bar{\boldsymbol{a}}_k^{\mathrm{T}} a ˉ k T 左乘方程组两边,立即得到 DFT 正变换公式 :
c k = 1 N ∑ l = 0 N − 1 f l ω N − k l , k = 0 , 1 , ⋯ , N − 1 c_k = \frac{1}{N}\sum_{l=0}^{N-1} f_l\, \omega_N^{-kl}, \quad k=0,1,\cdots,N-1 c k = N 1 l = 0 ∑ N − 1 f l ω N − k l , k = 0 , 1 , ⋯ , N − 1
由矩阵方程即得 DFT 反变换公式 :
f l = ∑ k = 0 N − 1 c k ω N k l , l = 0 , 1 , ⋯ , N − 1 f_l = \sum_{k=0}^{N-1} c_k\, \omega_N^{kl}, \quad l=0,1,\cdots,N-1 f l = k = 0 ∑ N − 1 c k ω N k l , l = 0 , 1 , ⋯ , N − 1
算法实现
DFT 正变换与反变换时间复杂度均为 O ( N 2 ) \mathcal{O}(N^2) O ( N 2 ) 。
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( - 1 j * 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( 1 j * k * 2 * np.pi * l / N)
return f
FFT
核心思想
基本策略 :
将 N N N 个点的 DFT 问题分解为 2 个 N / 2 N/2 N /2 个点的 DFT 问题,重复此过程,直至变成 N / 2 N/2 N /2 个 2 点 DFT 问题。
前提 :N = 2 m N = 2^m N = 2 m ,也即 N N N 为 2 的整数次幂。
定义单位根 :
ω N = e − i 2 π N \omega_N = e^{-\mathrm{i}\frac{2\pi}{N}} ω N = e − i N 2 π
重写 DFT(令 a l ≡ f l / N a_l \equiv f_l/N a l ≡ f l / N ):
c k = 1 N ∑ l = 0 N − 1 f l e − i k 2 l π N = ∑ l = 0 N − 1 a l ω N k l , k = 0 , 1 , ⋯ , N − 1 c_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-1 c k = N 1 l = 0 ∑ N − 1 f l e − i k N 2 l π = l = 0 ∑ N − 1 a l ω N k l , k = 0 , 1 , ⋯ , N − 1
数学基础
ω N k + N / 2 = − ω N k \omega_N^{k+N/2} = -\omega_N^k ω N k + N /2 = − ω N k
ω N k + N = ω N k \omega_N^{k+N} = \omega_N^k ω N k + N = ω N k
且当 N N N 为偶数时(k = 0 , 1 , ⋯ , N / 2 − 1 k = 0,1,\cdots,N/2-1 k = 0 , 1 , ⋯ , N /2 − 1 ):
( ω N k ) 2 = ω N / 2 k (\omega_N^k)^2 = \omega_{N/2}^k ( ω N k ) 2 = ω N /2 k
( ω N k + N / 2 ) 2 = ω N / 2 k (\omega_N^{k+N/2})^2 = \omega_{N/2}^k ( ω N k + N /2 ) 2 = ω N /2 k
ω N N / 2 = − 1 \omega_N^{N/2} = -1 ω N N /2 = − 1
奇偶分解推导
对 k = 0 , 1 , ⋯ , N / 2 − 1 k = 0, 1, \cdots, N/2-1 k = 0 , 1 , ⋯ , N /2 − 1 ,将求和按偶数项(l = 0 , 2 , ⋯ l=0,2,\cdots l = 0 , 2 , ⋯ )和奇数项(l = 1 , 3 , ⋯ l=1,3,\cdots l = 1 , 3 , ⋯ )分开:
c k = ∑ l = 0 , 2 , ⋯ , N − 2 a l ( ω N / 2 k ) l / 2 ⏟ 偶数子问题 E k + ω N k ∑ l = 1 , 3 , ⋯ , N − 1 a l ( ω N / 2 k ) ( l − 1 ) / 2 ⏟ 奇数子问题 O k c_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} c k = 偶数子问题 E k l = 0 , 2 , ⋯ , N − 2 ∑ a l ( ω N /2 k ) l /2 + ω N k 奇数子问题 O k l = 1 , 3 , ⋯ , N − 1 ∑ a l ( ω N /2 k ) ( l − 1 ) /2
利用 ω N k + N / 2 = − ω N k \omega_N^{k+N/2} = -\omega_N^k ω N k + N /2 = − ω N k ,得到:
c k + N / 2 = E k − ω N k O k c_{k+N/2} = E_k - \omega_N^k\, O_k c k + N /2 = E k − ω N k O k
也即:
c k = E k + ω N k O k , c k + N / 2 = E k − ω N k O k , k = 0 , ⋯ , N 2 − 1 c_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}-1 c k = E k + ω N k O k , c k + N /2 = E k − ω N k O k , k = 0 , ⋯ , 2 N − 1
几何意义 :每次分治将 N N N 点问题分为两个 N / 2 N/2 N /2 点问题,每层只需 O ( N ) O(N) O ( N ) 的合并工作,共 log 2 N \log_2 N log 2 N 层,总复杂度 O ( N log N ) O(N\log N) O ( N log N ) 。
算法实现
标准递归 :
# 标准递归版
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( - 2 j * 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( - 2 j * 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( - 2 j * 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 + 1 n+1 n + 1 位十进制数 a n a n − 1 ⋯ a 0 ‾ \overline{a_n a_{n-1}\cdots a_0} a n a n − 1 ⋯ a 0 等于多项式:
A ( x ) = a 0 + a 1 x + ⋯ + a n x n A(x) = a_0 + a_1 x + \cdots + a_n x^n A ( x ) = a 0 + a 1 x + ⋯ + a n x n
在 x = 10 x=10 x = 10 处的求值,即 A ( 10 ) = a n a n − 1 ⋯ a 0 ‾ A(10) = \overline{a_n a_{n-1}\cdots a_0} A ( 10 ) = a n a n − 1 ⋯ a 0 。
因此,两个大数相乘 ⇔ \Leftrightarrow ⇔ 两个多项式相乘:
C ( x ) = A ( x ) ⋅ B ( x ) , deg C = n + m C(x) = A(x)\cdot B(x), \quad \deg C = n+m C ( x ) = A ( x ) ⋅ B ( x ) , deg C = n + m
得到 C ( x ) C(x) C ( x ) 的系数后,再用秦九韶算法以 O ( n + m ) O(n+m) O ( n + m ) 计算 C ( 10 ) C(10) C ( 10 ) ,即得乘积。
算法流程
总时间复杂度: O ( N log N ) O(N\log N) O ( N log N ) ,其中 N > 2 n N > 2n N > 2 n 。
输入: n + 1 n+1 n + 1 位大数 A A A ,m + 1 m+1 m + 1 位大数 B B B (n ≥ m n \ge m n ≥ m )
输出: 乘积 A × B A \times B A × B
步骤 操作 时间复杂度 1 将 A A A 、B B B 的各位数字作为多项式系数,零填充至长度 N N N (取大于 2 n 2n 2 n 的最小 2 的幂次) — 2 FFT ( A ) → \text{FFT}(A) \to FFT ( A ) → 频域 F A F_A F A ;FFT ( B ) → \text{FFT}(B) \to FFT ( B ) → 频域 F B F_B F B O ( N log N ) O(N\log N) O ( N log N ) 3 逐点相乘:H [ k ] = F A [ k ] ⋅ F B [ k ] H[k] = F_A[k] \cdot F_B[k] H [ k ] = F A [ k ] ⋅ F B [ k ] ,k = 0 , … , N − 1 k=0,\dots,N-1 k = 0 , … , N − 1 O ( N ) O(N) O ( N ) 4 IFFT ( H ) → \text{IFFT}(H) \to IFFT ( H ) → 时域 C C C (多项式 C ( x ) C(x) C ( x ) 的系数)O ( N log N ) O(N\log N) O ( N log N ) 5 对 C C C 的系数做进位处理(系数可能 > 9 > 9 > 9 ) — 6 用秦九韶算法计算 C ( 10 ) C(10) C ( 10 ) O ( n + m ) O(n+m) O ( n + m )
复杂度汇总
算法 时间复杂度 空间复杂度 适用条件 二维 Lagrange 插值 O ( ( n m ) 2 ) O((nm)^2) O (( nm ) 2 ) O ( n m ) O(nm) O ( nm ) 矩形网格,低次 分片双线性插值 O ( n m ) O(nm) O ( nm ) O ( n m ) O(nm) O ( nm ) 矩形网格,任意规模 DFT(朴素) O ( N 2 ) O(N^2) O ( N 2 ) O ( N ) O(N) O ( N ) 任意 N N N FFT(Cooley-Tukey) O ( N log N ) O(N\log N) O ( N log N ) O ( N ) O(N) O ( N ) N = 2 m N = 2^m N = 2 m 基于 FFT 的大数乘法 O ( N log N ) O(N\log N) O ( N log N ) O ( N ) O(N) O ( N ) N > 2 max ( n , m ) N > 2\max(n,m) N > 2 max ( n , m )