多项式乘法问题的快速傅里叶变换算法简述

问题:给定多项式 A ( x ) = Σ i = 0 n a i x i , B ( x ) = Σ i = 0 m b i x i A(x) = \Sigma_{i = 0}^n a_i x^i, B(x) = \Sigma_{i = 0}^m b_i x^i A(x)=Σi=0naixi,B(x)=Σi=0mbixi 的系数 a 0.. n , b 0.. m a_{0 .. n}, b_{0 .. m} a0..n,b0..m,求其多项式乘积 C ( x ) = A ( x ) B ( x ) = Σ i = 0 m + n c i x i C(x) = A(x) B(x) = \Sigma_{i = 0}^{m + n} c_i x^i C(x)=A(x)B(x)=Σi=0m+ncixi 的各项系数 c 0.. n + m c_{0 .. n + m} c0..n+m,以下不妨设 n > m n > m n>m

朴素算法:计算 c i = Σ j = 0 i a j b i − j c_i = \Sigma_{j = 0}^i a_j b_{i - j} ci=Σj=0iajbij,时间复杂度 O ( n 2 ) O(n^2) O(n2)

多项式乘法的快速傅里叶变换(FFT)算法:
第一步,找到最小的 N N N,满足 N ≥ n + m N \geq n + m Nn+m N N N 为 2 的整数幂,将多项式 A ( x ) A(x) A(x) B ( x ) B(x) B(x) 高次项系数补 0 0 0,补至 x N − 1 x^{N - 1} xN1 项为止,至此 A ( x ) A(x) A(x) B ( x ) B(x) B(x) 以及积 C ( x ) C(x) C(x) 都有 N N N 个系数;
第二步,用 FFT 将 A ( x ) A(x) A(x) B ( x ) B(x) B(x) 由系数表示转为点值表示,即将 a 0 , a 1 , . . , a N − 1 a_0, a_1, .., a_{N - 1} a0,a1,..,aN1 转化为 A ( ω N 0 ) , A ( ω N 1 ) , . . , A ( ω N N − 1 ) A(\omega_N^0), A(\omega_N^1), .., A(\omega_N^{N - 1}) A(ωN0),A(ωN1),..,A(ωNN1),其中所取的 N N N 个样本点是方程 ω N = 1 \omega^N = 1 ωN=1 N N N 个复根,即满足 ω N k = e 2 π i k N = cos ⁡ 2 π k N + i sin ⁡ 2 π k N , i = − 1 \omega_N^k = e^{\frac{2 \pi i k}{N}} = \cos \frac{2 \pi k}{N} + i \sin \frac{2 \pi k}{N}, i = \sqrt{-1} ωNk=eN2πik=cosN2πk+isinN2πk,i=1 B ( x ) B(x) B(x) 也是同理;
第三步,计算 C ( ω N k ) = A ( ω N k ) B ( ω N k ) C(\omega_N^k) = A(\omega_N^k) B(\omega_N^k) C(ωNk)=A(ωNk)B(ωNk),得出多项式 C ( x ) C(x) C(x) 的点值表示;
第四步,用 FFT 将 C ( x ) C(x) C(x) 由点值表示转为系数表示,这一步和第二步都可以在 Θ ( n log ⁡ n ) \Theta(n \log n) Θ(nlogn) 的时间复杂度下完成;
第五步,删去系数为 0 0 0 的高次项,保留到最高项 c n + m x n + m c_{n + m} x^{n + m} cn+mxn+m
总时间复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn)

FFT 具体细节不赘述,参考代码如下,其中 F F T ( a , l ) FFT(a, l) FFT(a,l) 函数将 a [ 0.. 2 l ] a[0 .. 2^l] a[0..2l] 由参数表示转为点值表示, F F T N ( a , l ) FFTN(a, l) FFTN(a,l) 函数将 a [ 0.. 2 l ] a[0 .. 2^l] a[0..2l] 由点值表示转为参数表示。

import math

def BitReverse(p, l):
    q, r = [], [0]
    for i in range(1, 1 << l):
        r.append(r[i >> 1] >> 1 | (i & 1) << l - 1)
    for ri in r:
        q.append(p[ri])
    return q

def IterativeFFT(a, l, g):
    a = BitReverse(a, l)
    for s in range(l):
        m = 2 << s
        wm = complex(math.cos(2 * math.pi / m), g * math.sin(2 * math.pi / m))
        for k in range(0, 1 << l, m):
            w = complex(1)
            for j in range(m >> 1):
                u, t = a[k + j], w * a[k + j + (m >> 1)]
                a[k + j], a[k + j + (m >> 1)] = u + t, u - t
                w *= wm
    return a

def FFT(a, l):
    return IterativeFFT(a, l, 1)

def FFTN(a, l):
    return [ai / (1 << l) for ai in IterativeFFT(a, l, -1)]

由于 FFT 引入复数和浮点运算,使得运算结果和标准答案间存在由小数运算导致的系统误差。考虑到复数域上方程 z N = 1 z^N = 1 zN=1 与模 p p p 剩余类域上方程 x N ≡ 1 ( m o d   p ) x^N \equiv 1 (mod \ p) xN1(mod p) 的同构性,若 ∃ x N ∈ [ 1 , p − 1 ] \exist x_N \in [1, p - 1] xN[1,p1] 使得 x N N ≡ 1 ( m o d   p ) x_N^N \equiv 1 (mod \ p) xNN1(mod p) 且对于 ∀ i , j ∈ [ 1 , N ] \forall i, j \in [1, N] i,j[1,N],若 i ≠ j i \neq j i=j x N i ≢ x N j ( m o d   p ) x_N^i \not\equiv x_N^j (mod \ p) xNixNj(mod p),则 x N x_N xN 在模 p p p 意义下与 FFT 中的 ω N \omega_N ωN 作用相同,可以代替以避免引入复数和浮点运算。以上就是数论变换(NTT)的简要概括。

你可能感兴趣的:(算法,算法)