Chapter 08

이산 푸리에 변환과 FFT

5~7장은 다항식으로 근사했습니다. 그런데 하루의 기온, 회전하는 기계의 진동, 소리처럼 되풀이되는 데이터에는 다항식보다 사인과 코사인이 어울립니다. 등간격으로 잰 주기 데이터를 삼각함수로 보간하면 그 계수가 이산 푸리에 변환(DFT)이고, DFT는 N×NN\times N 행렬 곱이라 N2N^2번의 곱셈이 듭니다. 1965년에 널리 알려진 고속 푸리에 변환(FFT)은 같은 결과를 Nlog⁡2NN\log_2 N에 비례하는 연산으로 냅니다. 백만 개의 표본이면 수만 배 빠릅니다. 이 장은 DFT의 정의와 단위근의 기하, 표본화가 만드는 앨리어싱, FFT의 짝·홀 분할과 나비 연산, FFT로 합성곱과 큰 수 곱셈을 빠르게 하는 법, 그리고 실제 신호의 스펙트럼을 읽을 때 만나는 누설과 창 함수까지 다룹니다.

이 수식이 없었다면와이파이와 LTE·5G의 OFDM

스마트폰의 LTE·5G와 와이파이는 OFDM이라는 방식으로 데이터를 보냅니다. 수백~수천 개의 서로 다른 주파수(부반송파)에 비트를 나눠 실어 한꺼번에 보내는 방식인데, 송신기는 매 심볼마다 역 DFT로 그 주파수들을 합쳐 하나의 파형을 만들고, 수신기는 DFT로 다시 갈라냅니다. 부반송파가 2048개라면 DFT를 직접 계산할 때 심볼 하나에 약 400만 번의 복소 곱셈이 필요하고, 이를 초당 수만 번 해야 합니다. FFT는 같은 일을 약 1만 1천 번의 곱셈으로 끝냅니다.

FFT의 아이디어는 가우스(Carl Friedrich Gauss)의 1805년 무렵 미출판 원고에 이미 있었다고 알려져 있지만, 널리 퍼진 것은 쿨리(James Cooley)와 튜키(John Tukey)의 1965년 논문 뒤입니다. 그 뒤로 디지털 오디오, MRI 영상 재구성, 레이더, 지진파 분석, 큰 수의 곱셈까지 FFT 위에 올라섰습니다.

N개 주파수의 데이터→DFT: N² 곱셈→FFT: (N/2) log₂N 나비→실시간 무선 통신·영상·오디오

주기 데이터의 삼각 보간

하루 24번 잰 기온

기상 관측소가 한 시간마다 기온을 잽니다. 기온은 하루를 주기로 되풀이되므로 측정 사이의 값을 보간할 때 다항식은 어울리지 않습니다. 다항식은 구간 밖으로 가면 무한히 커지고, 0시와 24시의 값과 기울기가 맞아야 한다는 주기 조건을 모릅니다. 주기가 있는 함수의 자연스러운 재료는 cos⁡kx\cos kx와 sin⁡kx\sin kx입니다.

주기를 2π2\pi로 맞추고 한 주기를 NN등분한 점 xj=2πj/Nx_j = 2\pi j/N(j=0,…,N−1j = 0, \dots, N - 1)에서 값 fjf_j를 안다고 합시다. 복소 지수 eikx=cos⁡kx+isin⁡kxe^{ikx} = \cos kx + i\sin kx로 쓰면 계산이 훨씬 간단해집니다. 핵심은 등간격 점에서 지수 함수들이 이산적으로 직교한다는 사실입니다.

정리 8.1 (이산 직교성) 정수 k,lk, l에 대해
∑j=0N−1eikxj eilxj‾=∑j=0N−1e2πi(k−l)j/N={N,k≡l(modN)0,그 밖\sum_{j=0}^{N-1} e^{ikx_j}\,\overline{e^{ilx_j}} = \sum_{j=0}^{N-1} e^{2\pi i(k - l)j/N} = \begin{cases} N, & k \equiv l \pmod N\\ 0, & \text{그 밖}\end{cases}

증명. q=e2πi(k−l)/Nq = e^{2\pi i(k - l)/N}이라 하면 합은 등비급수 ∑j=0N−1qj\sum_{j=0}^{N-1}q^j이다. k≡lk \equiv l이면 q=1q = 1이라 합이 NN. 아니면 q≠1q \ne 1, qN=1q^N = 1이므로 (1−qN)/(1−q)=0(1 - q^N)/(1 - q) = 0. ∎

7장의 언어로 말하면 등간격 점 위의 내적 ⟨u,v⟩=∑ju(xj)v(xj)‾\langle u, v\rangle = \sum_j u(x_j)\overline{v(x_j)}에서 eikxe^{ikx}들이 직교 기저입니다. 그래서 보간 계수가 정규 방정식을 풀 필요 없이 내적 하나로 나옵니다. 단, kk와 k+Nk + N은 표본점에서 구별되지 않습니다(ei(k+N)xj=eikxje^{i(k+N)x_j} = e^{ikx_j}). 이것이 3절의 앨리어싱입니다.

정리 8.2 (삼각 보간) NN이 짝수일 때
p(x)=1N∑k=−N/2+1N/2−1Fk eikx+1NFN/2cos⁡Nx2,Fk=∑j=0N−1fj e−ikxjp(x) = \frac1N\sum_{k=-N/2+1}^{N/2-1} F_k\,e^{ikx} + \frac{1}{N}F_{N/2}\cos\frac{Nx}{2},\qquad F_k = \sum_{j=0}^{N-1} f_j\,e^{-ikx_j}
FkF_{k}는 kk에 대해 주기 NN이므로 F−k=FN−kF_{-k} = F_{N-k}로 읽는다. fjf_j가 실수이면 F−k=Fk‾F_{-k} = \overline{F_k}이고 pp도 실수이다. 나이퀴스트 항 cos⁡(Nx/2)\cos(Nx/2)는 ±N/2\pm N/2 두 지수를 반씩 나눈 것이다.
는 p(xj)=fjp(x_j) = f_j를 만족하는 차수 N/2N/2 이하의 유일한 실 삼각 다항식이다. 계수 FkF_k의 계산이 다음 절의 이산 푸리에 변환이다.
예제 8.1 (네 점의 삼각 보간) N=4N = 4, xj=0,π2,π,3π2x_j = 0, \frac\pi2, \pi, \frac{3\pi}2에서 f=(2,1,0,1)f = (2, 1, 0, 1).
  1. e−ikxj=(−i)kje^{-ikx_j} = (-i)^{kj}. F0=2+1+0+1=4F_0 = 2 + 1 + 0 + 1 = 4.
  2. F1=2+1⋅(−i)+0⋅(−1)+1⋅(i)=2F_1 = 2 + 1\cdot(-i) + 0\cdot(-1) + 1\cdot(i) = 2. F2=2−1+0−1=0F_2 = 2 - 1 + 0 - 1 = 0. F3=F−1=F1‾=2F_3 = F_{-1} = \overline{F_1} = 2.
  3. p(x)=14(4+2eix+2e−ix)+0=1+cos⁡xp(x) = \frac14\bigl(4 + 2e^{ix} + 2e^{-ix}\bigr) + 0 = 1 + \cos x. 확인: 1+cos⁡0=21 + \cos 0 = 2, 1+cos⁡π2=11 + \cos\frac\pi2 = 1, 1+cos⁡π=01 + \cos\pi = 0. 데이터는 사실 1+cos⁡x1 + \cos x의 표본이었고, 삼각 보간이 그것을 정확히 되살렸다.
SIMULATOR

점을 끌어 만드는 삼각 보간

한 주기 [0, 2π) · 점을 위아래로 끌기
|Fk| / N, k = 0 … N/2
점의 개수 N
삼각 보간 p(x)원래 함수다항식 보간
F₀/N (평균)—
가장 큰 |Fk|/N (k ≥ 1)—
원래 함수와의 최대 오차—
해볼 것: ① 점 하나를 끌면 곡선 전체가 매끄럽게 반응하고, 0과 2π에서 값과 기울기가 이어지는 것을 보세요. ② e^(sin x)는 N = 8만 되어도 오차가 10⁻³ 근처, N = 16이면 10⁻⁸ 수준입니다. 사각파는 불연속점 근처의 출렁임이 N을 늘려도 남습니다(깁스 현상). ③ 다항식 보간을 겹치면 주기 경계 근처에서 다항식이 따로 놀아 버립니다. 모델: 정리 8.2의 실 삼각 다항식, N은 짝수.

DFT의 정의, 행렬, 단위근

표본의 위치 xjx_j를 잊고 숫자의 배열만 남기면 정의가 깔끔해집니다.

정의 8.3 (이산 푸리에 변환) ωN=e−2πi/N\omega_N = e^{-2\pi i/N}이라 하자. 복소 벡터 x=(x0,…,xN−1)\mathbf x = (x_0, \dots, x_{N-1})의 이산 푸리에 변환(discrete Fourier transform, DFT)은
Xk=∑j=0N−1xj ωNjk,k=0,1,…,N−1X_k = \sum_{j=0}^{N-1} x_j\,\omega_N^{jk},\qquad k = 0, 1, \dots, N - 1
이고, 역변환은 xj=1N∑k=0N−1Xk ωN−jkx_j = \frac1N\sum_{k=0}^{N-1}X_k\,\omega_N^{-jk}이다. 행렬로 X=FNx\mathbf X = F_N\mathbf x, (FN)kj=ωNjk(F_N)_{kj} = \omega_N^{jk}이며 FN−1=1NFN‾F_N^{-1} = \frac1N\overline{F_N}이다.

ωN\omega_N은 1의 원시 NN제곱근, 곧 복소 평면의 단위원을 NN등분한 점입니다(MathBook: 복소수는 회전이다). FNF_N의 kk번째 행은 단위원을 시계 방향으로 한 걸음에 k/Nk/N바퀴씩 도는 수열이고, XkX_k는 신호를 그 회전과 짝지어 더한 값입니다. 신호에 그 빠르기로 도는 성분이 있으면 화살표들이 한 방향으로 모여 ∣Xk∣|X_k|가 커지고, 없으면 서로 상쇄됩니다. 역변환이 켤레를 쓰는 이유는 정리 8.1 그대로입니다. FN‾FN=NI\overline{F_N}F_N = NI이므로 FN/NF_N/\sqrt N은 유니터리 행렬입니다. 여기서 두 가지 성질이 바로 나옵니다.

정리 8.4 (파스발 등식과 조건수) ∑j∣xj∣2=1N∑k∣Xk∣2\sum_j|x_j|^2 = \frac1N\sum_k|X_k|^2이다. FN/NF_N/\sqrt N이 유니터리이므로 κ2(FN)=1\kappa_2(F_N) = 1, 곧 DFT는 완벽하게 조건이 좋은 문제다. 실제로 배정밀도 FFT로 변환했다가 역변환하면 상대 오차가 N=210N = 2^{10}에서 3.8×10−163.8\times10^{-16}, N=218N = 2^{18}에서도 5.3×10−165.3\times10^{-16}에 머문다.
예제 8.2 (손으로 DFT) x=(1,2,3,4)\mathbf x = (1, 2, 3, 4), N=4N = 4, ω4=−i\omega_4 = -i.
F4=[11111−i−1i1−11−11i−1−i],F4[1234]=[10−2+2i−2−2−2i].F_4 = \begin{bmatrix} 1 & 1 & 1 & 1\\ 1 & -i & -1 & i\\ 1 & -1 & 1 & -1\\ 1 & i & -1 & -i\end{bmatrix},\qquad F_4\begin{bmatrix}1\\2\\3\\4\end{bmatrix} = \begin{bmatrix} 10\\ -2 + 2i\\ -2\\ -2 - 2i\end{bmatrix}.

X0=10X_0 = 10은 합(평균의 NN배). 실수 입력이므로 X3=X1‾X_3 = \overline{X_1}. 파스발: 1+4+9+16=301 + 4 + 9 + 16 = 30, 14(100+8+4+8)=30\frac14(100 + 8 + 4 + 8) = 30. 직접 계산은 1616번의 곱셈이 들었다. 4절에서 이를 줄인다.

표본 간격이 Δt\Delta t초이면 XkX_k는 주파수 k/(NΔt)k/(N\Delta t) Hz 성분입니다. 곧 주파수 눈금의 간격(주파수 분해능)은 Δf=1/(NΔt)=1/T\Delta f = 1/(N\Delta t) = 1/T, 관측 시간의 역수입니다. 실수 신호에서는 kk와 N−kN - k가 켤레라서 k=0,…,N/2k = 0, \dots, N/2만 보면 충분하고, 진폭 AA인 사인파 Acos⁡(2πk0j/N)A\cos(2\pi k_0j/N)는 ∣Xk0∣=∣XN−k0∣=AN/2|X_{k_0}| = |X_{N-k_0}| = AN/2로 나타납니다. 그래서 진폭을 읽을 때는 2∣Xk∣/N2|X_k|/N을 그립니다.

SIMULATOR

신호 합성 → 스펙트럼

시간 영역: N = 128 표본 (1초)
진폭 스펙트럼 2|Xk|/N
가장 큰 봉우리 세 개 (Hz: 진폭)—
파스발: Σ|x|² · Σ|X|²/N—
해볼 것: ① 성분 2의 진폭을 0으로 하면 봉우리 하나가 사라집니다. 시간 영역에서는 알아보기 어려운 변화가 주파수 영역에서는 한눈에 보입니다. ② 잡음 σ를 1로 올리면 시간 파형은 엉망이지만 스펙트럼의 봉우리는 잡음 바닥 위로 뚜렷합니다. 잡음은 128개 주파수에 고르게 퍼지고, 사인파는 한 칸에 모이기 때문입니다. ③ 주파수를 64(나이퀴스트)로 하면 진폭이 두 배로 보입니다. k = N/2에는 켤레 짝이 따로 없기 때문입니다. 모델: xj = Σ A cos(2π f j/128) + 잡음, 주파수는 정수 Hz.
SIMULATOR

단위근과 DFT 행렬: Xₖ는 회전 화살표의 합

xj ωjk를 이어 붙이기
FN의 위상 (행 = k, 열 = j)
Xk—
|Xk|—
ωN = e−2πi/N—
해볼 것: ① 신호 cos(2π·2j/N)에서 k를 0부터 올려 보세요. k = 2와 k = N − 2에서만 화살표가 한 방향으로 모여 |Xk| = N/2가 되고, 나머지 k에서는 사슬이 원점으로 돌아옵니다. ② '모두 1'이면 X₀ = N이고 나머지는 모두 0, 임펄스는 모든 Xk = 1입니다. ③ 오른쪽 행렬에서 k번째 행의 색이 j를 따라 k바퀴 도는 것, 행렬이 대칭((F)kj = (F)jk)인 것을 보세요. 모델: 왼쪽의 점선 원은 단위원, 회색 점은 ωjk의 위치.

표본화와 앨리어싱, 나이퀴스트

7 Hz가 3 Hz로 보인다

진동 센서를 초당 10번 읽었더니 3 Hz의 진동이 보였습니다. 그런데 실제 기계는 7 Hz로 떨고 있었습니다. 측정은 정확했고 계산에도 실수가 없었습니다. 표본만 보고는 두 진동을 구별할 방법이 원리적으로 없습니다.

표본화 주파수 fs=1/Δtf_s = 1/\Delta t로 cos⁡(2πft)\cos(2\pi f t)를 뜨면 표본은 cos⁡(2πfj/fs)\cos(2\pi fj/f_s)입니다. ff에 fsf_s의 정수배를 더하거나 부호를 바꿔도 표본은 같습니다(cos⁡(2π(f+mfs)j/fs)=cos⁡(2πfj/fs+2πmj)\cos(2\pi(f + mf_s)j/f_s) = \cos(2\pi fj/f_s + 2\pi mj)). 그래서 표본이 보여 줄 수 있는 주파수는 [0,fs/2][0, f_s/2]뿐이고, 그 밖의 주파수는 이 구간 안으로 접혀 들어옵니다. 이것이 앨리어싱(aliasing)입니다. 7 Hz를 fs=10f_s = 10으로 뜨면 ∣7−10∣=3|7 - 10| = 3 Hz로 보입니다.

정리 8.5 (표본화 정리, 나이퀴스트–섀넌) 연속 신호 x(t)x(t)의 푸리에 변환이 ∣f∣<B|f| \lt B 밖에서 0이면(대역 제한), fs>2Bf_s \gt 2B로 뜬 표본으로부터
x(t)=∑n=−∞∞x(nΔt) sinc ⁣(t−nΔtΔt),sinc(u)=sin⁡πuπux(t) = \sum_{n=-\infty}^{\infty} x(n\Delta t)\,\mathrm{sinc}\!\left(\frac{t - n\Delta t}{\Delta t}\right),\qquad \mathrm{sinc}(u) = \frac{\sin\pi u}{\pi u}
로 xx를 정확히 복원할 수 있다. fs/2f_s/2를 나이퀴스트 주파수(Nyquist frequency)라 한다.

겉보기 주파수는 falias=∣f−fs⋅round(f/fs)∣f_{\text{alias}} = |f - f_s\cdot\mathrm{round}(f/f_s)|입니다. 실무의 처방은 두 가지입니다. 표본화 전에 아날로그 저역 통과 필터(앤티앨리어싱 필터)로 fs/2f_s/2 위의 성분을 지우거나, 관심 대역보다 충분히 빠르게 뜹니다. CD 오디오가 사람의 가청 한계 약 20 kHz의 두 배를 조금 넘는 44.1 kHz를 쓰는 이유입니다. 일단 표본에 섞여 들어온 앨리어스는 어떤 계산으로도 되돌릴 수 없습니다(MathBook: 연속을 격자로 자르기).

SIMULATOR

앨리어싱: 표본화 주파수 슬라이더

t ∈ [0, 1]초: 실제 신호 · 표본 · 표본이 말하는 신호
주파수 축: 접힘
실제 신호표본겉보기 신호
나이퀴스트 fs/2—
겉보기 주파수—
주기당 표본 수—
해볼 것: ① 기본값(f = 7, fs = 10)에서 빨간 표본이 3 Hz 곡선 위에 정확히 놓이는 것을 보세요. ② fs를 천천히 올려 14를 넘기는 순간 겉보기 주파수가 7 Hz로 돌아옵니다. ③ fs = f로 맞추면 표본이 모두 같은 값이 되어 신호가 상수(0 Hz)로 보입니다. 바퀴가 멈춰 보이는 영화 속 마차 바퀴와 같은 원리입니다. 모델: x(t) = cos(2πft), 표본 t = n/fs.

쿨리–튜키 FFT: 짝·홀 분할과 나비 연산

백만 점의 스펙트럼

N=220≈106N = 2^{20} \approx 10^6개 표본의 DFT를 정의대로 계산하면 복소 곱셈이 N2≈1.1×1012N^2 \approx 1.1\times10^{12}번입니다. 초당 10910^9번 곱하는 컴퓨터로 18분이 넘게 걸립니다. 그런데 FNF_N의 원소는 N2N^2개가 아니라 NN가지 값(ωN\omega_N의 거듭제곱)뿐입니다. 이 반복되는 구조를 이용할 수 없을까요?

NN이 짝수일 때 합을 짝수 번째와 홀수 번째로 나눕니다. ωN2=ωN/2\omega_N^2 = \omega_{N/2}이므로

Xk=∑m=0N/2−1x2m ωN/2mk+ωNk∑m=0N/2−1x2m+1 ωN/2mk=Ek+ωNk Ok.X_k = \sum_{m=0}^{N/2-1} x_{2m}\,\omega_{N/2}^{mk} + \omega_N^k\sum_{m=0}^{N/2-1} x_{2m+1}\,\omega_{N/2}^{mk} = E_k + \omega_N^k\,O_k.
EkE_k, OkO_k는 각각 짝수 표본, 홀수 표본의 N/2N/2점 DFT다. 이들은 kk에 대해 주기 N/2N/2이고, ωNk+N/2=−ωNk\omega_N^{k + N/2} = -\omega_N^k이다.
정리 8.6 (나비 연산) k=0,…,N/2−1k = 0, \dots, N/2 - 1에 대해
Xk=Ek+ωNkOk,Xk+N/2=Ek−ωNkOk.X_k = E_k + \omega_N^kO_k,\qquad X_{k + N/2} = E_k - \omega_N^kO_k.
곧 길이 NN의 DFT는 길이 N/2N/2의 DFT 두 개와, 곱셈 한 번(회전 인자(twiddle factor) ωNk\omega_N^k)과 덧셈·뺄셈 한 번씩인 나비 연산(butterfly) N/2N/2개로 계산된다.

이를 N=2pN = 2^p까지 되풀이하는 것이 기수 2 쿨리–튜키 FFT(Cooley–Tukey FFT)입니다. 비용을 T(N)T(N)이라 하면 T(N)=2T(N/2)+cNT(N) = 2T(N/2) + cN, T(1)=0T(1) = 0이므로 T(N)=cNlog⁡2NT(N) = cN\log_2N입니다. 정확히는 log⁡2N\log_2N단계마다 나비가 N/2N/2개, 복소 곱셈 N2log⁡2N\frac N2\log_2N번입니다(ω0=1\omega^0 = 1 같은 자명한 곱셈을 빼면 더 적다).

NN직접 DFT N2N^2FFT N2log⁡2N\frac N2\log_2N비율
8864125.3
102410241,048,5765,120205
204820484,194,30411,264372
2202^{20}1.10×10¹²1.05×10⁷104,858

재귀를 펼쳐 놓으면 입력이 x0,x4,x2,x6,x1,x5,x3,x7x_0, x_4, x_2, x_6, x_1, x_5, x_3, x_7(N=8N = 8) 순서로 맨 아래에 놓입니다. 짝·홀 분할을 세 번 했으니 인덱스를 2진수로 쓰고 비트를 뒤집은 순서입니다(1=0012→1002=41 = 001_2 \to 100_2 = 4). 그래서 실제 구현은 먼저 배열을 비트 반전 순서로 바꾼 뒤, 길이 2, 4, 8, …의 나비를 제자리에서 계산합니다. 추가 메모리가 필요 없습니다.

함수 FFT(x)                             // 제자리, N = 2^p
  for i = 0 … N−1                        // 1단계: 비트 반전 순서로 재배열
    j ← bitreverse(i, p)
    if i < j then swap(x[i], x[j])
  len ← 2
  while len ≤ N                         // log₂N 단계
    w ← exp(−2πi / len)                   // 이 단계의 회전 인자 밑
    for s = 0, len, 2·len, … < N          // 블록마다
      t ← 1
      for k = 0 … len/2 − 1              // 나비 len/2개
        a ← x[s+k];  b ← t · x[s+k+len/2]
        x[s+k] ← a + b;  x[s+k+len/2] ← a − b
        t ← t · w
    len ← 2·len
  반환 x                                // 역변환: w의 부호를 바꾸고 N으로 나눈다
예제 8.3 (예제 8.2를 FFT로) x=(1,2,3,4)\mathbf x = (1, 2, 3, 4).
  1. 짝수 표본 (1,3)(1, 3)의 2점 DFT: E=(1+3, 1−3)=(4,−2)E = (1 + 3,\ 1 - 3) = (4, -2). 홀수 표본 (2,4)(2, 4): O=(6,−2)O = (6, -2).
  2. 회전 인자 ω40=1\omega_4^0 = 1, ω41=−i\omega_4^1 = -i. X0=4+6=10X_0 = 4 + 6 = 10, X2=4−6=−2X_2 = 4 - 6 = -2.
  3. X1=−2+(−i)(−2)=−2+2iX_1 = -2 + (-i)(-2) = -2 + 2i, X3=−2−(−i)(−2)=−2−2iX_3 = -2 - (-i)(-2) = -2 - 2i. 예제 8.2와 같다. 자명하지 않은 곱셈은 (−i)⋅(−2)(-i)\cdot(-2) 하나뿐이었다.

회전 인자 t←t⋅wt \leftarrow t\cdot w를 반복 곱으로 만들면 반올림 오차가 쌓이므로, 정밀한 구현은 미리 cos⁡\cos, sin⁡\sin 표를 만들어 둡니다. 그렇게 하면 FFT의 상대 오차는 O(εmachlog⁡N)O(\varepsilon_{\text{mach}}\log N)으로, 정의대로 계산한 DFT보다 오히려 정확합니다. NN이 2의 거듭제곱이 아니어도 N=N1N2N = N_1N_2로 인수분해되면 같은 분할이 되고(혼합 기수), 소수 NN도 블루스타인 방법 등으로 O(Nlog⁡N)O(N\log N)에 계산됩니다. FFTW 같은 라이브러리는 기계에 맞는 분할 순서를 실행 중에 골라 씁니다.

SIMULATOR

8점 FFT 나비 그림 한 단계씩

단계
이 단계의 나비 수—
지금까지 복소 곱셈 (자명한 것 제외)—
직접 DFT와의 최대 차이—
해볼 것: ① '비트 반전'에서 왼쪽 열의 인덱스 순서 0, 4, 2, 6, 1, 5, 3, 7을 2진수로 읽어 보세요. ② 단계를 넘길 때마다 강조된 나비(위 a + ωb, 아래 a − ωb)가 이웃 2개 → 4개 → 8개 묶음으로 넓어집니다. 선 위의 숫자는 곱해지는 회전 인자 ωlenk입니다. ③ 마지막 단계의 결과가 직접 DFT와 반올림 수준으로 같습니다. 자명한 곱셈(1, −i)을 빼면 8점 FFT의 진짜 복소 곱셈은 2번뿐입니다. 모델: 기수 2, 시간 솎음(decimation in time).
SIMULATOR

O(N²) vs O(N log N): 이 브라우저에서 시간 재기

직접 DFT (측정)FFT (측정)기준선
직접 DFT 기울기—
FFT 기울기—
같은 N에서 속도 비—
측정 준비 중…
해볼 것: ① 로그–로그 그래프에서 직접 DFT의 기울기는 2, FFT의 기울기는 1을 조금 넘습니다(log N 인자). ② 직접 DFT 최대 크기를 2¹³까지 올리고 다시 측정하면 직접 DFT 한 번에 수백 ms가 걸립니다. 크기를 두 배로 할 때마다 네 배씩 늘어납니다. FFT는 2¹⁸까지 재도 순식간입니다. ③ 작은 N에서는 측정이 들쭉날쭉합니다. 시간 측정 해상도와 JIT 컴파일 때문입니다. 모델: 회전 인자는 직접 DFT도 표로 미리 계산, 각 크기는 5 ms 이상 반복해 평균.

빠른 합성곱과 다항식·큰 수 곱셈

백만 자리 곱셈

두 nn자리 수를 초등학교 방식으로 곱하면 자릿수 곱이 n2n^2번입니다. 원주율을 수조 자리까지 계산하는 프로그램은 백만 자리가 훌쩍 넘는 수를 수없이 곱해야 합니다. n2=1012n^2 = 10^{12}번의 곱셈을 매번 할 수는 없습니다. 곱셈의 정체를 다시 보면, 자릿수 배열의 합성곱입니다.

다항식 a(x)=∑aixia(x) = \sum a_ix^i와 b(x)=∑bjxjb(x) = \sum b_jx^j의 곱의 계수는 ck=∑i+j=kaibjc_k = \sum_{i + j = k}a_ib_j, 곧 계수 배열의 (선형) 합성곱(convolution) c=a∗b\mathbf c = \mathbf a * \mathbf b입니다. 정수 123×45123 \times 45는 x=10x = 10에서의 다항식 곱 (3+2x+x2)(5+4x)(3 + 2x + x^2)(5 + 4x)입니다. 직접 계산은 O(nm)O(nm)인데, DFT는 합성곱을 곱셈으로 바꿉니다.

정리 8.7 (합성곱 정리) 길이 NN의 원형 합성곱 (a⊛b)k=∑j=0N−1ajb(k−j) mod N(\mathbf a \circledast \mathbf b)_k = \sum_{j=0}^{N-1}a_jb_{(k - j)\bmod N}에 대해
DFT(a⊛b)=DFT(a)⊙DFT(b)(⊙는 성분별 곱).\mathrm{DFT}(\mathbf a \circledast \mathbf b) = \mathrm{DFT}(\mathbf a)\odot\mathrm{DFT}(\mathbf b)\qquad(\odot\text{는 성분별 곱}).

증명. ∑k∑jajbk−jωk⋅l=∑jajωjl∑kbk−jω(k−j)l\sum_k\sum_ja_jb_{k-j}\omega^{k\cdot l} = \sum_ja_j\omega^{jl}\sum_k b_{k-j}\omega^{(k-j)l}. 안쪽 합은 k−jk - j를 NN을 법으로 한 바퀴 돌므로 BlB_l이다. ∎

원형 합성곱은 끝을 넘어간 항이 앞으로 감겨 들어옵니다. 다항식 곱셈에 필요한 것은 선형 합성곱이므로, 두 배열 뒤에 0을 붙여 길이를 N≥n+m−1N \ge n + m - 1로 늘리면 감겨 들 항이 모두 0이 되어 두 합성곱이 같아집니다.

함수 FFT_곱셈(a, b)                       // 길이 n, m의 계수 배열
  N ← n + m − 1 이상인 가장 작은 2의 거듭제곱
  A ← FFT(a 뒤에 0을 붙여 길이 N)
  B ← FFT(b 뒤에 0을 붙여 길이 N)
  C ← A ⊙ B                                 // N번의 곱셈
  c ← IFFT(C)의 앞 n + m − 1개
  반환 c                                    // 정수 계수면 반올림. 큰 수면 받아올림 처리
예제 8.4 (다항식과 정수)
  1. (1+2x+3x2)(4+5x)(1 + 2x + 3x^2)(4 + 5x): a=(1,2,3)\mathbf a = (1, 2, 3), b=(4,5)\mathbf b = (4, 5), N=4N = 4. 합성곱 c=(4, 5+8, 10+12, 15)=(4,13,22,15)\mathbf c = (4,\ 5 + 8,\ 10 + 12,\ 15) = (4, 13, 22, 15), 곧 4+13x+22x2+15x34 + 13x + 22x^2 + 15x^3.
  2. 제로 패딩 없이 길이 3의 원형 합성곱을 하면 15x315x^3항이 x0x^0자리로 감겨 (19,13,22)(19, 13, 22)이 된다. 틀린 답이다.
  3. 123×45123\times45: 낮은 자리부터 (3,2,1)∗(5,4)=(15,22,13,4)(3, 2, 1) * (5, 4) = (15, 22, 13, 4). 받아올림: 15→515 \to 5 올림 1, 22+1=23→322 + 1 = 23 \to 3 올림 2, 13+2=15→513 + 2 = 15 \to 5 올림 1, 4+1=54 + 1 = 5. 답 55355535.

비용은 FFT 세 번과 곱셈 NN번, 합쳐서 O(Nlog⁡N)O(N\log N)입니다. 큰 수 곱셈에서는 합성곱 값이 n⋅92n\cdot 9^2까지 커지므로 부동소수점 반올림이 정수 반올림을 망치지 않도록 한 칸에 담는 자릿수를 조절하거나, 반올림 오차가 없는 수론적 변환(유한체에서의 DFT)을 씁니다. 같은 원리로 신호의 FIR 필터, 이미지 흐림(MathBook: 모든 것은 주파수의 합), 확률 분포의 합의 분포를 FFT로 계산합니다. 필터 길이가 수십 이하이면 직접 합성곱이 더 빠르고, 수백 이상이면 FFT가 이깁니다.

SIMULATOR

FFT 합성곱: 다항식 · 큰 수 · 이동 평균

예
FFT 길이 N—
결과—
직접 계산과의 최대 차이—
해볼 것: ① 다항식 (1, 2, 3)·(4, 5)에서 '제로 패딩 없이'를 켜면 마지막 계수 15가 첫 칸으로 감겨 19가 됩니다. ② '큰 수'에서 두 수를 30자리 넘게 입력해도 FFT 곱이 BigInt 직접 곱셈과 일치합니다. ③ '이동 평균'에서 원형 합성곱을 켜면 신호의 오른쪽 끝 값이 왼쪽 시작 부분에 섞여 들어옵니다. 모델: 큰 수는 한 칸에 10진 한 자리, 결과는 반올림 후 받아올림.

스펙트럼 누설, 창 함수, 제로 패딩

봉우리가 번지고 작은 성분이 묻힌다

2절의 시뮬레이터는 주파수를 정수 Hz로만 골랐습니다. 실제 신호의 주파수는 관측 시간에 정수 개의 주기가 들어맞을 이유가 없습니다. 10.5 Hz를 1초 동안 재면 스펙트럼의 봉우리가 낮아지고 옆 칸으로 넓게 번져, 근처의 약한 성분을 덮어 버립니다.

DFT는 관측한 NN개 표본이 한 주기인 주기 신호라고 가정합니다. 주기가 정수 개 들어맞지 않으면 끝과 처음이 이어지는 곳에 불연속이 생기고, 불연속의 계수는 1/k1/k로 느리게 줄었습니다(7장 정리 7.10). 수학적으로는 무한한 사인파에 길이 NN의 사각 창을 곱한 것이고, 곱셈은 주파수 영역에서 창의 스펙트럼 ∣sin⁡(πk)/sin⁡(πk/N)∣|\sin(\pi k)/\sin(\pi k/N)|과의 합성곱입니다. 사인파 하나의 에너지가 모든 칸으로 새어 나가는 이 현상을 스펙트럼 누설(spectral leakage)이라 합니다.

처방은 신호의 양 끝을 부드럽게 0으로 줄이는 창 함수(window function) wjw_j를 곱한 뒤 DFT하는 것입니다. 끝의 불연속이 사라져 누설이 빠르게 줄어드는 대신, 봉우리의 폭(주엽)이 넓어집니다. 주파수 분해능과 누설 억제를 맞바꾸는 셈입니다.

창 (N=64N = 64)식 wjw_j주엽 반폭최대 부엽칸 사이 손실
사각11칸−13.3 dB−3.92 dB
한(Hann)12−12cos⁡2πjN\frac12 - \frac12\cos\frac{2\pi j}{N}2칸−31.5 dB−1.42 dB
블랙먼0.42−0.5cos⁡2πjN+0.08cos⁡4πjN0.42 - 0.5\cos\frac{2\pi j}{N} + 0.08\cos\frac{4\pi j}{N}3칸약 −58 dB약 −1.1 dB

표의 값은 창의 스펙트럼을 아주 촘촘히 계산해 잰 것입니다. "칸 사이 손실"은 주파수가 두 칸의 정중앙(예: 10.5)에 있을 때 봉우리가 얼마나 낮아지는지입니다. 사각 창으로 N=64N = 64, 10.5 Hz를 재면 진폭 1이 0.646(−3.79 dB)으로 읽히고 약 20칸 떨어진 곳에서도 −26 dB가 남지만, 한 창은 0.849(−1.42 dB)로 읽히고 같은 곳에서 −93 dB까지 떨어집니다.

제로 패딩(zero padding)은 표본 뒤에 0을 붙여 더 긴 DFT를 하는 것입니다. 같은 연속 스펙트럼(창을 곱한 신호의 이산 시간 푸리에 변환)을 더 촘촘한 간격으로 표본하는 효과라, 봉우리의 위치와 높이를 더 정확히 읽을 수 있습니다. 하지만 분해능, 곧 가까운 두 주파수를 구별하는 능력은 관측 시간 TT로 정해진 1/T1/T에서 나아지지 않습니다. 정보는 0을 붙인다고 늘지 않습니다. 두 성분이 1칸보다 가까우면 더 오래 측정하는 수밖에 없습니다.

SIMULATOR

창 함수와 누설, 제로 패딩

창을 곱한 신호 (N = 64)
스펙트럼 (dB, 진폭 1 = 0 dB)
창
제로 패딩
스펙트럼패딩 없는 DFT 칸참 주파수
읽은 봉우리 위치—
읽은 봉우리 높이—
약한 성분 위치(±½칸)의 누설 최대—
해볼 것: ① 사각 창에서 주파수를 10 → 10.5로 바꾸면 빨간 칸들이 모두 들려 누설이 생깁니다. 정수 칸이면 누설이 없습니다. ② 약한 성분(−40 dB, 6칸 위)이 사각 창에서는 누설에 묻히고, 한·블랙먼 창에서는 봉우리로 드러납니다. −50 dB로 낮추면 한 창의 누설(그 자리에서 약 −54 dB)과 비슷해져 겨우 보이고, 블랙먼(약 −63 dB)에서는 뚜렷합니다. ③ 위치 차이를 1칸 이하로 줄이면 16배 패딩으로도 두 봉우리가 갈라지지 않습니다. 패딩은 곡선을 촘촘히 그릴 뿐, 분해능 1/T를 바꾸지 못합니다. 모델: 주기형 창(분모 N), 진폭은 창 이득으로 보정.

응용: 오디오, 이미지 압축, 스펙트럴 미분

오디오 스펙트럼. 음악 분석기와 음성 인식은 신호를 짧은 구간(예: 44.1 kHz에서 2048표본, 약 46 ms)으로 잘라 한 창을 곱하고 FFT를 반복합니다. 이것이 단시간 푸리에 변환이고, 그 크기를 시간–주파수 평면에 그린 것이 스펙트로그램입니다. 구간 길이가 주파수 분해능(44100/2048≈21.544100/2048 \approx 21.5 Hz)과 시간 분해능을 맞바꿉니다.

이미지 압축 맛보기. JPEG은 영상을 8×88\times8 블록으로 나눠 각 블록에 2차원 이산 코사인 변환(DCT)을 합니다. DCT는 신호를 좌우 대칭으로 늘여 붙인 뒤의 DFT라, 블록 경계의 불연속이 없어 누설이 적고 에너지가 낮은 주파수 몇 개에 모입니다. 높은 주파수 계수를 거칠게 양자화하거나 버려서 용량을 줄입니다(MathBook: 모든 것은 주파수의 합). 7장의 체비쇼프 계수가 바로 DCT로 계산되었다는 것도 같은 이야기입니다.

스펙트럴 미분. 주기 함수를 미분하면 eikxe^{ikx}의 계수가 ikik배가 됩니다. 그래서 FFT → ikik 곱하기 → 역 FFT로 표본점에서의 도함수를 얻습니다(짝수 NN에서 나이퀴스트 계수는 0으로 둔다). 매끄러운 주기 함수에서는 7장의 해석적 함수처럼 계수가 기하급수로 줄기 때문에 오차가 NN에 대해 지수적으로 줄어듭니다. 9장의 차분 공식과 비교해 봅시다.

예제 8.5 (esin⁡xe^{\sin x}의 도함수, 표본점 최대 오차)
NN2차 중앙 차분4차 중앙 차분스펙트럴(FFT)
83.40×10⁻¹1.38×10⁻¹4.32×10⁻³
169.36×10⁻²1.58×10⁻²1.76×10⁻⁷
322.58×10⁻²1.13×10⁻³4.22×10⁻¹⁵
646.51×10⁻³7.59×10⁻⁵1.04×10⁻¹⁴

NN을 두 배로 할 때 차분 공식은 오차가 각각 약 4배, 16배 주는 대수적 수렴(O(h2)O(h^2), O(h4)O(h^4))이고, 스펙트럴 미분은 N=32N = 32에서 이미 반올림 바닥에 닿는다. 64에서 조금 커진 것은 kk가 커질수록 ikik가 반올림 오차를 증폭하기 때문이다. 이 정확도가 날씨 예보 모형과 난류 시뮬레이션에서 쓰는 스펙트럴 방법의 바탕이다. 단, 주기적이고 매끄러운 함수에서만 통한다(16장).

그 밖에. 7장에서 미룬 등간격 주기 데이터의 삼각 최소제곱은 DFT 계수를 ∣k∣≤n|k| \le n까지만 남기는 것입니다(이산 직교성 덕분에 정규 방정식이 대각). 2차원 FFT는 푸아송 방정식을 주기 경계에서 O(N2log⁡N)O(N^2\log N)에 풀고(16장), MRI는 측정한 k-공간 데이터를 2차원 역 FFT로 영상으로 바꿉니다.

이 도구가 쓰이는 곳

분야FFT가 하는 일주의할 점
무선 통신(OFDM: 와이파이, LTE, 5G)역 FFT로 부반송파 합성, FFT로 분리순환 전치로 다중 경로 지연을 원형 합성곱으로 만든다
진동·소음 진단회전 기계의 스펙트럼, 베어링 결함 주파수 찾기앤티앨리어싱 필터, 창 함수, 평균화
오디오·음성스펙트로그램, 이퀄라이저, 압축(MP3·AAC의 변형 DCT)구간 길이로 시간–주파수 분해능 맞바꾸기
영상JPEG의 DCT, 큰 커널의 흐림·선명화, MRI 재구성2차원 FFT, 경계 처리(패딩)
과학 계산스펙트럴 방법, 주기 푸아송 풀이, 큰 수 곱셈주기성·매끄러움 가정, 반올림 오차 관리

핵심 정리

  1. 등간격 점 xj=2πj/Nx_j = 2\pi j/N에서 eikxe^{ikx}들은 이산 직교한다. 그래서 주기 데이터의 삼각 보간 계수는 내적 하나, 곧 DFT Fk=∑fje−ikxjF_k = \sum f_je^{-ikx_j}로 바로 나온다.
  2. DFT는 X=FNxX = F_N\mathbf x, (FN)kj=ωNjk(F_N)_{kj} = \omega_N^{jk}, ωN=e−2πi/N\omega_N = e^{-2\pi i/N}. 역변환은 1NFN‾\frac1N\overline{F_N}이고 FN/NF_N/\sqrt N은 유니터리(파스발, κ=1\kappa = 1). 주파수 간격은 1/T1/T, 실수 신호의 진폭은 2∣Xk∣/N2|X_k|/N으로 읽는다.
  3. 표본화 주파수 fsf_s에서 ff와 f+mfsf + mf_s, −f-f는 구별되지 않는다. 보이는 대역은 [0,fs/2][0, f_s/2](나이퀴스트)이고 그 밖의 성분은 접혀 들어온다. 앨리어싱은 표본화 전에 막아야 한다.
  4. 쿨리–튜키 FFT: Xk=Ek+ωNkOkX_k = E_k + \omega_N^kO_k, Xk+N/2=Ek−ωNkOkX_{k+N/2} = E_k - \omega_N^kO_k. log⁡2N\log_2N단계 × 나비 N/2N/2개 = O(Nlog⁡N)O(N\log N). 입력은 비트 반전 순서, 계산은 제자리.
  5. DFT는 원형 합성곱을 성분별 곱으로 바꾼다. 길이 ≥n+m−1\ge n + m - 1로 제로 패딩하면 선형 합성곱(다항식 곱, 큰 수 곱)을 O(Nlog⁡N)O(N\log N)에 얻는다. 패딩이 모자라면 끝 항이 앞으로 감긴다.
  6. 정수 개 주기가 아니면 스펙트럼 누설이 생긴다. 창 함수(한, 블랙먼)는 부엽을 −31 dB, −58 dB 수준으로 낮추는 대신 주엽을 넓힌다. 제로 패딩은 스펙트럼을 촘촘히 표본할 뿐 분해능 1/T1/T는 그대로다.
  7. 매끄러운 주기 함수의 스펙트럴 미분(FFT → ikik → 역 FFT)은 오차가 NN에 대해 지수적으로 준다. esin⁡xe^{\sin x}는 N=32N = 32에서 4×10−154\times10^{-15}.

확인 퀴즈

Q1. x=(1,0,−1,0)\mathbf x = (1, 0, -1, 0)의 DFT X=(X0,X1,X2,X3)X = (X_0, X_1, X_2, X_3)는?

ω4=−i\omega_4 = -i. X0=1−1=0X_0 = 1 - 1 = 0, X1=1+(−1)(−1)=2X_1 = 1 + (-1)(-1) = 2, X2=1+(−1)(1)=0X_2 = 1 + (-1)(1) = 0, X3=1+(−1)(−1)=2X_3 = 1 + (-1)(-1) = 2. 신호가 cos⁡(πj/2)\cos(\pi j/2)이므로 k=1k = 1과 k=3=−1k = 3 = -1에 N/2=2N/2 = 2씩 나타난다.

Q2. 초당 1000번 표본화한 신호에서 스펙트럼에 120 Hz 봉우리가 보였다. 실제 신호가 될 수 없는 주파수는?

겉보기 주파수는 ∣f−1000m∣|f - 1000m|이다. 880 → 120, 1120 → 120. 620 Hz는 ∣620−1000∣=380|620 - 1000| = 380으로 보인다. 나이퀴스트 500 Hz 위의 성분은 500을 기준으로 접혀 들어온다.

Q3. N=4096N = 4096일 때 기수 2 FFT의 나비 연산 수는?

log⁡24096=12\log_2 4096 = 12단계, 단계마다 N/2=2048N/2 = 2048개. 12×2048=2457612\times2048 = 24576. 직접 DFT의 N2=16,777,216N^2 = 16{,}777{,}216과 비교하면 약 680배 적다.

Q4. 8점 FFT의 비트 반전 재배열에서 인덱스 3은 어디로 가는가?

3=01123 = 011_2을 뒤집으면 1102=6110_2 = 6. 전체 순서는 0, 4, 2, 6, 1, 5, 3, 7이다.

Q5. 길이 300과 길이 200의 배열을 FFT로 선형 합성곱하려 한다. 감김 없이 쓸 수 있는 가장 짧은 2의 거듭제곱 FFT 길이는?

결과 길이는 300+200−1=499300 + 200 - 1 = 499이므로 N≥499N \ge 499, 2의 거듭제곱으로 512. 256으로 하면 원형 합성곱의 감김이 생긴다.

Q6. 1초 동안 잰 신호에 10.0 Hz와 10.4 Hz 성분이 있다. 두 성분을 구별하려면?

분해능은 관측 시간의 역수 1/T=11/T = 1 Hz다. 0.4 Hz 차이를 가르려면 TT가 적어도 1/0.4=2.51/0.4 = 2.5초, 창의 주엽 폭까지 생각하면 그 몇 배가 필요하다. 제로 패딩과 표본화 주파수는 TT를 바꾸지 않고, 창은 주엽을 오히려 넓힌다.

Q7. 사각 창 대신 한 창을 쓸 때의 변화로 옳은 것은?

창 함수는 누설(부엽)과 분해능(주엽 폭)을 맞바꾼다. 한 창의 주엽 반폭은 2칸으로 사각의 1칸보다 넓다. 정수 칸 주파수의 한 창 스펙트럼은 이웃 두 칸에 −6 dB씩 퍼지지만 그 밖의 칸에는 새지 않는다.

Q8. 매끄러운 주기 함수의 도함수를 스펙트럴 방법으로 계산할 때 오차의 거동은?

해석적 주기 함수의 푸리에 계수는 기하급수로 줄기 때문에, 계수를 N/2까지 쓰는 스펙트럴 미분의 오차도 지수적으로 준다. 예제 8.5에서 e^(sin x)는 N = 32에서 10⁻¹⁵ 수준이었다.