NTT(Number Theoretic Transform) - ์ •์ˆ˜๋ก ์  ๋ณ€ํ™˜

๐Ÿ“ ์ƒ์„ธ ์ •๋ฆฌ

  • FFT๋ฅผ ์ž˜ ๋ชจ๋ฅธ๋‹ค๋ฉด, ๋จผ์ € ์ฝ๊ณ  ์˜ค์ž.
  • ๋‹คํ•ญ์‹์˜ ๊ณฑ์…ˆ ๊ฒฐ๊ณผ๋ฅผ ํŠน์ • ์†Œ์ˆ˜ $p$๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๋ฅผ ๊ตฌํ•ด์•ผํ•˜๊ฑฐ๋‚˜, ๊ฐ’์ด ์ปค์ง€๊ฑฐ๋‚˜ ํ•ด์„œ ์‹ค์ˆ˜ ์˜ค์ฐจ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.
    • ์‹ค์ˆ˜๋ฅผ ์•ˆ์“ฐ๊ณ  FFT๋ฅผ ํ•  ์ˆ˜๋Š” ์—†์„๊นŒ?
  • ์šฐ๋ฆฌ๊ฐ€ FFT์—์„œ ํ•ต์‹ฌ์œผ๋กœ ์‚ฌ์šฉํ•˜๋˜ $\omega = \exp\left(-i\dfrac{2\pi}{N}\right)$๋ฅผ ์กฐ๊ธˆ ๋” ์ƒ๊ฐํ•ด๋ณด์ž.
    • FFT์—์„œ ์‚ฌ์šฉ๋˜๋Š” ํ•ต์‹ฌ์ ์ธ ์„ฑ์งˆ์€ $\omega^N = 1$ ์ด๊ณ , $1, \omega, \omega^2, \cdots, \omega^{N-1}$์ด ๋ชจ๋‘ ๋‹ค๋ฅด๋‹ค๋Š” ์ ์ด๋‹ค.
    • ๊ทธ๋Ÿฌ๋ฉด ๋ชจ๋“ˆ๋Ÿฌ ํ•ฉ๋™์‹์˜ ๊ด€์ ์—์„œ, ์–ด๋–ค ์ˆ˜ $k$์™€ ์–ด๋–ค ์†Œ์ˆ˜ $p$๊ฐ€ ์žˆ์–ด์„œ $k^N \equiv 1 \pmod p$๋ฅผ ๋งŒ์กฑ์‹œํ‚ค๊ณ , $1, g, g^2, \cdots, g^{N-1} \pmod p$์ด ๋ชจ๋‘ ๋‹ค๋ฅด๋‹ค๋ฉด, FFT์™€ ๊ฐ™์€ ๋А๋‚Œ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ?
      • ์ด๋Ÿฌํ•œ ์ˆ˜ $g$๋ฅผ ์›์‹œ๊ทผ์ด๋ผ๊ณ  ํ•œ๋‹ค.
    • ๊ทธ๋Ÿฌ๋ฉด $N$์„ ๋˜ ์ž˜ ์žก์•„์•ผํ•  ๊ฒƒ ๊ฐ™์€๋ฐ, ํŽ˜๋ฅด๋งˆ์˜ ์†Œ์ •๋ฆฌ๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.
    • $a^{p-1} \equiv 1 \pmod p$ ์ด๋ฏ€๋กœ, $N = p-1$์ด๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.
    • FFT๋ฅผ ๋ฐ˜์œผ๋กœ ์ชผ๊ฐœ๋˜ ๋ฐฉ๋ฒ•์„ ์ž˜ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด์„ , $N$์ด $2$๋กœ ์ถฉ๋ถ„ํžˆ ๋งŽ์ด ๋‚˜๋ˆ„์–ด ๋–จ์–ด์ง€๋ฉด ์ข‹๊ฒ ๋‹ค.
      • $N = a \times 2^b$ ๊ผด์ด๋ฉด ์ข‹๊ฒ ๊ณ , ์ด์— ๋”ฐ๋ผ $p = a \times 2^b + 1$ ์ด๋ฉด ์ข‹๊ฒ ๋‹ค.
        • $N$์„ $2$๋กœ ๋‚˜๋ˆ„์–ด ๋–จ์–ด์ง€๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋Š” ํšŸ์ˆ˜์— ๋”ฐ๋ผ FFT๋กœ ๊ณ„์‚ฐ ๊ฐ€๋Šฅํ•œ ๋ฐฐ์—ด์˜ ํฌ๊ธฐ๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค!
        • $n \leq 2^b$ ์˜ ํฌ๊ธฐ์˜ ๋‹คํ•ญ์‹๊นŒ์ง€๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.
      • ์ด๋กœ ์–ด์šธ๋ฆฌ๋Š” ๋Œ€ํ‘œ์ ์ธ ์†Œ์ˆ˜๋กœ๋Š” $p = 998244353 = 119 \times 2^{23} + 1$ ์ด ์žˆ๊ฒ ๋‹ค.
        • ์ด๋•Œ ์›์‹œ๊ทผ $g = 3$ ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๊ทธ๋Ÿฌ๋ฉด ์ด์ œ $\omega = \exp\left(-i\dfrac{2\pi}{N}\right)$ ๋Œ€์‹  $\omega = g^{(p-1)/n}$ ์„ ์ด์šฉํ•ด์„œ FFT๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.
  • ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
void ntt(vector<mint> &a, bool inv = false){
	int N = a.size();
	assert(N > 0 && (N & (N-1)) == 0); // N์€ 2์˜ ๊ฑฐ๋“ญ์ œ๊ณฑ์ด์–ด์•ผ ํ•จ

	// ๋น„ํŠธ ๋’ค์ง‘๊ธฐ
	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]);
	}

	// ๋‹จ์œ„๊ทผ ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ
	mint ang = mint(3).pow((MOD-1)/N);
	if(inv) ang = ang.inv();
	
	vector<mint> w(N/2); // ๋‹จ์œ„๊ทผ
	w[0] = 1;
	rep(i, 1, N/2) w[i] = w[i-1] * ang;

	// Cooley-Tukey FFT
	for(int len = 2; len <= N; len <<= 1){
		int step = N / len;
		for(int i = 0; i < N; i += len){
			rep(j, 0, len>>1){
				mint even = a[i+j], odd = a[i+j+(len>>1)] * w[j*step];
				a[i+j] = even + odd;
				a[i+j+(len>>1)] = even - odd;
			}
		}
	}

	// ์—ญ FFT์ธ ๊ฒฝ์šฐ ๊ฒฐ๊ณผ๋ฅผ N์œผ๋กœ ๋‚˜๋ˆ”
	if(inv){
		mint invN = mint(N).inv();
		rep(i, 0, N) a[i] *= invN;
	}
}

โ”์งˆ๋ฌธ ์‚ฌํ•ญ

๐Ÿ”— ์ฐธ๊ณ  ์ž๋ฃŒ