精华 推荐

什么是 NTT

YIZHIYANG初来乍到 2026-5-16 0:04:17 45 浏览 0 点赞 0 收藏

什么是 NTT

NTT,全称是 Number Theoretic Transform,中文通常叫“数论变换”。

它可以理解为:

在模意义下进行的 FFT,用来快速计算多项式乘法,也就是卷积。

普通的多项式乘法是 O(n2)O(n^2) 的,而 NTT 可以把复杂度降到 O(nlogn)O(n\log n)

NTT 解决什么问题

假设有两个多项式:

A(x)=a0+a1x+a2x2++an1xn1A(x)=a_0+a_1x+a_2x^2+\cdots+a_{n-1}x^{n-1} B(x)=b0+b1x+b2x2++bm1xm1B(x)=b_0+b_1x+b_2x^2+\cdots+b_{m-1}x^{m-1}

它们的乘积为:

C(x)=A(x)B(x)C(x)=A(x)B(x)

其中 CC 的第 kk 项系数是:

ck=i+j=kaibjc_k=\sum_{i+j=k}a_i b_j

这就是卷积。

如果直接枚举 i,ji,j,复杂度是 O(nm)O(nm)。当 n,mn,m 很大时会很慢。

NTT 的作用就是快速求出所有 ckc_k

NTT 和 FFT 的关系

FFT 是快速傅里叶变换,它利用复数单位根来加速多项式乘法。

NTT 的思想和 FFT 很像,但它不使用复数,而是在模意义下使用“原根”构造出来的单位根。

可以粗略理解为:

  • FFT:在复数域里做快速变换;
  • NTT:在模质数意义下做快速变换。

NTT 的好处是没有浮点误差,结果天然是整数取模后的结果。

为什么需要特殊的模数

NTT 不能随便选模数。

为了能做长度为 nn 的 NTT,通常需要模数 pp 满足:

p=c2k+1p = c \cdot 2^k + 1

这样模 pp 意义下才容易找到足够多的 2k2^k 次单位根。

竞赛中最常用的模数是:

998244353=119223+1998244353 = 119 \cdot 2^{23} + 1

它的原根通常取:

g=3g = 3

所以 998244353998244353 非常适合做 NTT,最大可以支持长度不超过 2232^{23} 的变换。

NTT 的核心思想

多项式有两种表示方式。

第一种是系数表示:

A(x)=a0+a1x+a2x2+A(x)=a_0+a_1x+a_2x^2+\cdots

第二种是点值表示:

(x0,A(x0)),(x1,A(x1)),(x_0,A(x_0)),(x_1,A(x_1)),\ldots

如果两个多项式都变成点值表示,那么乘法非常简单。

因为:

C(xi)=A(xi)B(xi)C(x_i)=A(x_i)B(x_i)

也就是说,每个点对应的值直接相乘即可。

所以多项式乘法可以分成三步:

  1. AABB 从系数表示变成点值表示;
  2. 对每个位置做乘法;
  3. 再把结果从点值表示变回系数表示。

NTT 做的就是第 11 步和第 33 步。

NTT 的大致流程

设需要计算的长度为 lenlen,其中 lenlen22 的幂,并且要满足:

lenn+m1len \ge n+m-1

流程如下:

  1. 把两个多项式的系数数组补零到长度 lenlen
  2. 对数组 AA 做 NTT;
  3. 对数组 BB 做 NTT;
  4. 每一项相乘,得到点值形式的 CC
  5. CC 做逆 NTT;
  6. 得到答案系数。

复杂度是:

O(lenloglen)O(len\log len)

正变换和逆变换

NTT 有两个方向。

正变换把系数变成点值,逆变换把点值变回系数。

正变换使用单位根 ww,逆变换使用 ww 的逆元。

最后逆变换结束后,还要把每一项乘上 lenlen 的逆元。

也就是:

aiailen1a_i \leftarrow a_i \cdot len^{-1}

这一步非常重要,否则答案会整体多乘一个 lenlen

为什么 NTT 可以做到 O(nlogn)O(n\log n)

NTT 和 FFT 一样,使用分治思想。

一个多项式可以按照下标奇偶拆成两部分:

A(x)=A0(x2)+xA1(x2)A(x)=A_0(x^2)+xA_1(x^2)

其中:

  • A0A_0 是偶数下标项组成的多项式;
  • A1A_1 是奇数下标项组成的多项式。

通过单位根的性质,可以把长度为 nn 的问题拆成两个长度为 n/2n/2 的问题。

所以复杂度满足:

T(n)=2T(n2)+O(n)T(n)=2T\left(\frac n2\right)+O(n)

因此总复杂度为:

O(nlogn)O(n\log n)

NTT 常见用途

NTT 最常见的用途是快速卷积。

也就是快速计算:

ck=i+j=kaibjc_k=\sum_{i+j=k}a_i b_j

它经常出现在这些问题里:

  • 多项式乘法;
  • 生成函数;
  • 组合计数;
  • 分治优化卷积;
  • 多项式求逆、开根、取对数、指数;
  • 大整数乘法的取模版本。

和普通卷积的区别

普通卷积直接枚举每一对 i,ji,j

ci+j+=aibjc_{i+j} \mathrel{+}= a_i b_j

复杂度是 O(n2)O(n^2)

NTT 通过变换后逐项相乘,把复杂度降到 O(nlogn)O(n\log n)

所以当数据规模很大,比如 nn 达到 10510^5 或更大时,NTT 就非常有用。

一句话总结

NTT 是一种在模质数意义下进行的快速变换,可以把多项式乘法,也就是卷积,从 O(n2)O(n^2) 优化到 O(nlogn)O(n\log n)

它本质上是 FFT 在整数取模环境下的版本,常用于生成函数和多项式相关题目。

评论

0 条
还没有评论。