计算机

快速数论变换

理论知识

设 g 是质数 p 的一个 原根,即 1, g, g^2, \dots, g^{p - 2} 在模 p 意义下互不相同,且 g^{p - 1} \equiv 1 \pmod{p}。

将 p - 1 分解为 2 的幂与其他数的乘积,有 p - 1 = 2^l \cdot r。令 \alpha_N = g^{r},N = 2^l,则 \alpha 有着与复数单位根 \omega_N 非常相似的性质:

复数 有限域
\omega^N = 1 \alpha^N = 1
1, \omega, \omega^2, \dots, \omega^{N - 1} 互不相同 1, \alpha, \alpha^2, \dots, \alpha^{N - 1} 模 p 意义下互不相同
\omega^{\frac{N}{2}} = -1 \alpha^{\frac{N}{2}} = -1

而应用 FFT 加速多项式乘法,可以看做向多项式代入了 n 个点,转化为点值表示法。NTT 同理,虽然 NTT 变换后得到的“频谱”不再具有物理意义,但依然可以视为在模 p 意义下向多项式代入了 n 个点。

算法

描述

对于质数 p 和原根 g,保证 p - 1 是 n = 2^l 的倍数的情况下,只需将 FFT 中的 \omega = \mathrm{e}^{\mathrm{i} \frac{2 \pi}{n}} 替换为 g^{\frac{p - 1}{n}} 即可。

常用模数:

\begin{aligned} & p = 167772161 = 5 \times 2^{25}+1, \quad & g=3; \\ & p = 998244353 = 7 \times 17 \times 2^{23} + 1, \quad & g = 3; \\ & p = 1004535809 = 479 \times 2^{21}+1, \quad & g=3. \\ \end{aligned}

2^{21} > 2 \times 10^6,一般数组长度不会超过 1 \times 10^6,因此这些模数已经足够。

其余实现与 快速 Fourier 变换 几乎完全相同。

代码

ull tmp[MAXN], f[MAXN], g[MAXN], h[MAXN], omega[MAXN + 1];

void init()
{
    omega[0] = 1;
    constexpr size_t n = MAXN;
    ull o = qpow(3, (P - 1) / n);
    for (size_t i = 1; i <= n; i++)
    {
        omega[i] = omega[i - 1] * o % P;
    }
}

void ntt(ull *f, size_t n, int rev)
{
    if (n == 1)
    {
        return;
    }
    for (size_t i = 0; i < n; i++)
    {
        tmp[i] = f[i];
    }
    for (size_t i = 0; i < n / 2; i++)
    {
        f[i] = tmp[i * 2]; // g
        f[i + n / 2] = tmp[i * 2 + 1]; // h
    }
    ntt(f, n / 2, rev);
    ntt(f + n / 2, n / 2, rev);
    for (size_t i = 0; i < n / 2; i++)
    {
        ull o = omega[MAXN * (rev == -1) + rev * i * (MAXN / n)];
        tmp[i] = (f[i] + o * f[i + n / 2]) % P;
        tmp[i + n / 2] = (P + f[i] - o * f[i + n / 2] % P) % P;
    }
    for (size_t i = 0; i < n; i++)
    {
        f[i] = tmp[i];
    }
}

多模数 NTT

有些题目的数据范围或者答案会超过常用模数的范围,例如达到 10^{18},不能直接应用 NTT。此时,可以使用多个模数同时计算 NTT。最常用的就是两个模数。

双模数 NTT 下,数据对两个模数取模后,分别对两个模数使用 NTT 计算卷积。计算完成后,得到的答案是对两个模数分别取模的结果,使用 中国剩余定理 就可以从两个取模数还原出答案。

设答案对 p_1, p_2 取模的结果为 a_1, a_2,则要寻找原来的答案 x,就是解同余方程组:

\begin{cases} x \equiv a_1 & \pmod{p_1}; \\ x \equiv a_2 & \pmod{p_2}. \end{cases}

由 CRT 的公式,还原得到的数为

a = \left (a_1 p_2 p_2^{-1} + a_2 p_1 p_1^{-1} \right) \bmod p_1p_2,

其中 p_2^{-1} = p_2^{p_1 - 2} \bmod p_1,p_1^{-1} = p_1^{p_2 - 2} \bmod p_2。

BEYOND THE ARTICLE

读完了,有什么想法?

阅读已保存的问答示例,并循着引用回到原文。

查看公开问答示例
— 本文完 — 返回文章列表