快速数论变换(Number Theoretic Transform)
By Long Luo
之前的文章 快速傅里叶变换(FFT)算法 和 快速傅里叶变换(FFT)算法的实现及优化 详细介绍了 \(\textit{FFT}\) 的具体实现及其实现。
\(\textit{FFT}\) 优点很多,但缺点也很明显。例如单位复根的实部和虚部分别是一个正弦及余弦函数,有大量浮点数计算,计算量很大,而且浮点数运算产生的误差会比较大。
如果我们操作的对象都是整数的话,其实数学家已经发现了一个更好的方法:快速数论变换 \(\textit{(Number Theoretic Transform)}\) 1。
快速数论变换(NTT)
\(\textit{FFT}\) 的本质是什么?
- 将卷积操作变成了乘法操作。
是什么让 \(\textit{FFT}\) 做到了 \(O(n \log n)\) 的复杂度?
- 是单位复根。
那有没有什么其他的东西也拥有单位根的这些性质呢?
答案当然是有的,原根2 就具有和单位根一样的性质。
所以快速数论变换 \(\textit{NTT}\) 就是以数论为基础的具有循环卷积性质的,用有限域上的单位根来取代复平面上的单位根的 \(\textit{FFT}\)。
原根
仿照单位复数根的形式,也将原根的取值看成一个圆,不过这个圆上只有有限个点,每个点表达的是模数的剩余系中的值。
在 \(\textit{FFT}\) 中,我们总共用到了单位复根的这些性质:
- \(n\) 个单位复根互不相同;
- \(\omega_n^k = \omega_{2n}^{2k}\);
- \(\omega_n^{k} = -\omega_n^{k+n/2}\);
- \(\omega_n^a \times \omega_n^b = \omega_n^{a+b}\)。
我们发现原根具有和单位复根一样的性质,简单证明3 :
令 \(n\) 为大于 \(1\) 的 \(2\) 的幂,\(p\) 为素数且 \(n \mid (p-1)\),\(g\) 为 \(p\) 的一个原根。
我们设 \(g_n = g^{\frac{p-1}{n}}\):
\(g_n^n=g^{n \cdot \frac{p-1}{n}}=g^{p-1}\)
\(g_n^{\frac{n}{2}} = g^{\frac{p-1}{2}}\)
\(g_{an}^{ak} = g^{\frac{ak(p-1)}{an}} = g^{\frac{k(p-1)}{n}}=g_n^k\)
显然
\(g_n^n \equiv 1 \pmod p\)
\(g_n^{\frac{n}{2}} \equiv -1 \pmod p\)
\((g_n^{k+\frac{n}{2}})^2=g_n^{2k+n} \equiv g_n^{2k} \pmod p\)
证毕。
所以将 \(g_n^k\) 和 \(g_n^{k+\frac{n}2}\) 带入本质上和将 \(\omega_n^{k}\) 和 \(\omega_n^{k+\frac{n}{2}}\) 带入的操作无异。
利用 Vandermonde 矩阵性质,类似 \(\textit{NTT}\) 那样,我们可以从 \(\textit{NTT}\) 变换得到逆变换 \(\textit{INTT}\) 变换,设 \(x(n)\) 为整数序列,则有:
\(\textit{NTT}\) : \(X(m) = \sum \limits_{i=0}^{N}x(n)a^{mn} \pmod M\)
\(\textit{INTT}\) : \(X(m) = N^{-1}\sum \limits_{i=0}^{N}x(n)a^{-mn} \pmod M\)
这里 \(N^{-1}\) ,\(a^{-mn} \pmod M\) 为模意义下的乘法逆元。
当然, \(\textit{NTT}\) 也是有自己的缺点的:比如不能够处理小数的情况,以及不能够处理没有模数的情况。对于模数的选取也有一定的要求,首先是必须要有原根,其次是必须要是 \(2\) 的较高幂次的倍数。
NTT 实现
通过上面的分析,开始写代码吧:-)
\(\textit{NTT}\) 也有递归版(Recursion)和迭代版(Iteration) \(2\) 种实现:
递归版(Recursion)
1 | const long long G = 3; |
迭代版(Iteration)
1 | public: |
复杂度分析
- 时间复杂度:\(O((m+n) \log (m+n))\)。
- 空间复杂度:\(O(m+n)\)。