问题:给定两个大整数 A A A 和 B B B, A A A 和 B B B 的长度为 n n n 和 m m m,求 A A A 和 B B B 的乘积
1. 朴素做法
思考小学数学中两个数的乘法列竖式的方式,对于 B B B 的每一位,分别乘上 A A A 的每一位,得到的结果相加。
以 23 × 456 23\times 456 23×456 为例:
23 × 456 = 6 × 3 + 6 × 20 + 50 × 3 + 50 × 20 + 400 × 3 + 400 × 20 = 18 + 120 + 150 + 1000 + 1200 + 8000 = 10488 \begin{aligned} 23\times 456 &= 6\times 3 + 6\times 20+50\times 3+50\times 20+400\times 3+400\times 20\\ &= 18+120+150+1000+1200+8000 \\ &= 10488 \end{aligned} 23×456=6×3+6×20+50×3+50×20+400×3+400×20=18+120+150+1000+1200+8000=10488
如此需要进行 2 × 3 2\times 3 2×3 次乘法以及 2 × 3 − 1 2\times 3 - 1 2×3−1 次加法。
对于到长度为 n n n 的数 A A A 和长度为 m m m 的数 B B B ,则需要进行 n × m n\times m n×m 次乘法和 n × m − 1 n\times m-1 n×m−1 次加法
如此时间复杂度为: O ( n m ) O(nm) O(nm),不妨设 m ≤ n m\leq n m≤n,则时间复杂度为 O ( n 2 ) O(n^2) O(n2)
2. 快速傅里叶变换
上述的朴素做法时间复杂度过高,当两者长度都达到 1 0 5 10^5 105 级别时,就很难快速得到两个大整数的乘积了。
2.1 求解思想
2.1.1 大整数转换为多项式
考虑将两个大整数转换成多项式:
令 A ( x ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a n − 1 x n − 1 A(x)=a_0+a_1x+a_2x^2+\cdots+a_{n-1}x^{n-1} A(x)=a0+a1x+a2x2+⋯+an−1xn−1, B ( x ) = b 0 + b 1 x + b 2 x 2 + ⋯ + b m − 1 x m − 1 B(x)=b_0+b_1x+b_2x^2+\cdots+b_{m-1}x^{m-1} B(x)=b0+b1x+b2x2+⋯+bm−1xm−1
以上多项式的表示称为多项式的系数表示法
以 23 × 456 23\times 456 23×456 为例:
当 a 0 = 3 , a 1 = 2 a_0=3,a_1=2 a0=3,a1=2, A ( x ) = 3 + 2 x A(x)=3+2x A(x)=3+2x
当 b 0 = 6 , b 1 = 5 , b 2 = 4 , B ( x ) = 6 + 5 x + 4 x 2 b_0=6,b_1=5,b_2=4, B(x)=6+5x+4x^2 b0=6,b1=5,b2=4,B(x)=6+5x+4x2
可以看出:
23 23 23 可以转换为 a 0 = 3 , a 1 = 2 a_0=3,a_1=2 a0=3,a1=2 的多项式, A ( 10 ) = 23 A(10)=23 A(10)=23
456 456 456 可以转换为 b 0 = 6 , b 1 = 5 , b 2 = 4 b_0=6,b_1=5,b_2=4 b0=6,b1=5,b2=4 的多项式,当 B ( 10 ) = 456 B(10)=456 B(10)=456
那么 A ( 10 ) × B ( 10 ) A(10)\times B(10) A(10)×B(10) 即为 23 × 456 23\times 456 23×456 的结果
问题转换成了求解两个多项式 A ( x ) A(x) A(x) 与 B ( x ) B(x) B(x) 的乘积
令 C ( x ) = A ( x ) × B ( x ) = c 0 + c 1 x + c 2 x 2 + ⋯ + c n + m − 2 x n + m − 2 C(x)=A(x)\times B(x) = c_0+c_1x+c_2x^2+\cdots + c_{n+m-2}x^{n+m-2} C(x)=A(x)×B(x)=c0+c1x+c2x2+⋯+cn+m−2xn+m−2
则 c k = ∑ i + j = k a i × b j c_k=\sum\limits_{i+j=k} a_i\times b_{j} ck=i+j=k∑ai×bj
求 c k c_k ck 的过程也称为卷积,整体的时间复杂度也为 O ( n 2 ) O(n^2) O(n2)
2.1.2 多项式的点值表示法
上述将大整数转换为多项式的系数表示法进行多项式的计算,时间复杂度仍未改进。
事实上,多项式还有另外一种表示法,即点值表示法。
我们思考一条直线 y = k x + b y=kx+b y=kx+b,如果转换为上述表达,即为: A ( x ) = a 0 + a 1 x A(x)=a_0+a_1x A(x)=a0+a1x,其中 a 0 = b , a 1 = k a_0=b,a_1=k a0=b,a1=k。而两点确定一条直线,则我们如果找出两个不同点,计算出斜率 k k k 和截距 b b b ,则唯一确定一条直线,即唯一确定一个一阶多项式。
推广到更高阶的多项式,对于 n n n 阶多项式 A ( x ) = ∑ i = 0 n a i x i A(x)=\sum\limits_{i=0}^n a_ix^i A(x)=i=0∑naixi,如果找到 n + 1 n+1 n+1 个不同点,则可以唯一确定一个 n n n 阶多项式。二维平面上 n + 1 n+1 n+1 个不同点可以唯一确定一个 n n n 阶多项式)。
对于多项式 A ( x ) A(x) A(x),其点值表示法为: { ( x 0 , A ( x 0 ) ) , ( x 1 , ( A ( x 1 ) ) , ⋯ , ( x n , A ( x n ) ) } \{(x_0,A(x_0)),(x_1,(A(x_1)),\cdots,(x_n,A(x_n))\} {(x0,A(x0)),(x1,(A(x1)),⋯,(xn,A(xn))}
要证明 n n n 阶多项式可以由 n + 1 n+1 n+1 个点唯一确定,只需要证明这 n + 1 n+1 n+1 个系数是唯一的
对于 A ( x ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a n x n A(x)=a_0+a_1x+a_2x^2+\cdots+a_nx^n A(x)=a0+a1x+a2x2+⋯+anxn
将 n + 1 n+1 n+1 个点带入多项式,得到 n + 1 n+1 n+1 个方程
A ( x 0 ) = a 0 + a 1 x 0 + a 2 x 0 2 + ⋯ + a n x 0 n A(x_0)=a_0+a_1x_0+a_2x_0^2+\cdots+a_nx_0^n A(x0)=a0+a1x0+a2x02+⋯+anx0n
A ( x 1 ) = a 0 + a 1 x 1 + a 2 x 1 2 + ⋯ + a n x 1 n A(x_1)=a_0+a_1x_1+a_2x_1^2+\cdots+a_nx_1^n A(x1)=a0+a1x1+a2x12+⋯+anx1n
⋯ \cdots ⋯
A ( x n ) = a 0 + a 1 x n + a 2 x n 2 + ⋯ + a n x n n A(x_n)=a_0+a_1x_n+a_2x_n^2+\cdots+a_nx_n^n A(xn)=a0+a1xn+a2xn2+⋯+anxnn
写成矩阵形式:
[ A ( x 0 ) A ( x 1 ) ⋮ A ( x d ) ] = [ 1 x 0 x 0 2 ⋯ x 0 n 1 x 1 x 1 2 ⋯ x 1 n ⋮ ⋮ ⋮ ⋱ ⋮ 1 x n x n 2 ⋯ x n n ] [ a 0 a 1 ⋮ a n ] \left[\begin{array}{c} A\left(x_{0}\right) \\ A\left(x_{1}\right) \\ \vdots \\ A\left(x_{d}\right) \end{array}\right]=\left[\begin{array}{ccccc} 1 & x_{0} & x_{0}^{2} & \cdots & x_{0}^{n} \\ 1 & x_{1} & x_{1}^{2} & \cdots & x_{1}^{n} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 1 & x_{n} & x_{n}^{2} & \cdots & x_{n}^{n} \end{array}\right]\left[\begin{array}{c} a_{0} \\ a_{1} \\ \vdots \\ a_{n} \end{array}\right] A(x0)A(x1)⋮A(xd) = 11⋮1x0x1⋮xnx02x12⋮xn2⋯⋯⋱⋯x0nx1n⋮xnn a0a1⋮an
令方阵为矩阵 M M M , M M M 也称为范德蒙德矩阵, x 0 , x 1 , ⋯ , x n x_0,x_1,\cdots,x_n x0,x1,⋯,xn 两两不同。证明 n + 1 n+1 n+1 个系数唯一只需要证明 M M M 的行列式不为 0 0 0 即可,而范德蒙德行列式的值一定不为 0 0 0,故得证二维平面上 n + 1 n+1 n+1 个不同点可以唯一确定一个 n n n 阶多项式
至此,对于对于 n n n 阶多项式 A ( x ) = ∑ i = 0 n a i x i A(x)=\sum\limits_{i=0}^n a_ix^i A(x)=i=0∑naixi,我们有两种多项式表示法:
- 系数表示法,给出 n + 1 n+1 n+1 个系数
[ a 0 , a 1 , ⋯ , a n ] [a_0,a_1,\cdots,a_n] [a0,a1,⋯,an]
-
点值表示法,给出 n + 1 n+1 n+1 个点
{ ( x 0 , A ( x 0 ) ) , ( x 1 , ( A ( x 1 ) ) , ⋯ , ( x n , A ( x n ) ) } \{(x_0,A(x_0)),(x_1,(A(x_1)),\cdots,(x_n,A(x_n))\} {(x0,A(x0)),(x1,(A(x1)),⋯,(xn,A(xn))}
2.1.3 多项式乘积的计算流程
对于两个多项式, A ( x ) = ∑ i = 0 n a i x i A(x)=\sum\limits_{i=0}^n a_ix^i A(x)=i=0∑naixi 和 B ( x ) = ∑ i = 0 m b i x i B(x)=\sum\limits_{i=0}^m b_ix^i B(x)=i=0∑mbixi
两个多项式的乘积为 C ( x ) = A ( x ) ⋅ B ( x ) = ∑ i = 0 m + n c i x i C(x)=A(x)\cdot B(x)=\sum\limits_{i=0}^{m+n}c_ix^i C(x)=A(x)⋅B(x)=i=0∑m+ncixi 共有 n + m + 1 n+m+1 n+m+1 项
因此对于多项式 A ( x ) A(x) A(x) 和 B ( x ) B(x) B(x) 各取 n + m + 1 n+m+1 n+m+1 个点
对于 A ( x ) A(x) A(x),取点为: { ( x 0 , A ( x 0 ) ) , ( x 1 , ( A ( x 1 ) ) , ⋯ , ( x n + m , A ( x n + m ) ) } \{(x_0,A(x_0)),(x_1,(A(x_1)),\cdots,(x_{n+m},A(x_{n+m}))\} {(x0,A(x0)),(x1,(A(x1)),⋯,(xn+m,A(xn+m))}
对于 B ( x ) B(x) B(x),取点为: { ( x 0 , B ( x 0 ) ) , ( x 1 , ( B ( x 1 ) ) , ⋯ , ( x n + m , B ( x n + m ) ) } \{(x_0,B(x_0)),(x_1,(B(x_1)),\cdots,(x_{n+m},B(x_{n+m}))\} {(x0,B(x0)),(x1,(B(x1)),⋯,(xn+m,B(xn+m))}
得到的 C ( x ) C(x) C(x) 的 n + m + 1 n+m+1 n+m+1 个点为:
{ ( x 0 , A ( x 0 ) × B ( x 0 ) ) , ( x 1 , ( A ( x 1 ) × B ( x 1 ) ) , ⋯ , ( x n + m , A ( x n + m × B ( x n + m ) ) ) } \{(x_0,A(x_0)\times B(x_0)),(x_1,(A(x_1)\times B(x_1)),\cdots,(x_{n+m},A(x_{n+m}\times B(x_{n+m})))\} {(x0,A(x0)×B(x0)),(x1,(A(x1)×B(x1)),⋯,(xn+m,A(xn+m×B(xn+m)))}
该乘积的时间复杂度为: O ( n ) O(n) O(n)
如何得到 C ( x ) C(x) C(x) 的系数表示法呢?
- 将 A ( x ) A(x) A(x) 和 B ( x ) B(x) B(x) 从系数表示法转换成点值表示法,在 A ( x ) A(x) A(x) 和 B ( x ) B(x) B(x) 中各取 n + m + 1 n+m+1 n+m+1 个点, 这 n + m + 1 n+m+1 n+m+1 个点的横坐标对应相同
- O ( n ) O(n) O(n) 时间计算出每个点的纵坐标乘积,即得到表示 C ( x ) C(x) C(x) 的 n + m + 1 n+m+1 n+m+1 个点
- 将 C ( x ) C(x) C(x) 从点值表示法转换成系数表示法
2.2 求解流程
2.2.1 从系数表示到点值表示(FFT)
朴素做法
在 A ( x ) A(x) A(x) 和 B ( x ) B(x) B(x) 中各取 n + m + 1 n+m+1 n+m+1 个点,对于每个点,选择其横坐标后,通过系数表示法计算出其对应的纵坐标,需要的时间复杂度为 O ( n ) O(n) O(n),总时间复杂度为 O ( n 2 ) O(n^2) O(n2),因此时间复杂度不满足要求。
快速傅立叶变换(Fast Fourier Transform)是一种快速进行离散傅立叶变换的方法, 通过单位根的性质, 将 n n n 次求值的时间复杂度降为 O ( n log n ) O(n \log n) O(nlogn)
F F T \mathrm{FFT} FFT 的 n n n 保证为 2 2 2 的幂
A ( x ) → a = [ a 0 , a 1 , a 2 , ⋯ , a n − 1 ] ⊤ A(x) \rightarrow a=\left[a_{0}, a_{1}, a_{2}, \cdots, a_{n-1}\right]^{\top} A(x)→a=[a0,a1,a2,⋯,an−1]⊤ , 将其分为偶数项和奇数项两个向量
偶 a [ 0 ] = [ a 0 , a 2 , ⋯ , a n − 2 ] 奇 a [ 1 ] = [ a 1 , a 3 , ⋯ , a n − 1 ] 偶 a^{[0]}=\left[a_{0}, a_{2}, \cdots, a_{n-2}\right]\\ 奇 a^{[1]}=\left[a_{1}, a_{3}, \cdots, a_{n-1}\right] 偶a[0]=[a0,a2,⋯,an−2]奇a[1]=[a1,a3,⋯,an−1]
A ( x ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a n − 1 x n − 1 A [ 0 ] ( x ) = a 0 + a 2 x + a 4 x 2 + ⋯ + a n − 2 x n 2 − 1 A [ 1 ] ( x ) = a 1 + a 3 x + a 5 x 2 + ⋯ + a n − 1 x n 2 − 1 \begin{array}{l} A(x)=a_{0}+a_{1} x+a_{2} x^{2}+\cdots+a_{n-1}^{x} n-1 \\ A^{[0]}(x)=a_{0}+a_{2} x+a_{4} x^{2}+\cdots+a_{n-2} x^{\frac{n}{2}-1} \\ A^{[1]}(x)=a_{1}+a_{3} x+a_{5} x^{2}+\cdots+a_{n-1} x^{\frac{n}{2}-1} \end{array} A(x)=a0+a1x+a2x2+⋯+an−1xn−1A[0](x)=a0+a2x+a4x2+⋯+an−2x2n−1A[1](x)=a1+a3x+a5x2+⋯+an−1x2n−1
A [ 0 ] ( x 2 ) = a 0 + a 2 x 2 + a 4 x 4 + ⋯ + a n − 2 x n − 2 A [ 1 ] ( x 2 ) = a 1 + a 3 x 2 + a 5 x 4 + ⋯ + a n − 1 x n − 2 x A [ 1 ] ( x 2 ) = a 1 x + a 3 x 3 + a 5 x 5 + ⋯ + a n − 1 x n − 1 \begin{array}{l} A^{[0]}\left(x^{2}\right)=a_{0}+a_{2} x^{2}+a_{4} x^{4}+\cdots+a_{n-2} x^{n-2} \\ A^{[1]}\left(x^{2}\right)=a_{1}+a_{3} x^{2}+a_{5} x^{4}+\cdots+a_{n-1} x^{n-2} \\ x A^{[1]}\left(x^{2}\right)=a_{1} x+a_{3} x^{3}+a_{5} x^{5}+\cdots+a_{n-1} x^{n-1} \end{array} A[0](x2)=a0+a2x2+a4x4+⋯+an−2xn−2A[1](x2)=a1+a3x2+a5x4+⋯+an−1xn−2xA[1](x2)=a1x+a3x3+a5x5+⋯+an−1xn−1
那么有: $A(x)=A{[0]}\left(x{2}\right)+x A{[1]}\left(x{2}\right) $
原问题: 求 A ( x ) A(x) A(x) 在每个单位根上的值
转化为: 求次数界为 n 2 \frac{n}{2} 2n 的两个多项式 A [ 0 ] ( x ) A^{[0]}(x) A[0](x) 和 A [ 1 ] ( x ) A^{[1]}(x) A[1](x) 在每个单位根平方上的值, 再合并。
A ( w n k ) = A [ 0 ] ( ( w n k ) 2 ) + w n k A [ 1 ] ( ( w n k ) 2 ) A ( ω n k + n 2 ) = A [ 0 ] ( ( ω n k + n 2 ) 2 ) + ω n k + n 2 A [ 1 ] ( ( ω n k + n 2 ) 2 ) \begin{array}{l} A\left(w_{n}^{k}\right)=A^{[0]}\left(\left(w_{n}^{k}\right)^{2}\right)+w_{n}^{k} A^{[1]}\left(\left(w_{n}^{k}\right)^{2}\right) \\ A\left(\omega_{n}^{k+\frac{n}{2}}\right)=A^{[0]}\left(\left(\omega_{n}^{k+\frac{n}{2}}\right)^{2}\right)+\omega_{n}^{k+\frac{n}{2}} A^{[1]}\left(\left(\omega_{n}^{k+\frac{n}{2}}\right)^{2}\right) \end{array} A(wnk)=A[0]((wnk)2)+wnkA[1]((wnk)2)A(ωnk+2n)=A[0]((ωnk+2n)2)+ωnk+2nA[1]((ωnk+2n)2)
通过消去引理和折半引理, 将上式化简为:
A ( ω n k ) = A [ 0 ] ( ω n 2 k ) + ω n k A [ 1 ] ( ω n 2 k ) A ( ω n k + n 2 ) = A [ 0 ] ( ω n 2 k ) − ω n k A [ 1 ] ( ω n 2 k ) \begin{array}{ll} & A\left(\omega_{n}^{k}\right)=A^{[0]}\left(\omega_{\frac{n}{2}}^{k}\right)+\omega_{n}^{k} A^{[1]}\left(\omega_{\frac{n}{2}}^{k}\right) \\ & A\left(\omega_{n}^{k+\frac{n}{2}}\right)=A^{[0]}\left(\omega_{\frac{n}{2}}^{k}\right)-\omega_{n}^{k} A^{[1]}\left(\omega_{\frac{n}{2}}^{k}\right) \end{array} A(ωnk)=A[0](ω2nk)+ωnkA[1](ω2nk)A(ωnk+2n)=A[0](ω2nk)−ωnkA[1](ω2nk)
A [ 0 ] ( x ) A^{[0]}(x) A[0](x) 和 A [ 1 ] ( x ) A^{[1]}(x) A[1](x) 恰好均为次数界为 n 2 \frac{n}{2} 2n 的多项式
DFT n ( a ) = { D F T n 2 ( a [ 0 ] ) DFT n 2 ( a [ 1 ] ) \operatorname{DFT}_{n}(a)=\left\{\begin{array}{l} \mathrm{DFT}_{\frac{n}{2}}\left(a^{[0]}\right) \\ \operatorname{DFT}_{\frac{n}{2}}\left(a^{[1]}\right) \end{array}\right. DFTn(a)={DFT2n(a[0])DFT2n(a[1])
所以问题又转化为了求 次数界为 n 2 \frac{n}{2} 2n 的多项式 A [ 0 ] ( x ) A^{[0]}(x) A[0](x)和 A [ 1 ] ( x ) A^{[1]}(x) A[1](x)
这两个问题和原问题的描述类似, 原问题的次数界为 n n n , 子问题的次数界为 n 2 \frac{n}{2} 2n , 所以这两个问题是范围缩小一 半的两个子问题, 通过同样的方式求解这两个子问题, 那么就是熟悉的递归求解了。再根据折半引理, 就可以快速 地合并结果。
T ( n ) = 2 T ( n 2 ) + O ( n ) T(n)=2 T\left(\frac{n}{2}\right)+O(n) T(n)=2T(2n)+O(n)
根据主定理, 时间复杂度为: O ( n log n ) O(n \log n) O(nlogn)
2.2.2 点值表示的乘积
对于 A ( x ) A(x) A(x),取点为: { ( x 0 , A ( x 0 ) ) , ( x 1 , ( A ( x 1 ) ) , ⋯ , ( x n + m , A ( x n + m ) ) } \{(x_0,A(x_0)),(x_1,(A(x_1)),\cdots,(x_{n+m},A(x_{n+m}))\} {(x0,A(x0)),(x1,(A(x1)),⋯,(xn+m,A(xn+m))}
对于 B ( x ) B(x) B(x),取点为: { ( x 0 , B ( x 0 ) ) , ( x 1 , ( B ( x 1 ) ) , ⋯ , ( x n + m , B ( x n + m ) ) } \{(x_0,B(x_0)),(x_1,(B(x_1)),\cdots,(x_{n+m},B(x_{n+m}))\} {(x0,B(x0)),(x1,(B(x1)),⋯,(xn+m,B(xn+m))}
得到的 C ( x ) C(x) C(x) 的 n + m + 1 n+m+1 n+m+1 个点为:
{ ( x 0 , A ( x 0 ) × B ( x 0 ) ) , ( x 1 , ( A ( x 1 ) × B ( x 1 ) ) , ⋯ , ( x n + m , A ( x n + m × B ( x n + m ) ) ) } \{(x_0,A(x_0)\times B(x_0)),(x_1,(A(x_1)\times B(x_1)),\cdots,(x_{n+m},A(x_{n+m}\times B(x_{n+m})))\} {(x0,A(x0)×B(x0)),(x1,(A(x1)×B(x1)),⋯,(xn+m,A(xn+m×B(xn+m)))}
该乘积的时间复杂度为: O ( n ) O(n) O(n)
2.2.3 从点值表示到系数表示(IFFT)
朴素做法
已知 n + 1 n+1 n+1 个点组成的多项式点值表示 { x 0 , A ( x 0 ) , ( x 1 , A ( x 1 ) ) , … , ( x n , A ( x n ) ) } \{x_0,A(x_0),(x_1,A(x_1)),\ldots,(x_n,A(x_n))\} {x0,A(x0),(x1,A(x1)),…,(xn,A(xn))},求 A ( x ) A(x) A(x) 的过程叫作插值
拉格朗日插值公式计算: A ( x ) = ∑ i = 0 n A ( x i ) ∏ j ≠ i ( x − x j ) ∏ j ≠ i ( x i − x j ) A(x)=\sum\limits_{i=0}^n A(x_i)\frac{\prod\limits_{j\neq i}(x-x_j)}{\prod\limits_{j\neq i}(x_i-x_j)} A(x)=i=0∑nA(xi)j=i∏(xi−xj)j=i∏(x−xj)
时间复杂度: O ( n 2 ) O(n^2) O(n2)
顾名思义,快速傅里叶逆变换(Inverse Fast Fourier Transform)是快速傅里叶变换的逆。
D F T → y i = ∑ j = 0 n − 1 w n i j a j → y = V n a \mathrm{DFT} \rightarrow y_i=\sum\limits_{j=0}^{n-1}w_n^{ij}a_j\rightarrow y=V_n a DFT→yi=j=0∑n−1wnijaj→y=Vna
V n = [ 1 1 1 ⋯ 1 1 ω n ω 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 ) ] V_n=\begin{bmatrix} 1 & 1 & 1 & \cdots & 1 \\ 1 & \omega_n & \omega_n^2 & \cdots & \omega_n^{n-1} \\ 1 & \omega_n^2 & \omega_n^4 & \cdots & \omega_n^{2(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} Vn= 111⋮11ωnωn2⋮ωnn−11ωn2ωn4⋮ωn2(n−1)⋯⋯⋯⋱⋯1ωnn−1ωn2(n−1)⋮ωn(n−1)(n−1)
这里的 V n V_{n} Vn 是一个范德蒙德矩阵, 它的第 i i i 行第 j j j 列尾主 n n n 次单位根的 i j i j ij 次方
( V n ) i j = ω n i j \left(V_{n}\right)_{i j}=\omega_{n}^{i j} (Vn)ij=ωnij
离散傅立叶变换给出 a a a 求 y y y , 而离散傅立叶逆变换给出 y y y 求 a a a , 所以要用 V n V_{n} Vn 的逆矩阵乘向量 y y y , 即 a = V n − 1 y a=V_{n}^{-1} y a=Vn−1y , 其中
( V n − 1 ) i j = ω n − i j n \left(V_{n}^{-1}\right)_{i j}=\frac{\omega_{n}^{-i j}}{n} (Vn−1)ij=nωn−ij
( V n − 1 V n ) i j = ∑ k = 0 n − 1 ω n − k i n × ω n k j = ∑ k = 0 n − 1 ω n k ( j − i ) n \left(V_{n}^{-1} V_{n}\right)_{i j}=\sum_{k=0}^{n-1} \frac{\omega_{n}^{-k i}}{n} \times \omega_{n}^{k j}=\sum_{k=0}^{n-1} \frac{\omega_{n}^{k(j-i)}}{n} (Vn−1Vn)ij=∑k=0n−1nωn−ki×ωnkj=∑k=0n−1nωnk(j−i)
当且仅当 i = j i=j i=j 时, ( V n − 1 V n ) i j = 1 \left(V_{n}^{-1} V_{n}\right)_{i j}=1 (Vn−1Vn)ij=1 , 其余情况 ( V n − 1 V n ) i j = 0 \left(V_{n}^{-1} V_{n}\right)_{i j}=0 (Vn−1Vn)ij=0 , 所以结果是一个单位矩阵, 即 V n − 1 V n = I n V_{n}^{-1} V_{n}=I_{n} Vn−1Vn=In , 所以 ( V n − 1 ) i j = ω n − i j n \left(V_{n}^{-1}\right)_{i j}=\frac{\omega_{n}^{-i j}}{n} (Vn−1)ij=nωn−ij 得证
可以发现
I D F T → a i = V n − 1 y = ∑ j = 0 n − 1 ω n − i j n = 1 n ∑ j = 0 n − 1 ω n − i j y j D F T → y i = ∑ j = 0 n − 1 ω n i j a j \begin{array}{l} \mathrm{IDFT} \rightarrow a_{i}=V_{n}^{-1} y=\sum_{j=0}^{n-1} \frac{\omega_{n}^{-i j}}{n}=\frac{1}{n} \sum_{j=0}^{n-1} \omega_{n}^{-i j} y_{j} \\ \mathrm{DFT} \rightarrow y_{i}=\sum_{j=0}^{n-1} \omega_{n}^{i j} a_{j} \end{array} IDFT→ai=Vn−1y=∑j=0n−1nωn−ij=n1∑j=0n−1ωn−ijyjDFT→yi=∑j=0n−1ωnijaj
将 F F T \mathrm{FFT} FFT 中的 ω n = e 2 π n i = cos ( 2 π n ) + i sin ( 2 π n ) \omega_{n}=e^{\frac{2 \pi}{n} i}=\cos \left(\frac{2 \pi}{n}\right)+i \sin \left(\frac{2 \pi}{n}\right) ωn=en2πi=cos(n2π)+isin(n2π)
改成 ω n = e − 2 π n i = cos ( 2 π n ) − i sin ( 2 π n ) \omega_{n}=e^{-\frac{2 \pi}{n} i}=\cos \left(\frac{2 \pi}{n}\right)-i \sin \left(\frac{2 \pi}{n}\right) ωn=e−n2πi=cos(n2π)−isin(n2π)
如此, 对 F F T \mathrm{FFT} FFT 做部分修改即可由点值表示转换为系数表示,由于仅参数不同,故时间复杂度仍为 O ( n log n ) O(n\log n) O(nlogn)。
3. 快速数论变换
之前介绍了FFT,一种利用复数性质加速多项式乘积运算的算法
可以发现,在计算时,实部和虚部因为需要使用 c o s cos cos 和 s i n sin sin 以及 π \pi π,必须存储为浮点形式,复数的乘法运算也较为复杂。
计算机中的浮点数不免存在精度误差问题,所以我们考虑是否存在一种整数域内的形式,将复数转换为实数?
3.1 部分概念
3.1.1 欧拉函数
欧拉函数 φ ( n ) \varphi(n) φ(n) 表示小于等于 n n n 的数中与 n n n 互质的数的个数
3.1.2 欧拉定理
当 gcd ( a , n ) = 1 \operatorname{gcd}(a,n)=1 gcd(a,n)=1 ,存在 a φ ( n ) ≡ 1 ( m o d n ) a^{\varphi (n)} \equiv 1(\bmod\ n) aφ(n)≡1(mod n)
3.1.3 费马小定理
当 p p p 是质数,欧拉定理为 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1(\bmod\ p) ap−1≡1(mod p)
费马小定理就是欧拉定理的特殊形式
3.1.4 阶
由欧拉定理可知, 对 a ∈ Z , m ∈ N ∗ a \in \mathbb{Z}, m \in \mathbb{N}^{*} a∈Z,m∈N∗ , 若 gcd ( a , m ) = 1 \operatorname{gcd}(a, m)=1 gcd(a,m)=1 , 则 a φ ( m ) ≡ 1 ( m o d m ) a^{\varphi(m)} \equiv 1(\bmod m) aφ(m)≡1(modm) 。因此满足同余式 a n ≡ 1 ( m o d m ) a^{n} \equiv 1(\bmod m) an≡1(modm) 的最小正整数 n n n 存在, 这个 n n n 称作 a a a 模 m m m 的阶, 记作 δ m ( a ) \delta_{m}(a) δm(a)
3.1.5 原根
当 a a a 模 m m m 的阶为 φ ( m ) \varphi(m) φ(m) ,即 δ m ( a ) = φ ( m ) \delta_{m}(a)=\varphi(m) δm(a)=φ(m) ,则称 a a a 为模 m m m 的原根
3.2 用原根代替复数根
复数根: ω n k = ( e 2 π n i ) k \omega_{n}^k=(e^{\frac{2\pi}{n}i})^k ωnk=(en2πi)k
用原根来替代复数根的每个实数根: g n k = ( G p − 1 n ) k m o d p g_n^k=(G^{\frac{p-1}{n}})^k \bmod \ p gnk=(Gnp−1)kmod p
对原根而言:
g n n = g p − 1 = g φ ( p ) = 1 ≡ ( m o d p ) g n n / 2 = g ( p − 1 ) / 2 \begin{array}{l} g_{n}^{n}=g^{p-1}=g^{\varphi(p)}=1 \equiv(\bmod p) \\ g_{n}^{n / 2}=g^{(p-1) / 2} \end{array} gnn=gp−1=gφ(p)=1≡(modp)gnn/2=g(p−1)/2
而:
( g ( p − 1 ) / 2 ) 2 ≡ g p − 1 ≡ 1 ( m o d p ) \left(g^{(p-1) / 2}\right)^{2} \equiv g^{p-1} \equiv 1(\bmod p) (g(p−1)/2)2≡gp−1≡1(modp)
即求方程 x 2 ≡ 1 ( m o d p ) x^{2} \equiv 1(\bmod p) x2≡1(modp) , 在 p p p 为素数时只有 ± 1 \pm 1 ±1 两种取值。由于 g g g 是原根, 只有 g n n ≡ 1 ( m o d p ) g_{n}^{n} \equiv 1(\bmod p) gnn≡1(modp) , 故 g ( p − 1 ) / 2 ≡ − 1 ( m o d p ) g^{(p-1) / 2} \equiv-1(\bmod p) g(p−1)/2≡−1(modp)
原根满足:
- 具有周期性
- 一个周期内的数均不相同
- g n k = − g n k + n / 2 g_{n}^{k}=-g_{n}^{k+n / 2} gnk=−gnk+n/2
如此我们就找到了一个可以替换复数根的实数根,常用原根模数 9982442353 9982442353 9982442353,原根为 3 3 3,因为 998244352 = 2 23 ⋅ 7 ⋅ 17 998244352=2^{23}\cdot 7 \cdot 17 998244352=223⋅7⋅17
因为 2 23 = 8388608 2^{23}=8388608 223=8388608,对于长度在 8 × 1 0 6 8\times 10^6 8×106 内的大整数乘法足够使用。
4. 提交
从 LeetCode Multiply Strings 一题的提交结果来看:
- FFT 的递归版本比 FFT 的迭代版本需要更多的运行时间和空间。这是因为 FFT 的递归版本会使用额外的栈空间用来辅助计算,开辟和回收这些空间也需要一定的时间,因此相较于 FFT 的迭代版本,FFT 的递归版本的运行时间和空间消耗更多
- NTT 的迭代版本比 FFT 的迭代版本需要的运行时间和空间都更少。这是因为 NTT 使用的是原根,相较于单位复根需要的空间更少,计算次数也更少。
总体来说,FFT 和 NTT 的时间复杂度是相同的,均为 O ( n log n ) O(n\log n) O(nlogn)。NTT 在一定大小范围内的大整数乘法运算中不论是空间还是时间都好过于 FFT,计算的常数低于 FFT ,精度也高于 FFT ,唯一的问题是需要找到合适的原根保证可以覆盖运算需要的范围,当寻找到的原根可以覆盖需要的运算范围,可以选择 NTT ,否则就选择 FFT。
5. 补充
5.1 复数
形如 z = a + b i z=a+bi z=a+bi 的数称为复数,其中: a , b ∈ R , i 2 = − 1 , i a,b\in R,\ i^2=-1,i a,b∈R, i2=−1,i 虚数单位
复数的幅角表示形式: z = r e i θ z=r\mathcal{e}^{i\theta} z=reiθ, r r r 为模长, θ \theta θ 为幅角
加法法则: ( a + b i ) + ( c + d i ) = a + c + ( b + d ) i (a+bi)+(c+di)=a+c+(b+d)i (a+bi)+(c+di)=a+c+(b+d)i
减法法则: ( a + b i ) − ( c + d i ) = a − c + ( b − d ) i (a+bi)-(c+di)=a-c+(b-d)i (a+bi)−(c+di)=a−c+(b−d)i
乘法法则: ( a + b i ) × ( c + d i ) = a c + a d i + b c i + b d i 2 = a c − b d + ( a d + b c ) i (a+bi)\times (c+di)=ac+adi+bci+bdi^2=ac-bd+(ad+bc)i (a+bi)×(c+di)=ac+adi+bci+bdi2=ac−bd+(ad+bc)i
除法法则: ( a + b i ) ÷ ( c + d i ) = a + b i c + d i = a c + b d + ( b c − a d ) i c 2 − d 2 (a+bi)\div (c+di)=\frac{a+bi}{c+di}=\frac{ac+bd+(bc-ad)i}{c^2-d^2} (a+bi)÷(c+di)=c+dia+bi=c2−d2ac+bd+(bc−ad)i
欧拉定理: e i θ = cos θ + i sin θ e^{i\theta}=\cos\theta+i\sin\theta eiθ=cosθ+isinθ
任意一个复数 a + b i a+bi a+bi 对应复平面上的一个点 ( a , b ) (a,b) (a,b) ,横坐标为实部,纵坐标为虚部。
模长 r r r 即复平面上对应的点到原点的距离,即 a 2 + b 2 \sqrt{a^2+b^2} a2+b2
幅角 θ \theta θ 即复平面上对应的点到原点的连线与 x x x 轴的夹角
5.2 单位根
z = a + b i = r e i θ = r ( cos θ + i sin θ ) z=a+bi=re^{i\theta}=r(\cos\theta+i\sin\theta) z=a+bi=reiθ=r(cosθ+isinθ)
两个复数相乘的结果为:两个复数的模相乘,再乘以 e 幅角的和 e^{幅角的和} e幅角的和
单位根是方程 z n = 1 z^n=1 zn=1 在复数范围内的 n n n 个根,因为是单位根,因此模长 r r r 为 1 1 1,本质就是求解 n n n 个不同的 θ \theta θ,使得每个 θ \theta θ 对应的 z z z 都有 z n = 1 z^n=1 zn=1 。
z n = r n e n θ i = 1 ⇒ { r = 1 n θ = 2 k π z^n=r^ne^{n\theta i}=1\\\Rightarrow \left\{\begin{array}{l} r=1\\ n\theta =2k\pi \end{array}\right. zn=rnenθi=1⇒{r=1nθ=2kπ
所以 ω n = 1 \omega^n=1 ωn=1 的 n n n 个根为: ω n k = e i 2 k π n = cos 2 k π n + i sin 2 k π n , ( k = 0 , 1 , 2 , . . . , n − 1 ) \omega_n^k=e^{i\frac{2k\pi}{n}}=\cos\frac{2k\pi}{n}+i\sin\frac{2k\pi}{n},(k=0,1,2,...,n-1) ωnk=ein2kπ=cosn2kπ+isinn2kπ,(k=0,1,2,...,n−1)
主 n n n 次单位根 ω n = e i 2 π n \omega_n=e^{i\frac{2\pi}{n}} ωn=ein2π
5.2.1 消去引理
ω d n d k = ω n k \omega_{dn}^{dk}=\omega_n^k ωdndk=ωnk
证明: ω d n d k = ( e 2 π d n i ) d k = ( e 2 π n i ) k = ω n k \omega_{dn}^{dk}=(e^{\frac{2\pi}{dn} i})^{dk}=(e^{\frac{2\pi}{n}i})^k=\omega_n^k ωdndk=(edn2πi)dk=(en2πi)k=ωnk
5.2.2 折半引理
( w n k + n 2 ) 2 = ( ω n k ) 2 = ω n 2 k (w_n^{k+\frac{n}{2}})^2=(\omega_n^k)^2=\omega_{\frac{n}{2}}^k (wnk+2n)2=(ωnk)2=ω2nk
证明: ω n k + n 2 = ω n k ω n n 2 = − ω n k \omega_n^{k+\frac{n}{2}}=\omega_n^k\omega_n^{\frac{n}{2}}=-\omega_n^k ωnk+2n=ωnkωn2n=−ωnk ,其中 ω n n 2 = ω 2 = − 1 \omega_n^{\frac{n}{2}}=\omega_2=-1 ωn2n=ω2=−1 , ( ω n k ) 2 = ω n 2 k = ω n 2 k (\omega_n^k)^2=\omega_n^{2k}=\omega_{\frac{n}{2}}^k (ωnk)2=ωn2k=ω2nk
5.2.3 求和引理
∑ i = 0 n − 1 ( ω n k ) i = 0 \sum\limits_{i=0}^{n-1}(\omega_n^k)^i=0 i=0∑n−1(ωnk)i=0
证明: ∑ i = 0 n − 1 ( ω n k ) i = ( ω n k ) n − 1 ω n k − 1 = ( ω n n ) k − 1 ω n k − 1 = 1 k − 1 ω n k − 1 = 0 \sum\limits_{i=0}^{n-1}(\omega_n^k)^i=\frac{(\omega_n^k)^n-1}{\omega_n^k-1}=\frac{(\omega_n^n)^k-1}{\omega_n^k-1}=\frac{1^k-1}{\omega_n^k-1}=0 i=0∑n−1(ωnk)i=ωnk−1(ωnk)n−1=ωnk−1(ωnn)k−1=ωnk−11k−1=0
5.3 参考视频和博客
1. 课程PPT
2. 快速傅里叶变换(FFT)——有史以来最巧妙的算法?
3. 算法学习笔记(42): 快速数论变换
4. 快速数论变换(NTT)超详解