📜  多项式乘法的快速傅立叶变换(1)

📅  最后修改于: 2023-12-03 15:23:43.626000             🧑  作者: Mango

多项式乘法的快速傅立叶变换

多项式乘法是计算机科学中一个重要的问题,尤其是在数学和工程领域,如图像处理、信号处理、代码压缩等。传统的多项式乘法算法的时间复杂度为 $O(n^2)$,但是可以利用快速傅里叶变换(FFT)算法将其降为 $O(n\log n)$。

FFT算法介绍

FFT算法是一种高效的离散傅立叶变换(Discrete Fourier Transform,DFT)算法,它利用了一些数学技巧将DFT的时间复杂度从 $O(n^2)$ 降低到 $O(n\log n)$。因此,FFT算法可以将多项式的乘法效率提高到 $O(n\log n)$,是一种非常优秀的算法。

DFT

DFT是信号处理中常用的一种方法,它将时域(时间)上的信号转换为频域(频率)上的信号。给定一个长度为 $n$ 的序列 $x={x_0,x_1,x_2,\dots,x_{n-1}}$,其DFT为:

$$ X_k = \sum_{j=0}^{n-1} x_j \cdot w_n^{jk} \quad 0\le k<n $$

其中 $w_n=\mathrm{e}^{-2\pi \mathrm{i}/n}$,$\mathrm{i}$ 为虚数单位。DFT最简单的算法是通过定义式计算,时间复杂度为 $O(n^2)$。我们将在下一节中介绍FFT算法,它能够利用某些性质将复杂度降到 $O(n\log n)$。

IDFT

将信号从频域转换为时域,得到逆离散傅里叶变换(Inverse Discrete Fourier Transform,IDFT):

$$ x_j=\frac{1}{n}\sum_{k=0}^{n-1} X_k\cdot w_n^{-jk} \quad 0\le j<n $$

FFT算法

FFT算法建立在DFT的性质之上,通过分治法来实现复杂度为 $O(n\log n)$ 的计算。FFT有一系列的优势:

  • 时间复杂度: $O(n\log n)$,比DFT的 $O(n^2)$ 级别的算法快得多。
  • 空间复杂度:FFT算法需要O(n)的空间,比暴力算法好很多。

FFT算法的主要流程如下:

  1. 对$ n $个数据点,周期为$ T $,离散傅里叶变换共有$ n $次。
  2. 将$ n $个数据拆分为$ 2 $个子序列,分别做离散傅里叶变换。
  3. 对于此问题,如图所示: FFT算法
  • 直接对做普通点值乘法并进行系数加和。会造成时间复杂度为 $O(n^2)$,因此需要使用分治法来将问题分解为小型重复解决的子问题。
  • 对小的子问题进行递归求解。
  • 根据中间结果计算原问题的解。

在代码实现中,通常采用递归实现FFT算法,其中 $n$ 为 $2$ 的幂次。当 $n=1$ 时,递归返回;当 $n>1$ 时,递归计算两个子问题的FFT,然后将子问题的结果组合为原问题的FFT。对于计算 DFT 的常数项,CPU 的乘法操作比较快,建议采用直接计算的方式。

def fft(a):
    n = len(a)
    if n == 1:
        return a

    w_n = complex(cos(2*pi/n), sin(2*pi/n))
    omega, odd = 1, a[1::2]
    even, y = fft(a[::2]), [0] * n

    for i in range(n//2):
        y[i] = even[i] + omega * odd[i]
        y[i+n//2] = even[i] - omega * odd[i]
        omega = omega * w_n

    return y
多项式乘法

假设有两个多项式 $A(x)$ 和 $B(x)$,均为 $n$ 次多项式,它们的系数分别为:

$$ A(x) = a_0 + a_1x + a_2x^2 + \dots + a_{n-1}x^{n-1} + a_nx^n $$

$$ B(x) = b_0 + b_1x + b_2x^2 + \dots + b_{n-1}x^{n-1} + b_nx^n $$

则它们的乘积多项式 $C(x)$ 的系数可以通过 $A(x)$ 和 $B(x)$ 的卷积来计算:

$$ C(x) = A(x) \cdot B(x) = \sum_{i=0}^{n-1} a_ix^i \cdot \sum_{j=0}^{n-1} b_jx^j = \sum_{i+j\le n} a_ib_jx^{i+j} $$

因此,如果我们可以用FFT算法求出 $A(x)$ 和 $B(x)$ 的点值表达,就可以得到它们的乘积的点值表达式,最终再用逆FFT算法求出 $C(x)$ 的系数,即可解决多项式乘法问题。

多项式乘法 FFT算法的常用模板如下所示

def convolution(a, b):
    # 扩展到2的幂次方
    n = 1
    while n < len(a) + len(b):
        n *= 2
    a += [0] * (n - len(a))
    b += [0] * (n - len(b))

    # fft
    fft_a = fft(a)
    fft_b = fft(b)
    c = [i * j for i, j in zip(fft_a, fft_b)]
    # ifft
    ifft_c = ifft(c)

    # 将结果的虚部舍去
    return [int(round(i.real)) for i in ifft_c]

该程序 first 环节进行的是数据预处理,将两个多项式 $A(x)$ 和 $B(x)$ 添零至等长 2 次幂,在处理完成之后,进行 DFT(fast fourier transform)得到两个多项式的点值表达式,执行点值乘法,即对应项相乘,得到单个多项式的点值表达式。最后,通过 iDFT(inverse discrete fourier transform)将多项式转换回系数表达式。

结语

FFT算法技术是一种理解点值思想和快速多项式卷积思维的关键。将长时间复杂度的多项式乘法优化到快速的 $O(n\log n)$ 之后,FFT算法逐渐在实际使用中得到了广泛的应用。