길이 N인 DFT를 정의대로 계산하면 곱셈이 N²번 필요합니다. 고속 푸리에 변환(FFT)은 신호를 짝수·홀수 번째로 나누어 되풀이하는 방법으로 이것을 약 N log N번으로 줄입니다. 20세기의 가장 중요한 알고리즘 가운데 하나입니다.
FFT는 새로운 변환이 아닙니다. DFT(4장 정의 4.3)와 똑같은 값을 훨씬 적은 연산으로 계산하는 알고리즘입니다.
12.1 직접 계산의 비용
X[k]=∑n=0N−1x[n]e−j2πkn/N을 정의대로 계산하면 k마다 곱셈 N번, k가 N개이므로 N2번입니다. N=220≈106(오디오 약 24초)이면 약 1.1×1012번입니다. 초당 109번 곱하는 컴퓨터로 18분쯤 걸립니다. FFT는 같은 일을 약 2×107번, 0.02초에 합니다.
12.2 분할 정복: 기수 2 FFT
N이 짝수라 합시다. wN=e−j2π/N이라 쓰면 X[k]=∑nx[n]wNkn입니다. 합을 짝수 번째와 홀수 번째 표본으로 나눕니다.
정리 12.1 (FFT 점화식).E[k], O[k]를 짝수 표본열 x[0],x[2],…과 홀수 표본열 x[1],x[3],…의 길이 N/2 DFT라 하면, k=0,1,…,N/2−1에 대해
X[k]=E[k]+wNkO[k],X[k+N/2]=E[k]−wNkO[k]
입니다.
증명. 첫 식은 위에서 보였습니다. 둘째 식은 이렇습니다. E, O는 주기 N/2이므로 E[k+N/2]=E[k], O[k+N/2]=O[k]이고, wNk+N/2=wNke−jπ=−wNk입니다. ■
길이 N인 DFT 하나가 길이 N/2인 DFT 둘과 곱셈 N/2번, 덧셈 N번으로 계산됩니다. E[k]와 wNkO[k] 한 쌍에서 출력 두 개가 나오는 이 연산을 버터플라이(butterfly)라 합니다. N=2p이면 같은 일을 반복해 길이 1까지 내려갑니다. 길이 1인 DFT는 자기 자신입니다.
연산량. 길이 N의 비용을 C(N)이라 하면 C(N)=2C(N/2)+cN입니다. 한 단계의 작업량이 cN이고 단계가 log2N개이므로
C(N)=cNlog2N(직접계산은N2)
입니다. N=1024이면 N2≈106, Nlog2N≈104로 100배입니다. N=106이면 5만 배입니다. 이 차이 때문에 디지털 신호처리가 실용 기술이 되었습니다.
그림 12.1. N=4 FFT의 신호 흐름. 화살표 위의 수는 곱하는 값이고(없으면 1), 한 점으로 들어오는 화살표는 더합니다. 입력의 순서가 x[0],x[2],x[1],x[3]으로 바뀌어 있습니다(비트 반전 순서).
단계를 하나씩 진행하면 재배열된 입력이 버터플라이를 거쳐 DFT 값이 됩니다
이 위젯은 JavaScript가 필요합니다
12.3 구현
점화식을 그대로 옮긴 재귀 구현입니다. 실제로는 라이브러리를 쓰지만 한 번 손으로 따라가 볼 가치가 있습니다.
import numpy as np
def fft(x): # len(x) must be a power of 2
N = len(x)
if N == 1:
return np.array(x, dtype=complex)
E = fft(x[0::2]) # DFT of even-indexed samples
O = fft(x[1::2]) # DFT of odd-indexed samples
w = np.exp(-2j*np.pi*np.arange(N//2)/N)
return np.concatenate([E + w*O, E - w*O])
print(fft([1, 2, 3, 4])) # [10.+0.j -2.+2.j -2.+0.j -2.-2.j]
역변환. 역 DFT는 지수의 부호와 1/N만 다릅니다. x=N1FFT(X∗)∗이므로 같은 코드로 계산합니다.
제자리 계산. 실용 구현은 재귀 대신 입력을 비트 반전 순서(첨자를 이진수로 쓰고 뒤집은 순서)로 먼저 재배열한 뒤, 그림 12.1처럼 단계별로 버터플라이를 적용합니다. 추가 메모리가 필요 없습니다.
2의 거듭제곱이 아닌 N. 3이나 5로 나누는 혼합 기수 알고리즘, 소수 길이를 합성곱으로 바꾸는 방법이 있어 어떤 N이든 O(NlogN)에 계산됩니다. 다만 작은 소인수만 가진 N이 가장 빠릅니다.
실수 입력. 켤레 대칭(4장 정리 4.6) 때문에 절반만 계산하면 됩니다(numpy.fft.rfft).
12.4 FFT로 합성곱 계산하기
합성곱 정리(4장 정리 4.13)를 거꾸로 씁니다. 길이 L과 M인 두 수열의 합성곱을 직접 계산하면 곱셈이 LM번이지만, 다음 순서로 하면 O(NlogN)입니다.
두 수열 뒤에 0을 채워 길이 N≥L+M−1로 만듭니다.
각각 FFT합니다.
성분별로 곱합니다.
역 FFT합니다.
1단계의 영 채우기가 필수입니다. DFT의 합성곱은 순환 합성곱이라, 길이가 모자라면 결과의 뒷부분이 앞으로 감겨 들어와 겹칩니다.