快速数论变换-NTT
前文再续,书接上一回,我们说到多项式乘法可以用 FFT
来处理,但由于多次使用三角函数以及复数,难免带来精度的问题,动不动就炸掉了
于是,快速数论变换(NTT)就横空出世
他是一种用原根
代替单位根
的算法,他不仅保证了精度(在整数域内),而且支持多项式的取模,因此是一种优秀的算法
1. 原根
首先我们要了解原根是啥:
-
阶:形如 \(x^k\equiv 1\ (mod\ p)\) 的最小正整数 \(k\) 称为 \(x\) 的阶
-
原根:若 \(1\le g<n\),满足阶为 \(\phi(p)\) 的数 \(g\) 称为 \(p\) 的原根
说白了,就是 \(k\) 取不同值时,\(g^k\) 在模 \(p\) 意义下的取值都不同
模板题(求原根)
注:以下定理均无证明,需要的请自行查阅
首先,只有如下这几类数才有原根:\(2,4,p^k,2p^k\) (\(p\) 为奇质数
我们考虑找到 \(p\) 的最小原根 \(g\) :已有证明显示,\(g\) 是 \(n^{0.25}\) 级别的,因此我们可以直接暴力枚举寻找 \(g\)
但我们除了判断 \(g^{\phi(p)}\equiv 1\ (mod\ p)\) 外,还要判断小于 \(\phi(p)\) 的数 \(k\) 都满足 \(g^k\not \equiv 1\ (mod\ p)\)
有证明指出,若 \(g^k\equiv 1\ (mod\ p)\),那么一定满足 \(k|\phi(p)\)
设 \(\phi(n)\) 的质因数为 \(\{p_1,p_2,p_3,...,p_n\}\)
我们只需要验证 \(g^{\frac{\phi(n)}{p_i}}\not \equiv 1\ (mod\ p)\) 即可
这个过程的复杂度为 \(O(n^{0.25}\ log\ n)\)
一般题目中求了最小原根就足够了,但本题还要求剩下的原根
假设我们已经求出了最小原根 \(g\) ,那么有一条定理为:原根 \(g\) 满足 \(g^k\) 的阶为 \(\frac{\phi(p)}{gcd(\phi(p), k)}\)
也就是说,只有当 \(gcd(\phi(p), k)=1\) 时,\(g^k\ mod\ p\) 是 \(p\) 的原根之一;而一个数如果有原根,则有 \(\phi(\phi(n))\) 个
那我们就考虑枚举 \(1\) ~ \(\phi(p)\) 判断是否满足上述 \(gcd(\phi(p), k)=1\)
复杂度为 \(O(\phi(n)\ log\ \phi(n))\)
最后对答案进行排序即可
2. NTT
注:这里默认 \(p\) 为奇质数
我们先回顾一下 FFT
的单位根
都要满足些什么条件:
① \(n\) 个根的值互不相同
显然,根据原根的定义,我们一定有 \(g^0\) ~ \(g^{(p-1)}\) 互不相同
② \(\omega_n^k=\omega_{2n}^{2k}\)
根据我们的定义 \(G_n^k=g^{\frac{p-1}{n}\times k}\)
所以有 \(G^{2k}_{2n}=G_n^k\)
③ \(\omega_n^k=-\omega_n^{k+n/2}\)
我们有 \(G_n^n=(G_n^1)^n=g^{p-1}=1\ (mod\ p)\) (根据费马小定理
)
又因为 \((G_n^{n/2})^2=G_n^n=1\),所以 \(G_n^{n/2}=\pm1\),又根据性质①,知 \(G_n^{n/2}=-1\)
因此我们有 \(G_n^{k+n/2}=G_n^k\times G_n^k=-G_n^k\)
④ \(\sum_{i=0}^{n-1} (\omega_n^k)^i=0\)
这个是为了证明 IDFT
时直接将 \(\omega_n^1\) 换成 \(\omega_n^{-1}\),最后答案再除以 \(n\) 是正确的
用同样的套路,我们有
至此,我们的原根
\(G\) 都完美地符合了单位根
\(\omega\) 的所有性质
但由性质③,我们看出 \(p-1\) 必须要是 \(2\) 的整次幂的倍数才能正常分治,一次我们的模数 \(p\) 必须是 \(k\times 2^m+1\) 这样的形式
因此 NTT
有以下的限制条件:
-
- 系数必须为整数
-
- 在模意义下运算,而且对模数的要求较高,为 \(k\times 2^m+1\) 的奇质数
但不得不说,NTT
保证了精度,以及它不需要借助三角函数让复杂度降低不少的优点是很突出的
附上常见质数的原根