直入正题,我们需要解决的问题是多项式乘法 。给定两个最高次为n n n 和m m m 的多项式A ( x ) A(x) A ( x ) 和B ( x ) B(x) B ( x ) ,我们需要计算它们的乘积C ( x ) = A ( x ) ⋅ B ( x ) C(x) = A(x) \cdot B(x) C ( x ) = A ( x ) ⋅ B ( x ) 。
直接计算的方法需要O ( n m ) O(nm) O ( n m ) 的时间复杂度,而FFT可以将其优化到O ( ( n + m ) log ( n + m ) ) O((n+m)\log(n+m)) O ( ( n + m ) log ( n + m ) ) 。
时域和频域相信我,你不需要有 EE 背景也能理解这个问题。图片可以参考这篇文章:知乎 。
总而言之,一般的多项式表示办法:A ( x ) = a 0 + a 1 x + a 2 x 2 + … + a n − 1 x n − 1 A(x) = a_0 + a_1 x + a_2 x^2 + \ldots + a_{n-1} x^{n-1} A ( x ) = a 0 + a 1 x + a 2 x 2 + … + a n − 1 x n − 1 ,就是一种时域 的表示方法。这种方法的优点是直观,缺点是乘法需要O ( n m ) O(nm) O ( n m ) 的时间复杂度。
与此对应的,还有一种表示办法:A = { ( x 0 , A ( x 0 ) ) , ( x 1 , A ( x 1 ) ) , … , ( x k , A ( x k ) ) } A = \{(x_0, A(x_0)), (x_1, A(x_1)), \ldots, (x_k, A(x_k))\} A = { ( x 0 , A ( x 0 ) ) , ( x 1 , A ( x 1 ) ) , … , ( x k , A ( x k ) ) } ,其中x i x_i x i 是一些特定的点。这种方法叫做频域 的表示方法。它的优点是 (i) 要计算多项式乘法,将各个点的A ( x i ) A(x_i) A ( x i ) 和B ( x i ) B(x_i) B ( x i ) 相乘就行了,时间复杂度是O ( k ) O(k) O ( k ) ;(ii) 有k k k 个点就能唯一确定一个最高次为k − 1 k-1 k − 1 的多项式。
时域和频域的对应关系 —— 如果假设A ( x ) A(x) A ( x ) 是一堆正弦波的叠加,在时域中我们看到的是一个复杂的波形(如矩形波),代表最终的多项式值;在频域中,我们看到的是构成A ( x ) A(x) A ( x ) 的各个正弦波,在不同频率上的波有着不同的振幅(即A ( x i ) A(x_i) A ( x i ) 的值)。因此,时域和频域是同一个多项式的两种不同表示方法。
转载自上面的知乎链接,感谢原作者
因此,我们要进行多项式乘法,其实思路是很简单的:
DFT(离散傅里叶变换):将A ( x ) A(x) A ( x ) 和B ( x ) B(x) B ( x ) 从时域转换到频域,得到A ( x i ) A(x_i) A ( x i ) 和B ( x i ) B(x_i) B ( x i ) 。 点乘:在频域中,对应点相乘,得到C ( x i ) = A ( x i ) ⋅ B ( x i ) C(x_i) = A(x_i) \cdot B(x_i) C ( x i ) = A ( x i ) ⋅ B ( x i ) 。 IDFT(离散逆傅里叶变换):将C ( x i ) C(x_i) C ( x i ) 从频域转换回时域,得到C ( x ) C(x) C ( x ) 。 单位根不过说起来,如果真的只是随便选点A i A_i A i 的话,计算起来并不会更快。考虑一下,A ( x ) A(x) A ( x ) 就要选n + 1 n+1 n + 1 个点,计算这些点的A ( x i ) A(x_i) A ( x i ) 已经是O ( n 2 ) O(n^2) O ( n 2 ) 的时间复杂度了。但是就不能更快了吗?显然不是。
FFT 的核心就是选择了一些特殊的点,这些点叫做单位根 (roots of unity)。我相信你有一定的复数基础,总之至少应该见过三次主单位根ω 3 = − 1 + i 3 2 \omega_3 = \frac{-1 + i\sqrt{3}}{2} ω 3 = 2 − 1 + i 3 ,它的平方ω 3 2 = − 1 − i 3 2 \omega_3^2 = \frac{-1 - i\sqrt{3}}{2} ω 3 2 = 2 − 1 − i 3 ,和ω 3 0 = 1 \omega_3^0 = 1 ω 3 0 = 1 。它们一起构成了一个三次单位根的集合,平分了单位圆。也因此,可以写成:ω 3 = e 2 π i / 3 \omega_3 = e^{2\pi i / 3} ω 3 = e 2 π i / 3 ,ω 3 2 = e 4 π i / 3 \omega_3^2 = e^{4\pi i / 3} ω 3 2 = e 4 π i / 3 ,ω 3 0 = e 0 = 1 \omega_3^0 = e^{0} = 1 ω 3 0 = e 0 = 1 。
一般地,n n n 次单位根的集合可以表示为{ ω n 0 , ω n 1 , ω n 2 , … , ω n n − 1 } \{\omega_n^0, \omega_n^1, \omega_n^2, \ldots, \omega_n^{n-1}\} { ω n 0 , ω n 1 , ω n 2 , … , ω n n − 1 } ,其中ω n = e 2 π i / n = cos ( 2 π / n ) + i sin ( 2 π / n ) \omega_n = e^{2\pi i / n} = \cos(2\pi / n) + i \sin(2\pi / n) ω n = e 2 π i / n = cos ( 2 π / n ) + i sin ( 2 π / n ) 。
很明显,单位根有强烈的对称性:
ω n n = 1 \omega_n^n = 1 ω n n = 1 。对称性:ω n n / 2 = − 1 \omega_n^{n/2} = -1 ω n n / 2 = − 1 (当n n n 是偶数时),所以ω n k + n / 2 = − ω n k \omega_n^{k + n/2} = -\omega_n^k ω n k + n / 2 = − ω n k 。 折半引理:( ω n k ) 2 = ω n 2 k = ω n / 2 k (\omega_n^k)^2 = \omega_n^{2k} = \omega_{n/2}^k ( ω n k ) 2 = ω n 2 k = ω n / 2 k (当n n n 是偶数时)。 分治因此我们就可以通过分治 的方法来计算 DFT 了。
我们可以将A ( x ) = a 0 + a 1 x + a 2 x 2 + … + a n − 1 x n − 1 A(x) = a_0 + a_1 x + a_2 x^2 + \ldots + a_{n-1} x^{n-1} A ( x ) = a 0 + a 1 x + a 2 x 2 + … + a n − 1 x n − 1 分成两部分:
A ( x ) = ( a 0 + a 2 x 2 + a 4 x 4 + … ) + x ( a 1 + a 3 x 2 + a 5 x 4 + … ) A(x) = (a_0 + a_2 x^2 + a_4 x^4 + \ldots) + x (a_1 + a_3 x^2 + a_5 x^4 + \ldots) A ( x ) = ( a 0 + a 2 x 2 + a 4 x 4 + … ) + x ( a 1 + a 3 x 2 + a 5 x 4 + … )
前后两部分是相似的结构,我们可以定义:
A even ( x ) = a 0 + a 2 x + a 4 x 2 + … A_{\text{even}}(x) = a_0 + a_2 x + a_4 x^2 + \ldots A even ( x ) = a 0 + a 2 x + a 4 x 2 + … (偶数项)A odd ( x ) = a 1 + a 3 x + a 5 x 2 + … A_{\text{odd}}(x) = a_1 + a_3 x + a_5 x^2 + \ldots A odd ( x ) = a 1 + a 3 x + a 5 x 2 + … (奇数项)则A ( x ) = A even ( x 2 ) + x A odd ( x 2 ) A(x) = A_{\text{even}}(x^2) + x A_{\text{odd}}(x^2) A ( x ) = A even ( x 2 ) + x A odd ( x 2 ) 。
将单位根ω n k \omega_n^k ω n k 代入A ( x ) A(x) A ( x ) 中,我们得到:
A ( ω n k ) = A even ( ( ω n k ) 2 ) + ω n k A odd ( ( ω n k ) 2 ) = A even ( ω n / 2 k ) + ω n k A odd ( ω n / 2 k ) \begin{aligned} A(\omega_n^k) &= A_{\text{even}}((\omega_n^k)^2) + \omega_n^k A_{\text{odd}}((\omega_n^k)^2) \\ &= A_{\text{even}}(\omega_{n/2}^k) + \omega_n^k A_{\text{odd}}(\omega_{n/2}^k) \end{aligned} A ( ω n k ) = A even ( ( ω n k ) 2 ) + ω n k A odd ( ( ω n k ) 2 ) = A even ( ω n / 2 k ) + ω n k A odd ( ω n / 2 k )
好消息是,当我们计算A ( ω n k + n / 2 ) A(\omega_n^{k + n/2}) A ( ω n k + n / 2 ) 时,利用对称性:
A ( ω n k + n / 2 ) = A even ( ( ω n k + n / 2 ) 2 ) + ω n k + n / 2 A odd ( ( ω n k + n / 2 ) 2 ) = A even ( ω n / 2 k + n / 2 ) + ω n k + n / 2 A odd ( ω n / 2 k + n / 2 ) = A even ( ω n / 2 k ) + ω n k + n / 2 A odd ( ω n / 2 k ) ( ∗ ) = A even ( ω n / 2 k ) − ω n k A odd ( ω n / 2 k ) \begin{aligned} A(\omega_n^{k + n/2}) &= A_{\text{even}}((\omega_n^{k + n/2})^2) + \omega_n^{k + n/2} A_{\text{odd}}((\omega_n^{k + n/2})^2) \\ &= A_{\text{even}}(\omega_{n/2}^{k + n/2}) + \omega_n^{k + n/2} A_{\text{odd}}(\omega_{n/2}^{k + n/2}) \\ &= A_{\text{even}}(\omega_{n/2}^k) + \omega_n^{k + n/2} A_{\text{odd}}(\omega_{n/2}^k) \qquad (\ast) \\ &= A_{\text{even}}(\omega_{n/2}^k) - \omega_n^k A_{\text{odd}}(\omega_{n/2}^k) \end{aligned} A ( ω n k + n / 2 ) = A even ( ( ω n k + n / 2 ) 2 ) + ω n k + n / 2 A odd ( ( ω n k + n / 2 ) 2 ) = A even ( ω n / 2 k + n / 2 ) + ω n k + n / 2 A odd ( ω n / 2 k + n / 2 ) = A even ( ω n / 2 k ) + ω n k + n / 2 A odd ( ω n / 2 k ) ( ∗ ) = A even ( ω n / 2 k ) − ω n k A odd ( ω n / 2 k )
( ∗ ) (\ast) ( ∗ ) 注意这一步中,单位根的周期已经变成n 2 \frac{n}{2} 2 n 了,所以ω n / 2 k + n / 2 = ω n / 2 k \omega_{n/2}^{k + n/2} = \omega_{n/2}^k ω n / 2 k + n / 2 = ω n / 2 k 。
这样一来,要计算A ( ω n k ) A(\omega_n^k) A ( ω n k ) 和A ( ω n k + n / 2 ) A(\omega_n^{k + n/2}) A ( ω n k + n / 2 ) ,只需要递归地计算A even ( ω n / 2 k ) A_{\text{even}}(\omega_{n/2}^k) A even ( ω n / 2 k ) 和A odd ( ω n / 2 k ) A_{\text{odd}}(\omega_{n/2}^k) A odd ( ω n / 2 k ) 就行了。
如果假设n + 1 = 2 k n + 1 = 2^k n + 1 = 2 k ,也就是说总项数是 2 的幂次的话,那么得到的A even A_{\text{even}} A even 和A odd A_{\text{odd}} A odd 的项数都是2 k − 1 2^{k-1} 2 k − 1 ,因此可以完美地进行递归。每一层递归的时间复杂度是O ( n ) O(n) O ( n ) ,总共有O ( log n ) O(\log n) O ( log n ) 层递归,因此总的时间复杂度是O ( n log n ) O(n \log n) O ( n log n ) 。
递归的边界条件是当n = 1 n = 1 n = 1 的时候,直接返回A ( x ) = a 0 A(x) = a_0 A ( x ) = a 0 。
从 DFT 到 IDFTIDFT 的问题与 DFT 正好相反,我们已知A = { ( ω n 0 , A ( ω n 0 ) ) , ( ω n 1 , A ( ω n 1 ) ) , … , ( ω n n − 1 , A ( ω n n − 1 ) ) } A = \{(\omega_n^0, A(\omega_n^0)), (\omega_n^1, A(\omega_n^1)), \ldots, (\omega_n^{n-1}, A(\omega_n^{n-1}))\} A = { ( ω n 0 , A ( ω n 0 ) ) , ( ω n 1 , A ( ω n 1 ) ) , … , ( ω n n − 1 , A ( ω n n − 1 ) ) } ,我们需要计算A ( x ) A(x) A ( x ) 的系数a 0 , a 1 , … , a n − 1 a_0, a_1, \ldots, a_{n-1} a 0 , a 1 , … , a n − 1 。
这就用到了 DFT 的逆变换公式:
a k = 1 n ∑ j = 0 n − 1 A ( ω n j ) ⋅ ω n − j k a_k = \frac{1}{n} \sum_{j=0}^{n-1} A(\omega_n^j) \cdot \omega_n^{-jk} a k = n 1 j = 0 ∑ n − 1 A ( ω n j ) ⋅ ω n − j k
这是如何得到的呢?我们可以将这n n n 个多项式的值写成线性方程组:
[ 1 1 1 … 1 1 ω n 1 ω n 2 … ω n n − 1 1 ω n 2 ω n 4 … ω n 2 ( n − 1 ) ⋮ ⋮ ⋮ ⋱ ⋮ 1 ω n n − 1 ω n 2 ( n − 1 ) … ω n ( n − 1 ) ( n − 1 ) ] [ a 0 a 1 a 2 ⋮ a n − 1 ] = [ A ( ω n 0 ) A ( ω n 1 ) A ( ω n 2 ) ⋮ A ( ω n n − 1 ) ] \begin{bmatrix} 1 & 1 & 1 & \ldots & 1 \\ 1 & \omega_n^1 & \omega_n^2 & \ldots & \omega_n^{n-1} \\ 1 & \omega_n^2 & \omega_n^4 & \ldots & \omega_n^{2(n-1)} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & \omega_n^{n-1} & \omega_n^{2(n-1)} & \ldots & \omega_n^{(n-1)(n-1)} \end{bmatrix} \begin{bmatrix}a_0 \\ a_1 \\ a_2 \\ \vdots \\ a_{n-1}\end{bmatrix} = \begin{bmatrix}A(\omega_n^0) \\ A(\omega_n^1) \\ A(\omega_n^2) \\ \vdots \\ A(\omega_n^{n-1})\end{bmatrix} ⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ 1 1 1 ⋮ 1 1 ω n 1 ω n 2 ⋮ ω n n − 1 1 ω n 2 ω n 4 ⋮ ω n 2 ( n − 1 ) … … … ⋱ … 1 ω n n − 1 ω n 2 ( n − 1 ) ⋮ ω n ( n − 1 ) ( n − 1 ) ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤ ⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ a 0 a 1 a 2 ⋮ a n − 1 ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤ = ⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ A ( ω n 0 ) A ( ω n 1 ) A ( ω n 2 ) ⋮ A ( ω n n − 1 ) ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤
观察其中与a k a_k a k 相关的列:
[ 1 ω n k ω n 2 k ⋮ ω n ( n − 1 ) k ] \begin{bmatrix} 1 \\ \omega_n^k \\ \omega_n^{2k} \\ \vdots \\ \omega_n^{(n-1)k} \end{bmatrix} ⎣ ⎢ ⎢ ⎢ ⎢ ⎢ ⎢ ⎡ 1 ω n k ω n 2 k ⋮ ω n ( n − 1 ) k ⎦ ⎥ ⎥ ⎥ ⎥ ⎥ ⎥ ⎤
我们可以将这个列向量与A ( ω n j ) A(\omega_n^j) A ( ω n j ) 的行向量进行点积:
∑ j = 0 n − 1 A ( ω n j ) ⋅ ω n − j k = A ( ω n 0 ) ⋅ 1 + A ( ω n 1 ) ⋅ ω n − k + A ( ω n 2 ) ⋅ ω n − 2 k + … + A ( ω n n − 1 ) ⋅ ω n − ( n − 1 ) k = a 0 ∑ j = 0 n − 1 ω n 0 + a 1 ∑ j = 0 n − 1 ω n j + a 2 ∑ j = 0 n − 1 ω n 2 j + … + a n − 1 ∑ j = 0 n − 1 ω n ( n − 1 ) j \begin{aligned} \sum_{j=0}^{n-1} A(\omega_n^j) \cdot \omega_n^{-jk} &= A(\omega_n^0) \cdot 1 + A(\omega_n^1) \cdot \omega_n^{-k} + A(\omega_n^2) \cdot \omega_n^{-2k} + \ldots + A(\omega_n^{n-1}) \cdot \omega_n^{-(n-1)k} \\ &= a_0 \sum_{j=0}^{n-1} \omega_n^{0} + a_1 \sum_{j=0}^{n-1} \omega_n^{j} + a_2 \sum_{j=0}^{n-1} \omega_n^{2j} + \ldots + a_{n-1} \sum_{j=0}^{n-1} \omega_n^{(n-1)j} \end{aligned} j = 0 ∑ n − 1 A ( ω n j ) ⋅ ω n − j k = A ( ω n 0 ) ⋅ 1 + A ( ω n 1 ) ⋅ ω n − k + A ( ω n 2 ) ⋅ ω n − 2 k + … + A ( ω n n − 1 ) ⋅ ω n − ( n − 1 ) k = a 0 j = 0 ∑ n − 1 ω n 0 + a 1 j = 0 ∑ n − 1 ω n j + a 2 j = 0 ∑ n − 1 ω n 2 j + … + a n − 1 j = 0 ∑ n − 1 ω n ( n − 1 ) j
由于单位根的性质:
当k = 0 k = 0 k = 0 时,∑ j = 0 n − 1 ω n 0 = 1 + 1 + … + 1 = n \sum_{j=0}^{n-1} \omega_n^{0} = 1 + 1 + \ldots + 1 = n ∑ j = 0 n − 1 ω n 0 = 1 + 1 + … + 1 = n 。 当k ≠ 0 k \neq 0 k = 0 时,∑ j = 0 n − 1 ω n j k = ω n 0 + ω n k + ω n 2 k + … + ω n ( n − 1 ) k \sum_{j=0}^{n-1} \omega_n^{jk} = \omega_n^{0} + \omega_n^{k} + \omega_n^{2k} + \ldots + \omega_n^{(n-1)k} ∑ j = 0 n − 1 ω n j k = ω n 0 + ω n k + ω n 2 k + … + ω n ( n − 1 ) k ,无论k k k 的值是多少,这都是所有单位根的一种排列,等于ω n 0 + ω n 1 + ω n 2 + … + ω n n − 1 = 0 \omega_n^{0} + \omega_n^{1} + \omega_n^{2} + \ldots + \omega_n^{n-1} = 0 ω n 0 + ω n 1 + ω n 2 + … + ω n n − 1 = 0 。 因此,只有当k = 0 k = 0 k = 0 时,才会有非零的贡献:
∑ j = 0 n − 1 A ( ω n j ) ⋅ ω n − j k = a k ⋅ n \sum_{j=0}^{n-1} A(\omega_n^j) \cdot \omega_n^{-jk} = a_k \cdot n j = 0 ∑ n − 1 A ( ω n j ) ⋅ ω n − j k = a k ⋅ n
因此,我们可以得到
a k = 1 n ∑ j = 0 n − 1 A ( ω n j ) ⋅ ω n − j k a_k = \frac{1}{n} \sum_{j=0}^{n-1} A(\omega_n^j) \cdot \omega_n^{-jk} a k = n 1 j = 0 ∑ n − 1 A ( ω n j ) ⋅ ω n − j k
再观察这个公式,我们发现它与 DFT 的公式 $$A(\omega_n^k) = \sum_{j=0}^{n-1} a_j \cdot \omega_n^{jk}$$
的结构非常相似;唯一的区别是ω n j k \omega_n^{jk} ω n j k 变成了ω n − j k \omega_n^{-jk} ω n − j k ,以及前面多了一个1 n \frac{1}{n} n 1 的系数。
因此,我们可以通过相同的分治方法来计算 IDFT。
可能你发现了,以上思路和计算神经网络的输出层使用 softmax、误差使用 cross-entropy 时,计算∂ L ∂ w i j \frac{\partial L}{\partial w_{ij}} ∂ w i j ∂ L 的思路有点共通之处,最终只有i = k i = k i = k 的项会有非零的贡献。这玩意学名叫做克罗内克 delta 函数,反映的是函数的正交性。
除此之外类似的,就是有不止一种方法推导,以下是另一种:
要证明a k = 1 n ∑ j = 0 n − 1 A ( ω n j ) ⋅ ω n − j k a_k = \frac{1}{n} \sum_{j=0}^{n-1} A(\omega_n^j) \cdot \omega_n^{-jk} a k = n 1 ∑ j = 0 n − 1 A ( ω n j ) ⋅ ω n − j k ,我们可以将A ( ω n j ) A(\omega_n^j) A ( ω n j ) 的定义代入:
S = 1 n ∑ j = 0 n − 1 A ( ω n j ) ⋅ ω n − j k = 1 n ∑ j = 0 n − 1 ( ∑ m = 0 n − 1 a m ⋅ ω n j m ) ⋅ ω n − j k = 1 n ∑ m = 0 n − 1 a m ( ∑ j = 0 n − 1 ω n j ( m − k ) ) \begin{aligned} S &= \frac{1}{n} \sum_{j=0}^{n-1} A(\omega_n^j) \cdot \omega_n^{-jk} \\ &= \frac{1}{n} \sum_{j=0}^{n-1} \left( \sum_{m=0}^{n-1} a_m \cdot \omega_n^{jm} \right) \cdot \omega_n^{-jk} \\ &= \frac{1}{n} \sum_{m=0}^{n-1} a_m \left( \sum_{j=0}^{n-1} \omega_n^{j(m-k)} \right) \end{aligned} S = n 1 j = 0 ∑ n − 1 A ( ω n j ) ⋅ ω n − j k = n 1 j = 0 ∑ n − 1 ( m = 0 ∑ n − 1 a m ⋅ ω n j m ) ⋅ ω n − j k = n 1 m = 0 ∑ n − 1 a m ( j = 0 ∑ n − 1 ω n j ( m − k ) )
当m = k m = k m = k 时,∑ j = 0 n − 1 ω n 0 = n \sum_{j=0}^{n-1} \omega_n^{0} = n ∑ j = 0 n − 1 ω n 0 = n 。 当m ≠ k m \neq k m = k 时,∑ j = 0 n − 1 ω n j ( m − k ) = ω n 0 + ω n m − k + ω n 2 ( m − k ) + … + ω n ( n − 1 ) ( m − k ) \sum_{j=0}^{n-1} \omega_n^{j(m-k)} = \omega_n^{0} + \omega_n^{m-k} + \omega_n^{2(m-k)} + \ldots + \omega_n^{(n-1)(m-k)} ∑ j = 0 n − 1 ω n j ( m − k ) = ω n 0 + ω n m − k + ω n 2 ( m − k ) + … + ω n ( n − 1 ) ( m − k ) ,和上面分析的一样,结果为0 0 0 。 所以,
S = 1 n ∑ m = 0 n − 1 a m ⋅ { n if m = k , 0 if m ≠ k . S = \frac{1}{n} \sum_{m=0}^{n-1} a_m \cdot \begin{cases} n & \text{if } m = k, \\ 0 & \text{if } m \neq k. \end{cases} S = n 1 m = 0 ∑ n − 1 a m ⋅ { n 0 if m = k , if m = k .
因此,S = a k ⋅ n S = a_k \cdot n S = a k ⋅ n ,从而得到:
a k = 1 n ∑ j = 0 n − 1 A ( ω n j ) ⋅ ω n − j k a_k = \frac{1}{n} \sum_{j=0}^{n-1} A(\omega_n^j) \cdot \omega_n^{-jk} a k = n 1 j = 0 ∑ n − 1 A ( ω n j ) ⋅ ω n − j k
实现不过实际上,众所周知,递归的开销比较大,因此我们通常会使用迭代的方式来实现 FFT 和 IFFT。迭代的核心思想是先进行位逆序 (bit-reversal)排列,然后在每一轮迭代中,按照当前的子问题大小进行合并。
原索引 二进制 逆序二进制 逆序索引 0 000 000 0 1 001 100 4 2 010 010 2 3 011 110 6 4 100 001 1 5 101 101 5 6 110 011 3 7 111 111 7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 #include <iostream> #include <vector> #include <complex> #include <cmath> using namespace std;using cd = complex<double >;using int64 = long long ;const double PI = acos (-1 );void fft (vector<cd> &a, bool invert) { int n = a.size (); for (int i = 1 , j = 0 ; i < n; i++) { int bit = n >> 1 ; for (; j & bit; bit >>= 1 ) j ^= bit; j |= bit; if (i < j) swap (a[i], a[j]); } for (int len = 2 ; len <= n; len <<= 1 ) { double angle = 2 * PI / len * (invert ? -1 : 1 ); cd wlen (cos(angle), sin(angle)) ; for (int i = 0 ; i < n; i += len) { cd w (1 ) ; for (int j = 0 ; j < len / 2 ; j++) { cd u = a[i + j], v = a[i + j + len / 2 ] * w; a[i + j] = u + v; a[i + j + len / 2 ] = u - v; w *= wlen; } } } if (invert) { for (cd &x : a) x /= n; } } vector<int64> multiply (const vector<int64> &a, const vector<int64> &b) { vector<cd> fa (a.begin(), a.end()) , fb (b.begin(), b.end()) ; int n = 1 ; while (n < a.size () + b.size ()) n <<= 1 ; fa.resize (n); fb.resize (n); fft (fa, false ); fft (fb, false ); for (int i = 0 ; i < n; i++) fa[i] *= fb[i]; fft (fa, true ); vector<int64> result (n) ; for (int i = 0 ; i < n; i++) result[i] = round (fa[i].real ()); return result; } int main () { vector<int64> a = {1 , 2 , 3 }; vector<int64> b = {4 , 5 }; vector<int64> result = multiply (a, b); for (int64 coeff : result) cout << coeff << " " ; cout << endl; }