다항식 곱셈이라는 문제
두 다항식 \(A(x)=\sum_{i=0}^{n-1} a_i x^i\), \(B(x)=\sum_{j=0}^{m-1} b_j x^j\)의 곱 \(C=A\cdot B\)를 구하면,
$$ c_k = \sum_{i+j=k} a_i b_j $$
가 된다. 정의대로 계산하면 \(O(nm)\)이지만, 이는 곧 길이 \(n,m\)인 두 수열의 합성곱(convolution) 이다. 큰 정수 곱셈, 문자열 매칭 점수, 부분합 개수 세기 등이 전부 이 형태로 환원된다.
점-값 표현과 핵심 통찰
차수 \(d\) 미만의 다항식은 서로 다른 점 \(d\)개의 함숫값으로 유일하게 결정된다. 따라서
- \(A,B\)를 같은 점 \(N\)개에서 평가하고 (\(N \ge n+m-1\)),
- 점마다 값을 곱한 뒤 (\(O(N)\)),
- 다시 계수로 보간하면
곱을 얻는다. 문제는 평가/보간이 일반적으로 \(O(N^2)\)라는 점이다. FFT의 통찰은 평가점으로 \(N\)제곱근(단위근)을 쓰면 분할 정복이 가능하다는 것이다.
단위근과 분할 정복
\(\omega_N = e^{2\pi i / N}\) 를 \(N\)차 원시 단위근이라 하자. \(A\)를 짝/홀 차수로 나누면
$$ A(x) = A_{\text{even}}(x^2) + x\,A_{\text{odd}}(x^2) $$
\(x = \omega_N^k\) 를 대입하면 \(x^2 = \omega_{N/2}^k\) 가 되어, 크기 \(N\) 평가가 크기 \(N/2\) 짜리 두 평가로 줄어든다. 또한 \(\omega_N^{k+N/2} = -\omega_N^k\) 이므로 한 번의 계산으로 두 출력을 얻는다 (butterfly):
$$ X_k = E_k + \omega_N^k O_k,\qquad X_{k+N/2} = E_k - \omega_N^k O_k $$
점화식 \(T(N)=2T(N/2)+O(N)\) 이므로 전체 \(O(N\log N)\).
역변환
DFT 행렬은 \(F_{jk}=\omega_N^{jk}\) 이고 그 역은 \(F^{-1}_{jk} = \frac{1}{N}\omega_N^{-jk}\) 이다. 즉 역FFT는 \(\omega_N\) 대신 \(\omega_N^{-1}\) 을 쓰고 마지막에 \(N\)으로 나누면 된다. 정변환과 코드를 공유할 수 있다.
정밀도와 NTT의 동기
복소수 FFT는 부동소수점 오차가 있어, 계수가 큰 정수 합성곱에서는 반올림이 위험하다. 모듈러 환경에서 정확한 정수 결과가 필요하면 단위근을 소수 \(p\)의 곱셈군에서 잡는 NTT(수론적 변환)를 쓴다. \(p = c\cdot 2^k + 1\) 꼴(예: \(998244353 = 119\cdot 2^{23}+1\))이면 \(2^k\)차 원시근이 존재해 같은 butterfly가 정수 위에서 정확히 돈다.
복잡도 요약
| 연산 | 복잡도 |
|---|---|
| 직접 곱셈 | \(O(nm)\) |
| FFT/NTT 곱셈 | \(O(N\log N)\), \(N=2^{\lceil\log_2(n+m-1)\rceil}\) |
| 메모리 | \(O(N)\) |