Evaluate a polynomial at roots of unity, multiply pointwise, interpolate back — turning convolution into .
The three steps
evaluate A and B at the n-th roots of unity O(n log n)
multiply pointwise O(n)
interpolate (inverse transform) O(n log n)
Evaluation and interpolation are the same algorithm with replaced by and a final division by . See Cooley-Tukey for the derivation.
NTT — the one to use
Over with , a primitive root gives a -th root of unity . Everything is exact integer arithmetic — no precision worries at all.
const long long MOD = 998244353, ROOT = 3; // 998244353 = 119 * 2^23 + 1
void ntt(vector<long long>& a, bool invert) {
int n = a.size();
for (int i = 1, j = 0; i < n; i++) {
int bit = n >> 1;
for (; j & bit; bit >>= 1) j ^= bit;
j ^= bit;
if (i < j) swap(a[i], a[j]);
}
for (int len = 2; len <= n; len <<= 1) {
long long w = powmod(ROOT, (MOD - 1) / len, MOD);
if (invert) w = powmod(w, MOD - 2, MOD);
for (int i = 0; i < n; i += len) {
long long wn = 1;
for (int j = 0; j < len / 2; j++) {
long long u = a[i+j], v = a[i+j+len/2] * wn % MOD;
a[i+j] = (u + v) % MOD;
a[i+j+len/2] = (u - v + MOD) % MOD;
wn = wn * w % MOD;
}
}
}
if (invert) {
long long ninv = powmod(n, MOD - 2, MOD);
for (auto& x : a) x = x * ninv % MOD;
}
}Identical in shape to the complex FFT — only the root of unity changes.
NTT-friendly primes
| Prime | Factorisation | Max length | Root |
|---|---|---|---|
| 998244353 | 3 | ||
| 1004535809 | 3 | ||
| 469762049 | 3 | ||
| 2013265921 | 31 | ||
| 4179340454199820289 | 3 |
is not NTT-friendly (), which is why has become the standard contest modulus.
Arbitrary modulus
When the required modulus is not NTT-friendly:
- Three primes + CRT. Run NTT modulo three of the primes above; the true product is below , so CRT reconstructs it exactly, then reduce.
- Coefficient splitting. Write , similarly for ; four complex FFTs (or three with packing) give the exact product.
__int128accumulation for small .
Method 1 is the more robust; method 2 avoids modular arithmetic entirely.
FFT vs NTT
| Complex FFT | NTT | |
|---|---|---|
| Arithmetic | double | integers mod |
| Exactness | approximate | exact |
| Precision limits | real, and easy to hit | none |
| Speed | comparable | slightly slower per butterfly |
| Modulus required | no | yes (or CRT) |
| Real-valued inputs | natural | needs a modulus |
Use NTT unless the coefficients are genuinely real.
Optimisations worth having
- Precompute the roots once into an array; recomputing
powmodper level is a large constant. - Avoid the modulo by using
unsigned long longand reducing lazily (Barrett or Montgomery). - Skip the final inverse transform when you only need one coefficient.
- Reuse transforms — squaring needs one forward transform, not two.
- Pack two real sequences into one complex FFT (real in the real part, the other in the imaginary part).
What builds on it
| Operation | Cost |
|---|---|
| Polynomial multiplication | |
| Series inverse, log, exp, sqrt, pow | |
| Polynomial division and remainder | |
| Multipoint evaluation and interpolation | |
| GCD of polynomials | (half-GCD) |
| Linear recurrence, $n$-th term | |
| Big integer multiplication | |
| String matching with wildcards | |
| Counting with generating functions |
See also: NTT · Cooley-Tukey · Convolution