[경고] 아래 글을 읽지 않고 "고속 푸리에 변환"을 보면 바보로 느껴질 수 있습니다.
[고속 푸리에 변환의 쉬운 이해]
통신 이론과 신호 처리에 필수적인 고속 푸리에 변환(fast Fourier transform, FFT)은 이산 푸리에 변환(discrete Fourier transform, DFT)의 항들을 우함수(even function)와 기함수(odd function)로 모아서 급수를 빠르게 계산하기 위한 혁신적인 알고리즘이다[1], [2]. FFT는 1805년가우스 28세, 조선 순조 시절 무렵에 가우스Carl Friedrich Gauss(1777–1855)가 먼저 고안해 사용했다는 전설을 가지고 있다.[푸리에 교수가 푸리에 급수(Fourier series)를 공개한 시점이 1807년이다.] 현대적인 FFT는 뼈대 있는 수학자 룽에Carl Runge(1856–1927)의 1903년룽에 47세, 대한제국 시절 기여를 거쳐 미국 수학자 쿨리James Cooley(1926–2016)와 투키John Tukey(1915–2000)가 1965년쿨리 39세, 박정희 정부 시절에 재발견했다[3]. 쿨리와 투키가 FFT를 구현한 방법론은 쿨리–투키 알고리즘(Cooley–Tukey algorithm)으로 부른다. 이 알고리즘의 유도는 기존 DFT에 대한 관찰로부터 시작한다.

여기서 은 표본 개수, 은 시계열(time sequence), 는 푸리에 계수(Fourier coefficient)이다. DFT의 모든 항을 항상 이등분할 수 있도록, FFT에서는 보통 을 2의 거듭제곱인 = 으로 가정한다. 여기서 은 1이상의 자연수이다. 먼저 식 (1)에서 복소 지수 함수를 으로 치환해서 멱급수(power series) 형태로 다시 쓴다.

복소 지수 함수 의 거듭제곱은 다음 성질을 가진다.


그 다음에 식 (2)를 이등분해서 DFT 항을 에 대한 우함수와 기함수인 부분으로 나눈다.


여기서 = , 원래 DFT 급수를 1번 분해한 와 는 각각 짝수 및 홀수 밑수를 가진 만을 모은 다항식이다. 그러면 식 (3a)의 둘째식과 식 (3b)의 첫째식을 이용해서 를 다시 쓸 수 있다.

여기서 = 이다. 그러면 식 (6)을 다시 계산할 필요없이, 식 (4)에서 구한 과 의 결과값에서 부호만 ( )에서 ( )로 바꾸면 된다. 즉, DFT 급수를 우함수와 기함수로 분해함으로써 계산 속도를 2배만큼 증가시킬 수 있다. 이 방식을 염두에 두고 식 (4)를 새롭게 보면, 와 는 항의 개수가 반[= ]만큼 줄어든 또 다른 DFT 급수이다. 그래서 재귀적인 방법으로 식 (5)를 다시 우함수와 기함수 관련 다항식으로 모아서 계속 연산을 가속화시킨다. 이와 같은 기법이 바로 쿨리–투키 알고리즘의 핵심이. 예를 들어, 재귀 관계에서 과 은 다음 다항식으로 재분해된다.




여기서 = 이다. 식 (8a)에서 와 는 각각 를 구성하는 의 짝수차와 홀수차 항을 모아서 만든 다항식이다. 비슷하게 와 는 각각 의 짝수차와 홀수차 항으로 구성한다.
좀더 쉽게 쿨리–투키 알고리즘을 이해하기 위해, 예시적으로 = 인 경우부터 출발해 식 (4)를 다시 기술한다.


[그림 1] 기본 나비 도표(사진 출처: wikipedia.org)

[그림 2] 변형 나비 도표
[그림 1]은 나비를 닮아서 나비 도표(butterfly diagram)라고 명한다. 기본 나비 도표의 에 를 곱한 변형은 [그림 2]처럼 화살표를 하나 추가해서 그린다. 표본 크기를 더 증가시킨 = 인 경우도 식 (4)를 바탕으로 우함수와 기함수를 분리한다.


[그림 3] 나비 도표
식 (10)을 나비 도표로 나타낸 결과인 [그림 3]은 약간 복잡하지만 명확한 규칙이 보인다. 시계열 를 시작할 때, 를 이진수로 나타내어 비트를 역전시킨다. 이 과정은 FFT의 비트 역전(bit reversal)이라 이름 붙인다. 그러면 [그림 3]의 입력은 0, 2, 1, 3의 순서로 들어간다. 그 다음에 [그림 1]처럼 = 에 대한 FFT를 각각 수행하고, 이 결과를 모아서 각 화살표에 대해 = 에 대한 FFT 연산을 다시 적용한다. 식 (4)와 (7)은 비트 역전이 필요한 이유를 잘 보여주고 있다. 우리가 홀짝을 나눈 다음에 표본 개수 과 지수 를 1/2하고, 우함수와 기함수 관련 다항식을 각각 홀짝으로 다시 분류한다. 지수 를 줄이는 단계는 2의 거듭제곱으로 나누면서 계속 반복된다. 최종적으로 항이 2개만 남으면 멈춘 후, 나비 도표인 [그림 1]을 이용해 계산한다. 시계열 의 가 줄어서 마지막에 선택된 두 항[그림 1에서 0과 1로 표기]은 를 이진수로 표시할 때 최상위 비트(most significant bit, MSB)가 0이나 1인 경우이다. [그림 1]을 포함한 [그림 3]에서 나비 도표의 둘째 단계는 MSB의 그 다음 비트가 0이나 1을 가진 를 처리한다. 따라서 FFT에서는 [그림 3]처럼 비트 역전을 통해서 입력 시계열 를 두 개씩 모아야 한다.

[그림 4] 나비 도표
[그림 4]는 [그림 3]을 2개 나열해서 나비 도표를 구현한 결과를 보여준다. 시계열 의 는 비트 역전을 통해서 결정한다. 입력이 8개라서 우함수와 기함수 부분인 다항식 과 은 각각 4개의 표본을 가지고 있다. 식 (4)를 사용해서 유도한 = 인 FFT 관계식은 다음과 같다.

여기서 = , 과 은 [그림 3]으로 계산한다.
표본 개수가 = 이 아니더라도 [1]을 참고해서 FFT를 수행할 수 있다. 하지만 이 2의 거듭제곱이 아니면 홀짝 모으기가 번거로워진다. 만약 표본이 작아서 < 이면, 을 증가시켜 강제로 = 을 만들고 모르는 는 으로 채워서 FFT를 돌린다. 이런 부가적인 과정은 FFT의 영 채우기(zero padding)로 이름 붙인다. 영 채우기의 조건은 붙여넣은 의 크기가 매우 작아서 0으로 근사할 수 있다는 가정이다. 예를 들어, 개로 영 채우기를 하면 표본수는 으로 늘고 전체 표본화 시간은 = 로 증가하다. 여기서 는 표본화 주기(sampling period)이다. 이로 인해 주파수 영역의 각주파수는 = 로 변한다. 여기서 = , , ; 각주파수 간격은 = 가 되어 이전보다 줄어든다. 그래서 시간 영역의 표본수를 늘리면, 그만큼 주파수 영역의 표본 간격이 축소된다. 하지만 의 범위는 = 이므로, 영 채우기로는 의 최대 한계를 늘릴 수 없다. 여기서 = 와 = 는 각각 표본화 주파수(sampling frequency)와 표본화 각주파수(sampling angular frequency)로 부른다.
주파수 영역에도 영 채우기가 가능하다. 기존 의 마지막 값인 이 0에 가깝다고 생각해서, 이상을 0으로 간주하고 원하는 만큼 0을 추가한다. 이 경우 표본화 주기 의 고정 유무에 따라 시간 영역에 나타나는 효과가 달라진다. 먼저 를 상수라 가정하고 시간 영역의 영 채우기와 동일한 논리를 적용한다. 그러면 영을 채운 회수 을 아무리 늘려도 의 영역[= = ]은 증가하지 않고 각주파수 간격 [= ]만 좁아진다. 이때 시간 영역에서는 전체 표본화 시간[= ]이 주파수 영역에서 영 채우기한 만큼 늘어난다. 반대로 가 바뀔 수 있지만 는 고정이면, 데칼코마니(décalcomanie)처럼 시간 영역의 영 채우기와 대칭적으로 생각한다. 영 채우기로 인해 표본화 각주파수는 로 늘어나지만 자체는 고정된다. 이 결과를 시간으로 가져가서 = = 로 둔다. 따라서 표본화 주기는 = 로 축소되고, 전체 표본화 시간은 = 로 변화 없다.
FFT의 기본 알고리즘은 간단해서 우리가 쉽게 프로그램할 수 있다. 그런데 많은 자료를 진짜 빠르게 처리하려면 전문적인 FFT 함수 집합 혹은 라이브러리(library)를 써야 한다. 우리가 쉽게 구할 수 있는 상용 FFT 라이브러리 중에 FFTW(FFT in the West)가 있다[4]. FFTW는 MATLAB에도 쓰일 정도로 잘 검증된 연산을 제공한다. 다만 FFTW는 비상업적인 용도로 쓸 때에만 무료로 이용할 수 있다. NVIDIA GPU를 사용하는 경우는 cuFFT[CUDA(Compute Unified Device Architecture) 기반 FFT]를 사용할 수 있다[5]. 여러 문헌에 나오는 결과를 종합하면, 표본수가 커질수록 cuFFT는 CPU를 쓰는 FFTW보다 훨씬 빠른 성능을 보인다.
FFT는 중앙화 고속 푸리에 변환(centered FFT, CFFT: CDFT의 고속화), 대칭 고속 푸리에 변환(symmetric FFT, SFFT: SDFT의 고속화), 일반화 고속 푸리에 변환(generalized FFT, GFFT: GDFT의 고속화) 등과 같은 변종이 많이 있다. 하지만 CFFT, SFFT를 위한 고속 알고리즘을 다시 제안하지 않고, 이미 구현된 FFT를 바꾸어서 CFFT, SFFT를 구현한다. CFFT는 순환 편이(circular shift) 연산자 를 도입해서 FFT로 재구성한다.

여기서 = , = , = ; 혹은 는 를 넘지 않는 바닥 함수(floor function), 는 표본 수열 을 만큼 편이시킨다. 이 개념을 CDFT(centered DFT) 공식에 넣어서 FFT를 포함하도록 변형한다.

CFFT의 역변환인 ICFFT(inverse centered FFT)도 IFFT(inverse FFT)와 로 계산한다.

표본 개수 이 홀수인 경우는 SFFT와 CFFT의 결과는 같다. 하지만 이 짝수가 되면 = 을 기준으로 음과 양인 이 대칭적이지 않아서 만큼 을 움직여야 한다. 즉, 홀수인 에서는 = ( = )를 써도 되지만 짝수에서는 = ( )로 만든다. 따라서 SFFT는 식 (13a)처럼 FFT와 를 활용하지만 FFT 전에 가 만드는 위상 기여를 곱한다.


이와 같이 FFT는 약방의 감초처럼 우리가 상상하는 다양한 푸리에 변환에 고속화라는 감칠맛을 더한다.
[참고문헌]
[1] J. W. Cooley and J. W. Tukey, "An algorithm for the machine calculation of complex Fourier series," Math. Comput., vol. 19, no. 90, pp. 297–301, Apr. 1965.
[2] E. O. Brigham and R. E. Morrow, "The fast Fourier transform," IEEE Spectr., vol. 4, no. 12, pp. 63–70, Dec. 1967.
[3] M. T. Heideman, D. H. Johnson, and C. S. Burrus, "Gauss and the history of the fast Fourier transform," IEEE ASSP Mag., vol. 1, no. 4, pp. 14–21, Oct. 1984.
[4] M. Frigo and S. G. Johnson, "C Subroutine Library for Computing the DFT," FFTW 3.3.10, 2022. (방문일 2022-10-04)
[5] cuFFT Library User's Guide, cuFFT API Reference, NVIDIA Corp., California, USA, 2022. (방문일 2022-10-04)
여러 자료를 읽어보았지만 나비 도표 모양으로 계산이 가능한 과정이 결론을 보고 계산해보니 맞더라 식으로 기술된 곳이 많아 이해에 애를 먹었는데 나비 도표의 구성을 기본 식에서 유도해주셔서 이해할 수 있었습니다. 감사합니다.
답글삭제익명님, 본 블로그의 지향점을 잘 느껴주셔서 감사합니다^^
삭제혹시나, 웨이블릿 변환에 대해 다루실 예정은 없으신가요?
답글삭제신호 처리에서 웨이블릿 변환(wavelet transform)도 참 중요한데요, 지금은 여유가 없네요 ㅠㅠ
삭제(2)번 식의 맨 오른쪽 항의 지수가 k(M-1)이 되어야 하는 것 아닌가요?
답글삭제틀렸네요. 지적 정말 감사해요, 익명님^^
삭제