CNTT
自己口胡版:就是在 \(\mathbf{Z}_p[\sqrt g]\) 上做 NTT,\(g\) 为 \(p\) 的二次非剩余。
原版:就是在 \(\mathbf{Z}_p[\sqrt{-1}]\) 上做 NTT。要求 \(-1\) 为 \(p\) 的二次非剩余。
感兴趣的可以看看提交记录
两者均被 MTT 吊打。推销 128MTT
黑历史,慎点
前言
在oi wiki上看到了CNTT后,一直不明白它的原理,看完原论文后发现自己好像推了个假的CNTT(
原理
在 CNTT 中,用 \(a+bi\) 代表一个复数,其中实部和虚部分别对 \(p\) 取模,\(p \in \mathbb{P}\)。(\(\mathbb{P}\) 为素数集)
注意,这里的 \(i^2\) 不一定是 \(-1\),只要满足 \(i^2\) 为模 \(p\) 意义下的一个二次非剩余即可。
下面证明,\(\{a+bi\}\) 模 p 下构成一个数域。(upd:然而 FFT 不一定要在数域里做)
只证一下乘法逆元。
逆元:对 \(x = a+bi\),其中 \(a \not= 0 \vee b \not= 0\),设其逆元为 \(y=c+di\),由 \(xy=1\) 可知
当 \(b \not= 0\) 时:由 \((2)\) 知 \(c = -adb^{-1}\),代入 \((1)\) 中可得 \(d = (bi^2-b^{-1}a^2)^{-1}\)。
若 \(bi^2-b^{-1}a^2 \equiv 0 \pmod p\),则 \(i^2 \equiv (ab^{-1})^2 \pmod p\),这与 \(i^2\) 是模 \(p\) 意义下的一个二次非剩余矛盾,所以 \(d\) 一定存在。
当 \(a \not= 0\) 时,用相似的方法可以推出 \(x^{-1} = y\) 一定存在。
方法
这个数域的大小是 \(p^2\),只要用一些方法找出 \(g = a+bi,g^{(p^2-1)/2} \equiv -1 \pmod p\),则 \(g\) 就是我们要找的 \(p^2-1\) 次“原根”,剩下的和 NTT 类似。
\(p=n\cdot2^k+1\) 时(\(p\) 为 NTT 模数),用 CNTT 可以将最大变换长度翻倍;
\(p=n\cdot2^k-1\) 时,用 CNTT 后最大变换长度能取到 NTT 模数级别。
性能
某谷的提交记录显示,CNTT常数是普通NTT的3倍左右(无读入优化等的情况下),甚至不如FFT。
复数相乘要做 \(4\) 次普通乘法,常数能不大吗
CNTT(O2) 4.56s / 56.52MB / 2.14KB C++14 (GCC 9) O2
CNTT 4.61s / 56.51MB / 2.14KB C++14 (GCC 9)
NTT 1.67s / 32.57MB / 1.69KB C++14 (GCC 9)
FFT 1.99s / 104.60MB / 1.56KB C++14 (GCC 9)
这里CNTT的模数是 \(998244353\),使用 \(j^2=3\),\(2^{24}\) 次单位根选用 \(0+125038983j\)。
FFT 有三次变两次优化,CNTT也能用,优化后的测评结果:
FFT 1.37s / 40.46MB / 1.53KB C++14 (GCC 9)
CNTT 1.98s / 24.45MB / 2.27KB C++14 (GCC 9)
优化完的 CNTT 快了不少,FFT 更是反杀 NTT。