Lecture 26. 복소 행렬과 푸리에 변환 Complex Matrices and the Fourier Transform — 서술
지난 강의에서 대칭행렬이 자기 자신의 직교기저를 들고 태어난다는 것을 보았다.
직교기저의 위력은 L17에서 이미 배웠다. 계수를 구하는 데 연립방정식을 풀 필요가 없고
내적 한 번이면 끝난다.
이번 강의에서는 가장 유명한 직교기저 를 만난다. 신호를 주파수로 갈라 놓는 기저이고,
음악 파일과 영상 통화와 의료 영상이 전부 그 위에 서 있다. 그런데 그 기저는 실수 세계에
살지 않는다. 복소수 위에 산다.
그래서 준비가 필요한데, 시작하자마자 문제가 하나 생긴다.
복소 벡터 x = [ 1 i ] \vv{x} = \begin{bmatrix} 1 \\ i \end{bmatrix} x = [ 1 i ] 의 길이를 지금까지 배운 대로
x T x \vv{x}^{\mathsf T}\vv{x} x T x 로 계산해 보자.
x T x = 1 2 + i 2 = 1 − 1 = 0 \vv{x}^{\mathsf T}\vv{x} = 1^2 + i^2 = 1 - 1 = 0 x T x = 1 2 + i 2 = 1 − 1 = 0 영벡터가 아닌데 길이가 0이다. 계산 착오가 아니라 지금까지의 정의가 복소수 위에서
무너진다는 신호이다. 이것부터 고치지 않으면 한 발짝도 나갈 수 없다.
1. 길이를 되찾는다 ¶ 실수에서 x T x = ∑ x j 2 \vv{x}^{\mathsf T}\vv{x} = \sum x_j^2 x T x = ∑ x j 2 가 길이의 제곱 노릇을 할 수 있었던 이유가
무엇인가. 제곱이 늘 음이 아니기 때문 이다. 그래서 더한 것도 음이 아니고, 0이 되려면
모든 항이 0이어야 했다.
복소수에서는 그 성질이 깨진다. i 2 = − 1 i^2 = -1 i 2 = − 1 이다. 그러면 복소수에서 늘 음이 아닌 것 은
무엇인가. 절댓값의 제곱이다. z = a + b i z = a + bi z = a + bi 에 대해
∣ z ∣ 2 = a 2 + b 2 = ( a − b i ) ( a + b i ) = z ˉ z |z|^2 = a^2 + b^2 = (a - bi)(a + bi) = \bar{z}\,z ∣ z ∣ 2 = a 2 + b 2 = ( a − bi ) ( a + bi ) = z ˉ z 이다. 자기 켤레와 곱하면 늘 음이 아닌 실수가 나온다. 이것이 우리가 찾던 성질이다.
그러므로 길이의 제곱을 이렇게 고쳐 쓰면 된다.
∥ x ∥ 2 = ∑ j ∣ x j ∣ 2 = ∑ j x ˉ j x j = x ˉ T x \lVert\vv{x}\rVert^2 = \sum_j |x_j|^2 = \sum_j \bar{x}_j x_j
= \bar{\vv{x}}^{\mathsf T}\vv{x} ∥ x ∥ 2 = j ∑ ∣ x j ∣ 2 = j ∑ x ˉ j x j = x ˉ T x 앞의 예로 확인하자. x = ( 1 , i ) \vv{x} = (1, i) x = ( 1 , i ) 이면 x ˉ = ( 1 , − i ) \bar{\vv{x}} = (1, -i) x ˉ = ( 1 , − i ) 이므로
x ˉ T x = 1 ⋅ 1 + ( − i ) ( i ) = 1 + 1 = 2 \bar{\vv{x}}^{\mathsf T}\vv{x} = 1 \cdot 1 + (-i)(i) = 1 + 1 = 2 x ˉ T x = 1 ⋅ 1 + ( − i ) ( i ) = 1 + 1 = 2 이다. 길이가 2 \sqrt2 2 이다. 성분이 각각 크기 1이니 당연한 답이다.
Figure 1: 왼쪽이 무너지는 계산이고 오른쪽이 고친 계산이다. 켤레는 실수축을 거울로 삼은 반사이고,
자기 자신과 거울상을 곱하면 늘 양수가 나온다.
켤레전치 ¶ "켤레를 취하고 전치한다"는 일이 앞으로 계속 나오므로 기호를 하나 만든다.
내적이 더 이상 대칭이 아니다 ¶ 실수에서는 x T y = y T x \vv{x}^{\mathsf T}\vv{y} = \vv{y}^{\mathsf T}\vv{x} x T y = y T x 였다. 복소수에서는
순서를 바꾸면 켤레가 된다.
y H x = x H y ‾ \vv{y}^{\mathsf H}\vv{x} = \overline{\vv{x}^{\mathsf H}\vv{y}} y H x = x H y 각 항이 y ˉ j x j \bar{y}_jx_j y ˉ j x j 와 x ˉ j y j \bar{x}_jy_j x ˉ j y j 로 서로 켤레이기 때문이다. 다만
x H y = 0 \vv{x}^{\mathsf H}\vv{y} = 0 x H y = 0 이면 그 켤레도 0이므로, 직교는 어느 쪽으로 재도 직교이다.
2. 에르미트 행렬과 유니타리 행렬 ¶ 전치가 켤레전치로 바뀌었으니, 전치로 정의했던 두 종류의 행렬도 따라서 바뀐다.
에르미트 행렬 — 대칭행렬의 복소판 ¶ A H = A A^{\mathsf H} = A A H = A 를 만족하는 행렬을 에르미트 행렬 이라 한다. 성분으로 쓰면
a j k = a k j ‾ a_{jk} = \overline{a_{kj}} a jk = a kj 이다.
대각 성분에 이 조건을 넣어 보자. j = k j = k j = k 로 두면
a j j = a j j ‾ ⟹ a j j 는 실수 a_{jj} = \overline{a_{jj}}
\qquad\Longrightarrow\qquad
a_{jj} \text{ 는 실수} a jj = a jj ⟹ a jj 는 실수 이다. 에르미트 행렬의 대각 성분은 반드시 실수이다. 자기 켤레와 같은 수는 실수뿐이기
때문이다.
예를 들어 다음은 에르미트 행렬이다.
H = [ 2 1 − i 1 + i 3 ] H = \begin{bmatrix} 2 & 1 - i \\ 1 + i & 3 \end{bmatrix} H = [ 2 1 + i 1 − i 3 ] 대각이 2 , 3 2, 3 2 , 3 으로 실수이고, 대각 밖의 두 성분이 서로 켤레이다.
H H H 의 고윳값을 실제로 구해 보면 1 과 4 로 실수이다.
tr ( H ) = 5 = 1 + 4 \operatorname{tr}(H) = 5 = 1 + 4 tr ( H ) = 5 = 1 + 4 이고 det H = 6 − ( 1 − i ) ( 1 + i ) = 6 − 2 = 4 = 1 × 4 \det H = 6 - (1-i)(1+i) = 6 - 2 = 4 = 1 \times 4 det H = 6 − ( 1 − i ) ( 1 + i ) = 6 − 2 = 4 = 1 × 4 이니
L21의 검산 도구도 그대로 통한다.
유니타리 행렬 — 직교행렬의 복소판 ¶ U H U = I U^{\mathsf H}U = I U H U = I 를 만족하는 행렬을 유니타리 행렬 이라 한다. 열들이 서로 직교하고 길이가 1이라는 뜻이다.
L17의 직교행렬이 하던 일을 그대로 한다.
길이를 보존한다. 확인은 한 줄이다.
∥ U x ∥ 2 = ( U x ) H ( U x ) = x H U H U ⏟ = I x = x H x = ∥ x ∥ 2 \lVert U\vv{x}\rVert^2
= \left(U\vv{x}\right)^{\mathsf H}\left(U\vv{x}\right)
= \vv{x}^{\mathsf H}\underbrace{U^{\mathsf H}U}_{=\,I}\vv{x}
= \vv{x}^{\mathsf H}\vv{x}
= \lVert\vv{x}\rVert^2 ∥ U x ∥ 2 = ( U x ) H ( U x ) = x H = I U H U x = x H x = ∥ x ∥ 2 고윳값의 절댓값이 1이다. U x = λ x U\vv{x} = \lambda\vv{x} U x = λ x 이고 x ≠ 0 \vv{x} \neq \vv{0} x = 0 이면
(11) 의 등식에 의해 양변의 길이가 같아야 한다.
∥ x ∥ = ∥ U x ∥ = ∥ λ x ∥ = ∣ λ ∣ ∥ x ∥ ⟹ ∣ λ ∣ = 1 \lVert\vv{x}\rVert = \lVert U\vv{x}\rVert = \lVert\lambda\vv{x}\rVert
= |\lambda|\,\lVert\vv{x}\rVert
\qquad\Longrightarrow\qquad
|\lambda| = 1 ∥ x ∥ = ∥ U x ∥ = ∥ λ x ∥ = ∣ λ ∣ ∥ x ∥ ⟹ ∣ λ ∣ = 1 고윳값이 전부 단위원 위에 있다. L21에서 회전행렬의 고윳값 ± i \pm i ± i 를 단위원에 찍었던
그림이 여기서 회수된다. 회전행렬은 유니타리 행렬의 가장 단순한 예였던 것이다.
대응표 ¶ 실수 복소 전치 A T A^{\mathsf T} A T 켤레전치 A H A^{\mathsf H} A H 길이 x T x \vv{x}^{\mathsf T}\vv{x} x T x x H x \vv{x}^{\mathsf H}\vv{x} x H x 대칭 S T = S S^{\mathsf T} = S S T = S 에르미트 A H = A A^{\mathsf H} = A A H = A 직교 Q T Q = I Q^{\mathsf T}Q = I Q T Q = I 유니타리 U H U = I U^{\mathsf H}U = I U H U = I 스펙트럼 정리 S = Q Λ Q T S = Q\Lambda Q^{\mathsf T} S = Q Λ Q T A = U Λ U H A = U\Lambda U^{\mathsf H} A = U Λ U H
준비가 끝났다. 이제 유니타리 행렬 중 가장 유명한 하나를 만나러 가자.
3. 단위원 위의 N N N 개 점 ¶ N N N 개의 수를 다루려 한다. 소리를 N N N 번 재서 얻은 표본이라고 생각해도 좋고 그냥 벡터라고
생각해도 좋다. 이 벡터를 담을 좋은 기저 를 만들 것인데, 그 재료가 하나 필요하다.
오일러 공식으로 풀어 쓰면 w = cos 2 π N − i sin 2 π N w = \cos\frac{2\pi}{N} - i\sin\frac{2\pi}{N} w = cos N 2 π − i sin N 2 π 이고,
절댓값이 1이다. w w w 를 곱하는 것은 단위원 위에서 시곗바늘을 한 칸 돌리는 일 이다.
N N N 등분한 눈금 하나만큼 돈다.
여기서 두 가지 성질이 나온다. 둘 다 지수법칙 한 줄이면 된다.
w N = ( e − 2 π i / N ) N = e − 2 π i = 1 w^N = \left(e^{-2\pi i/N}\right)^N = e^{-2\pi i} = 1 w N = ( e − 2 πi / N ) N = e − 2 πi = 1 w N / 2 = ( e − 2 π i / N ) N / 2 = e − π i = − 1 w^{N/2} = \left(e^{-2\pi i/N}\right)^{N/2} = e^{-\pi i} = -1 w N /2 = ( e − 2 πi / N ) N /2 = e − πi = − 1 말로 옮기면 이렇다. N N N 칸 돌면 제자리로 오고, 절반인 N / 2 N/2 N /2 칸 돌면 정반대편에 선다.
Figure 2: N = 8 N = 8 N = 8 일 때이다. w = e − 2 π i / 8 w = e^{-2\pi i/8} w = e − 2 πi /8 이라 시계 방향으로 돈다.
w 0 = 1 w^0 = 1 w 0 = 1 에서 출발해 여덟 칸 만에 제자리로 오고, 네 칸에서 -1 에 닿는다.
한 가지 더. 0 < s < N 0 < s < N 0 < s < N 이면 w s ≠ 1 w^s \neq 1 w s = 1 이다. 각도가 − 2 π s / N -2\pi s/N − 2 π s / N 인데
0 < s / N < 1 0 < s/N < 1 0 < s / N < 1 이라 한 바퀴를 채우지 못하기 때문이다. 이 사실도 곧 쓴다.
4. 푸리에 행렬 ¶ 이제 행렬을 만든다.
N × N N \times N N × N 행렬 F N F_N F N 의 ( j , k ) (j, k) ( j , k ) 성분을 다음으로 정한다.
( F N ) j k = w j k ( j , k = 0 , 1 , … , N − 1 ) \left(F_N\right)_{jk} = w^{jk}
\qquad (j, k = 0, 1, \dots, N-1) ( F N ) jk = w jk ( j , k = 0 , 1 , … , N − 1 ) 첨자가 0부터 시작하는 것이 이 강의의 약속이다.
성분이 첨자의 곱 이라는 점이 특이하다. j j j 와 k k k 를 곱해서 그만큼 시곗바늘을 돌린
자리의 값을 적는 것이다.
F 4 F_4 F 4 를 손으로 써 보자¶ N = 4 N = 4 N = 4 이면 w = e − 2 π i / 4 = e − π i / 2 = − i w = e^{-2\pi i/4} = e^{-\pi i/2} = -i w = e − 2 πi /4 = e − πi /2 = − i 이다. 거듭제곱을 먼저 정리하자.
w 0 = 1 , w 1 = − i , w 2 = − 1 , w 3 = i , w 4 = 1 w^0 = 1,
\quad
w^1 = -i,
\quad
w^2 = -1,
\quad
w^3 = i,
\quad
w^4 = 1 w 0 = 1 , w 1 = − i , w 2 = − 1 , w 3 = i , w 4 = 1 w 4 = 1 w^4 = 1 w 4 = 1 이므로 지수가 4를 넘으면 4로 나눈 나머지만 보면 된다. 예를 들어
w 6 = w 4 + 2 = w 2 = − 1 w^6 = w^{4+2} = w^2 = -1 w 6 = w 4 + 2 = w 2 = − 1 이고 w 9 = w 8 + 1 = w = − i w^9 = w^{8+1} = w = -i w 9 = w 8 + 1 = w = − i 이다.
이제 ( j , k ) (j,k) ( j , k ) 자리에 w j k w^{jk} w jk 를 적는다.
F 4 = [ w 0 w 0 w 0 w 0 w 0 w 1 w 2 w 3 w 0 w 2 w 4 w 6 w 0 w 3 w 6 w 9 ] = [ 1 1 1 1 1 − i − 1 i 1 − 1 1 − 1 1 i − 1 − i ] F_4 = \begin{bmatrix}
w^0 & w^0 & w^0 & w^0 \\
w^0 & w^1 & w^2 & w^3 \\
w^0 & w^2 & w^4 & w^6 \\
w^0 & w^3 & w^6 & w^9
\end{bmatrix}
= \begin{bmatrix}
1 & 1 & 1 & 1 \\
1 & -i & -1 & i \\
1 & -1 & 1 & -1 \\
1 & i & -1 & -i
\end{bmatrix} F 4 = ⎣ ⎡ w 0 w 0 w 0 w 0 w 0 w 1 w 2 w 3 w 0 w 2 w 4 w 6 w 0 w 3 w 6 w 9 ⎦ ⎤ = ⎣ ⎡ 1 1 1 1 1 − i − 1 i 1 − 1 1 − 1 1 i − 1 − i ⎦ ⎤ 0행과 0열이 전부 1이다. j = 0 j = 0 j = 0 이거나 k = 0 k = 0 k = 0 이면 j k = 0 jk = 0 jk = 0 이라 w 0 = 1 w^0 = 1 w 0 = 1 이기
때문이다.
Figure 3: F 8 F_8 F 8 의 실수부와 허수부를 색으로 칠한 것과, F 4 F_4 F 4 를 풀어 쓴 것이다.
줄무늬가 규칙적이고 대칭이 눈에 띈다. 그 규칙성이 7절에서 속도가 된다.
열은 주파수이고 행은 시각이다 ¶ k k k 열을 위에서 아래로 읽어 보자. w 0 ⋅ k , w 1 ⋅ k , w 2 ⋅ k , … w^{0 \cdot k}, w^{1 \cdot k}, w^{2 \cdot k}, \dots w 0 ⋅ k , w 1 ⋅ k , w 2 ⋅ k , … 이다.
시곗바늘이 한 걸음에 k k k 칸씩 도는 수열이다. k k k 가 클수록 빨리 도는 진동이다.
k = 0 k = 0 k = 0 열은 아예 돌지 않는 상수 신호이고, k = 1 k = 1 k = 1 열은 N N N 걸음에 한 바퀴 도는
가장 느린 진동이다.
j j j 행은 반대로 시각 j j j 에서 각 주파수가 어떤 값을 갖는지 를 늘어놓은 것이다.
어떤 책은 w = e + 2 π i / N w = e^{+2\pi i/N} w = e + 2 πi / N 으로 두고 그것을 변환으로 삼는다. 정규화 상수도
1 / N 1/N 1/ N 을 변환에 붙이거나, 역변환에 붙이거나, 1 / N 1/\sqrt N 1/ N 을 양쪽에 나누는 세 가지 관례가
모두 쓰인다. 이 교재는 w = e − 2 π i / N w = e^{-2\pi i/N} w = e − 2 πi / N 과 F − 1 = 1 N F H F^{-1} = \frac1N F^{\mathsf H} F − 1 = N 1 F H 를 쓴다.
numpy.fft 와 같은 규약이다.
5. 직교성 — 이 강의의 결론 ¶ 푸리에 행렬이 좋은 이유는 하나뿐이다. 열들이 서로 직교하기 때문 이다.
F H F = N I F^{\mathsf H}F = N I F H F = N I 증명해 보자. 3절에서 준비한 두 사실만 있으면 된다.
( F H F ) j k (F^{\mathsf H}F)_{jk} ( F H F ) jk 는 F F F 의 j j j 열과 k k k 열의 내적이다. 성분으로 쓰면
( F H F ) j k = ∑ m = 0 N − 1 ( F ) m j ‾ ( F ) m k = ∑ m = 0 N − 1 w m j ‾ w m k \left(F^{\mathsf H}F\right)_{jk}
= \sum_{m=0}^{N-1}\overline{\left(F\right)_{mj}}\left(F\right)_{mk}
= \sum_{m=0}^{N-1}\overline{w^{mj}}\,w^{mk} ( F H F ) jk = m = 0 ∑ N − 1 ( F ) mj ( F ) mk = m = 0 ∑ N − 1 w mj w mk 이다. ∣ w ∣ = 1 |w| = 1 ∣ w ∣ = 1 이므로 w ˉ = 1 / w = w − 1 \bar{w} = 1/w = w^{-1} w ˉ = 1/ w = w − 1 이고, 따라서
w m j ‾ = w − m j \overline{w^{mj}} = w^{-mj} w mj = w − mj 이다. 지수를 합치면
( F H F ) j k = ∑ m = 0 N − 1 w m ( k − j ) \left(F^{\mathsf H}F\right)_{jk} = \sum_{m=0}^{N-1} w^{m(k-j)} ( F H F ) jk = m = 0 ∑ N − 1 w m ( k − j ) 이 되어 등비급수 가 되었다. 공비는 r = w k − j r = w^{k-j} r = w k − j 이다. 두 경우로 나눈다.
j = k j = k j = k 일 때. r = w 0 = 1 r = w^0 = 1 r = w 0 = 1 이므로 1을 N N N 번 더하는 것이라 합이 N N N 이다.
j ≠ k j \neq k j = k 일 때. 0 < ∣ k − j ∣ < N 0 < |k - j| < N 0 < ∣ k − j ∣ < N 이므로 3절 끝의 사실에 의해 r ≠ 1 r \neq 1 r = 1 이다.
그러면 등비급수 공식을 쓸 수 있다.
분자를 계산하자. 여기서 (14) 의 w N = 1 w^N = 1 w N = 1 이 쓰인다.
r N = ( w k − j ) N = ( w N ) k − j = 1 k − j = 1 ⟹ r N − 1 = 0 r^N = \left(w^{k-j}\right)^N = \left(w^N\right)^{k-j} = 1^{k-j} = 1
\qquad\Longrightarrow\qquad
r^N - 1 = 0 r N = ( w k − j ) N = ( w N ) k − j = 1 k − j = 1 ⟹ r N − 1 = 0 분자가 0이고 분모는 0이 아니므로 합이 0이다. 두 경우를 합치면
(19) 의 식이다.
그래서 역변환이 공짜이다 ¶ (19) 의 양변을 N N N 으로 나누면 유니타리 행렬이 나온다.
( 1 N F ) H ( 1 N F ) = I \left(\frac{1}{\sqrt N}F\right)^{\mathsf H}\left(\frac{1}{\sqrt N}F\right) = I ( N 1 F ) H ( N 1 F ) = I 1 N F \frac{1}{\sqrt N}F N 1 F 는 유니타리 행렬이다. 그리고 (19) 의 양변에
F − 1 F^{-1} F − 1 을 곱하면 역행렬이 곧바로 나온다.
F − 1 = 1 N F H F^{-1} = \frac{1}{N}F^{\mathsf H} F − 1 = N 1 F H 계수는 내적 한 번 ¶ c = F x \vv{c} = F\vv{x} c = F x 를 성분으로 읽어 보자. c k c_k c k 는 F F F 의 k k k 행 과 x \vv{x} x 의 곱인데,
F F F 는 F j k = w j k F_{jk} = w^{jk} F jk = w jk 로 j j j 와 k k k 에 대칭이라 k k k 행과 k k k 열이 같다. 그러므로
c k = ∑ j w j k x j = ( k 번째 열 ‾ ) T x c_k = \sum_{j} w^{jk}x_j
= \left(\overline{k \text{ 번째 열}}\right)^{\mathsf T}\vv{x} c k = j ∑ w jk x j = ( k 번째 열 ) T x 이고, 이것은 x \vv{x} x 와 k k k 번째 주파수 기저의 내적 이다.
L17에서 배운 그 원리 그대로이다. 직교기저에서는 계수를 구하려고 연립방정식을 풀지 않는다.
내적 한 번이면 된다. 그리고 (25) 의 역변환은 그 계수들로 x \vv{x} x 를 도로
조립하는 일이다.
x = 1 N ∑ k c k ( k 번째 열 ) \vv{x} = \frac{1}{N}\sum_k c_k\left(k \text{ 번째 열}\right) x = N 1 k ∑ c k ( k 번째 열 ) 푸리에 변환과 역변환이 거의 같은 모양인 이유가 이것이다. 유니타리 행렬이기 때문이다.
6. 그런데 N 2 N^2 N 2 이 문제다 ¶ 이론은 끝났다. 실제로 써 보면 곧 벽에 부딪힌다.
N × N N \times N N × N 행렬을 벡터에 곱하려면 성분마다 곱셈 한 번씩, 모두 N 2 N^2 N 2 번의 곱셈이 든다.
N N N 이 작을 때는 아무 문제가 없다. 그런데 소리 1초는 보통 44100번 재고, 3분짜리 노래
하나면 표본이 800만 개쯤 된다.
N = 1 0 6 ⟹ N 2 = 1 0 12 N = 10^6
\qquad\Longrightarrow\qquad
N^2 = 10^{12} N = 1 0 6 ⟹ N 2 = 1 0 12 1조 번의 곱셈이다. 게다가 N × N N \times N N × N 행렬을 메모리에 올리는 것부터 불가능하다.
N = 1 0 6 N = 10^6 N = 1 0 6 이면 복소수 1012 개, 16테라바이트가 넘는다.
행렬을 만들지 않고 곱해야 한다. 그리고 곱셈 횟수도 줄여야 한다.
다행히 두 가지가 한꺼번에 해결된다.
7. 푸리에 행렬을 쪼갠다 ¶ 방법은 새롭지 않다. L4에서 A = L U A = LU A = LU 를 배웠고 L17에서 A = Q R A = QR A = QR 을 배웠다.
복잡한 행렬을 단순한 행렬들의 곱으로 쪼개는 것. 여기서도 같은 일을 한다.
다만 쪼개진 조각들이 극단적으로 성기어서 곱셈의 대부분이 0과의 곱이 되어 건너뛸 수 있을
뿐이다.
짝수와 홀수로 나눈다 ¶ N = 2 n N = 2n N = 2 n 이 짝수라 하자. y = F 2 n x \vv{y} = F_{2n}\vv{x} y = F 2 n x 의 j j j 성분을 쓴다.
y j = ∑ k = 0 2 n − 1 w j k x k ( w = e − 2 π i / ( 2 n ) ) y_j = \sum_{k=0}^{2n-1} w^{jk}x_k
\qquad (w = e^{-2\pi i/(2n)}) y j = k = 0 ∑ 2 n − 1 w jk x k ( w = e − 2 πi / ( 2 n ) ) 합을 k k k 가 짝수인 것과 홀수인 것 으로 가른다. 짝수는 k = 2 m k = 2m k = 2 m , 홀수는 k = 2 m + 1 k = 2m+1 k = 2 m + 1 로
쓰면 각각 m = 0 , … , n − 1 m = 0, \dots, n-1 m = 0 , … , n − 1 이다.
y j = ∑ m = 0 n − 1 w j ( 2 m ) x 2 m + ∑ m = 0 n − 1 w j ( 2 m + 1 ) x 2 m + 1 y_j = \sum_{m=0}^{n-1} w^{j(2m)}x_{2m} + \sum_{m=0}^{n-1} w^{j(2m+1)}x_{2m+1} y j = m = 0 ∑ n − 1 w j ( 2 m ) x 2 m + m = 0 ∑ n − 1 w j ( 2 m + 1 ) x 2 m + 1 뒤쪽 합에서 w j ( 2 m + 1 ) = w j w 2 j m w^{j(2m+1)} = w^{j}\,w^{2jm} w j ( 2 m + 1 ) = w j w 2 jm 이므로 w j w^j w j 를 밖으로 빼낸다.
y j = ∑ m ( w 2 ) j m x 2 m + w j ∑ m ( w 2 ) j m x 2 m + 1 y_j = \sum_{m}\left(w^2\right)^{jm}x_{2m}
+ w^j\sum_{m}\left(w^2\right)^{jm}x_{2m+1} y j = m ∑ ( w 2 ) jm x 2 m + w j m ∑ ( w 2 ) jm x 2 m + 1 여기서 절반 크기의 문제가 나타난다 ¶ w 2 w^2 w 2 가 무엇인지 보자. 지수를 계산하면 된다.
w 2 = ( e − 2 π i / ( 2 n ) ) 2 = e − 2 π i / n w^2 = \left(e^{-2\pi i/(2n)}\right)^2 = e^{-2\pi i/n} w 2 = ( e − 2 πi / ( 2 n ) ) 2 = e − 2 πi / n 이것은 크기 n n n 짜리 문제의 w w w 이다. 그러니 (31) 의 두 합이 각각
F n F_n F n 을 곱하는 일이다. 짝수 자리만 모은 벡터를 x 짝 \vv{x}_{\text{짝}} x 짝 , 홀수 자리만 모은
벡터를 x 홀 \vv{x}_{\text{홀}} x 홀 이라 하고
e = F n x 짝 , o = F n x 홀 \vv{e} = F_n\,\vv{x}_{\text{짝}},
\qquad
\vv{o} = F_n\,\vv{x}_{\text{홀}} e = F n x 짝 , o = F n x 홀 이라 두면, j = 0 , … , n − 1 j = 0, \dots, n-1 j = 0 , … , n − 1 에 대해
y j = e j + w j o j y_j = e_j + w^j\,o_j y j = e j + w j o j 이다. 크기 2 n 2n 2 n 문제의 절반이 크기 n n n 문제 두 개로 풀렸다.
나머지 절반은 부호만 다르다 ¶ 남은 것은 j = n , … , 2 n − 1 j = n, \dots, 2n-1 j = n , … , 2 n − 1 이다. j + n j + n j + n 자리를 계산해 보자. 두 조각을 따로 본다.
앞의 합. ( w 2 ) ( j + n ) m = ( w 2 ) j m ( w 2 ) n m \left(w^2\right)^{(j+n)m} = \left(w^2\right)^{jm}\left(w^2\right)^{nm} ( w 2 ) ( j + n ) m = ( w 2 ) jm ( w 2 ) nm 인데,
w 2 w^2 w 2 는 크기 n n n 문제의 w w w 이므로 ( w 2 ) n = 1 \left(w^2\right)^n = 1 ( w 2 ) n = 1 이다. 뒤 인수가 1이라
사라진다. 곧 앞의 합은 j j j 일 때와 똑같다.
밖으로 뺀 w j w^j w j . 여기가 핵심이다.
w j + n = w j w n = w j e − 2 π i n / ( 2 n ) = w j e − π i = − w j w^{j+n} = w^j\,w^n = w^j\,e^{-2\pi i n/(2n)} = w^j\,e^{-\pi i} = -\,w^j w j + n = w j w n = w j e − 2 πin / ( 2 n ) = w j e − πi = − w j (15) 의 w N / 2 = − 1 w^{N/2} = -1 w N /2 = − 1 이 여기서 쓰였다. 부호만 뒤집힌다.
y j + n = e j − w j o j y_{j+n} = e_j - w^j\,o_j y j + n = e j − w j o j 행렬로 쓰면 인수분해이다 ¶ (34) 의 식과 (36) 의 식을 행렬로 묶으면 이렇게 된다.
F 2 n = [ I D I − D ] [ F n 0 0 F n ] P , D = diag ( 1 , w , w 2 , … , w n − 1 ) F_{2n} =
\begin{bmatrix} I & D \\ I & -D \end{bmatrix}
\begin{bmatrix} F_n & 0 \\ 0 & F_n \end{bmatrix}
P,
\qquad
D = \operatorname{diag}\left(1, w, w^2, \dots, w^{n-1}\right) F 2 n = [ I I D − D ] [ F n 0 0 F n ] P , D = diag ( 1 , w , w 2 , … , w n − 1 ) 세 조각의 정체가 각각 분명하다.
P P P 는 짝수 자리를 앞으로, 홀수 자리를 뒤로 보내는 순열행렬 이다. L5에서 배운
그 순열행렬이다. 곱셈이 한 번도 필요 없고 자리만 옮기면 된다.
가운데 는 절반 크기 문제 두 개 이다. 블록 밖이 전부 0이라 곱셈이 절반으로 준다.
앞 은 둘을 합치는 조각이다. D D D 가 대각행렬이라 곱셈이 n n n 번뿐이고,
아래 블록은 위 블록의 부호만 뒤집은 것이라 계산을 다시 할 필요가 없다.
Figure 4: L L L 과 U U U 로 쪼갤 때와 같은 종류의 이야기이다. 다만 조각들이 훨씬 성기다.
이것을 고속 푸리에 변환 (FFT)이라 한다.
8. 얼마나 빨라지는가 ¶ F n F_n F n 이 다시 절반으로 쪼개지고, 또 쪼개진다. N N N 이 2의 거듭제곱이면 크기 1이 될 때까지
계속 쪼갤 수 있다.
크기 N N N 문제를 푸는 데 드는 곱셈 횟수를 T ( N ) T(N) T ( N ) 이라 하자.
(37) 에서 절반 문제가 둘이고, 합치는 데 D D D 의 곱셈이
N / 2 N/2 N /2 번 든다.
T ( N ) = 2 T ( N 2 ) + N 2 T(N) = 2\,T\!\left(\frac{N}{2}\right) + \frac{N}{2} T ( N ) = 2 T ( 2 N ) + 2 N 풀어 보자. 한 단계 내려갈 때마다 문제 크기가 절반이 되므로 log 2 N \log_2 N log 2 N 단계 만에
크기 1에 닿는다. 그리고 각 단계에서 합치는 비용은, 문제가 두 배로 많아지는 대신 각각이
절반 크기라 총합이 늘 N / 2 N/2 N /2 로 같다.
T ( N ) = N 2 + N 2 + ⋯ + N 2 ⏟ log 2 N 개 = N 2 log 2 N T(N) = \underbrace{\frac{N}{2} + \frac{N}{2} + \cdots + \frac{N}{2}}_{\log_2 N \text{ 개}}
= \frac{N}{2}\log_2 N T ( N ) = l o g 2 N 개 2 N + 2 N + ⋯ + 2 N = 2 N log 2 N N 2 N^2 N 2 이 N 2 log 2 N \frac{N}{2}\log_2 N 2 N log 2 N 으로 줄었다.
숫자로 보면 차이가 실감난다.
N N N N 2 N^2 N 2 N 2 log 2 N \frac{N}{2}\log_2 N 2 N log 2 N 몇 배 2 10 = 1 , 024 2^{10} = 1{,}024 2 10 = 1 , 024 1.0 × 1 0 6 1.0 \times 10^6 1.0 × 1 0 6 5.1 × 1 0 3 5.1 \times 10^3 5.1 × 1 0 3 205 2 15 = 32 , 768 2^{15} = 32{,}768 2 15 = 32 , 768 1.1 × 1 0 9 1.1 \times 10^9 1.1 × 1 0 9 2.5 × 1 0 5 2.5 \times 10^5 2.5 × 1 0 5 4,369 2 20 ≈ 1 0 6 2^{20} \approx 10^6 2 20 ≈ 1 0 6 1.1 × 1 0 12 1.1 \times 10^{12} 1.1 × 1 0 12 1.0 × 1 0 7 1.0 \times 10^7 1.0 × 1 0 7 104,858 2 24 ≈ 1.7 × 1 0 7 2^{24} \approx 1.7 \times 10^7 2 24 ≈ 1.7 × 1 0 7 2.8 × 1 0 14 2.8 \times 10^{14} 2.8 × 1 0 14 2.0 × 1 0 8 2.0 \times 10^8 2.0 × 1 0 8 1,398,101
Figure 5: 왼쪽은 두 곡선이고 오른쪽은 그 비이다. 격차가 끝없이 벌어진다.
9. 자주 하는 오해 ¶ 복소 벡터에 x T x \vv{x}^{\mathsf T}\vv{x} x T x 를 쓰는 것 ¶ 이 강의가 그 이야기로 시작했다. 실수에서는 맞고 복소수에서는 틀린다.
특히 코드에서 A.T 를 쓰면 오류 없이 조용히 틀린 답 이 나오므로 위험하다.
복소수를 다루는 순간부터 A.conj().T 를 반사적으로 써야 한다.
부호 규약과 정규화 상수를 섞는 것 ¶ 4절에서 경고했다. w w w 의 지수 부호와 1 / N 1/N 1/ N 을 어디에 붙이는지가 책마다 다르다.
어느 쪽을 쓰는지 정하고 끝까지 그것만 쓰면 된다. 검산은 간단하다.
F − 1 F = I F^{-1}F = I F − 1 F = I 가 나오는지 확인하면 된다.
FFT가 DFT의 근사라는 오해 ¶ 결과가 완전히 같다. FFT는 새로운 변환이 아니라 같은 행렬을 곱하는 더 빠른 방법이다.
부동소수점 오차는 오히려 FFT 쪽이 더 작다. 연산 횟수가 적어 오차가 덜 쌓이기 때문이다.
N N N 이 2의 거듭제곱이어야 한다는 오해¶ 7절의 유도는 N N N 이 짝수라는 것만 썼고, 끝까지 쪼개려면 2의 거듭제곱이 편하다.
그러나 실제 라이브러리는 N N N 을 소인수분해해서 3이든 5든 같은 방식으로 쪼갠다.
소수인 N N N 을 위한 방법도 따로 있다. numpy.fft 는 아무 N N N 이나 받는다.
에르미트 행렬의 대각에 허수를 쓰는 것 ¶ (8) 에서 본 대로 대각은 반드시 실수이다.
[ i 1 1 0 ] \begin{bmatrix} i & 1 \\ 1 & 0\end{bmatrix} [ i 1 1 0 ] 은 에르미트가 아니다.
마치며... ¶ 이번 강의에서 다룬 것을 정리하면 다음과 같다.
대상 내용 문제 복소수에서 x T x \vv{x}^{\mathsf T}\vv{x} x T x 가 길이 노릇을 못 한다 해결 켤레전치 A H A^{\mathsf H} A H . 길이는 x H x \vv{x}^{\mathsf H}\vv{x} x H x 에르미트 A H = A A^{\mathsf H} = A A H = A . 대각은 실수. L25가 그대로 성립유니타리 U H U = I U^{\mathsf H}U = I U H U = I . 길이 보존, ∣ λ ∣ = 1 \lvert\lambda\rvert = 1 ∣ λ ∣ = 1 w w w e − 2 π i / N e^{-2\pi i/N} e − 2 πi / N . w N = 1 w^N = 1 w N = 1 과 w N / 2 = − 1 w^{N/2} = -1 w N /2 = − 1 푸리에 행렬 ( F N ) j k = w j k (F_N)_{jk} = w^{jk} ( F N ) jk = w jk . 열은 주파수, 행은 시각직교성 F H F = N I F^{\mathsf H}F = NI F H F = N I — 등비급수와 w N = 1 w^N = 1 w N = 1 역변환 F − 1 = 1 N F H F^{-1} = \frac1N F^{\mathsf H} F − 1 = N 1 F H . 공짜계수 내적 한 번 (L17의 원리 그대로) 문제 행렬 곱은 N 2 N^2 N 2 . N = 1 0 6 N = 10^6 N = 1 0 6 이면 1012 FFT F 2 n F_{2n} F 2 n 을 세 조각으로 인수분해. w n = − 1 w^n = -1 w n = − 1 이 절반을 아낀다계산량 N 2 log 2 N \frac{N}{2}\log_2 N 2 N log 2 N . N = 1 0 6 N = 10^6 N = 1 0 6 에서 10만 배
복소수 위에서 길이와 직교를 다시 정의하는 일부터 시작했다. 핵심은 하나, 전치 대신
켤레전치를 쓰는 것 이었다. 그 약속 하나로 대칭은 에르미트가 되고 직교는 유니타리가 되며
스펙트럼 정리는 모양을 그대로 유지했다.
그다음 그 유니타리 행렬 중 가장 유명한 것을 만났다. 단위원을 N N N 등분한 점들로 만든
푸리에 행렬이었고, 그 열들이 서로 직교한다는 사실 하나가 역변환을 공짜로 만들었다.
L17에서 배운 "직교기저에서는 계수를 내적 한 번으로 구한다"는 원리가, 신호처리라는
전혀 다른 무대에서 그대로 작동한 것이다.
마지막으로 그 행렬을 쪼갰다. 짝수와 홀수로 나누자 절반 크기의 문제 두 개가 나타났고,
w N / 2 = − 1 w^{N/2} = -1 w N /2 = − 1 덕분에 나머지 절반은 부호만 뒤집으면 되었다. N 2 N^2 N 2 이 N log N N\log N N log N 이 되었다.
새로운 수학이 아니라 행렬 인수분해였다.
다음 강의에서는 다시 실수의 세계로 돌아간다. 그리고 지난 강의에서 남겨 둔 물음에
답한다. 대칭행렬의 고윳값이 실수인 것까지는 알았는데, 그 실수들이 전부 양수라면
무슨 일이 벌어지는가. 별것 아닌 조건 같지만 그 하나에서 최소값과 타원과 안정성이
한꺼번에 따라 나온다.
이번 강의의 내용을 파이썬으로 확인해 보려면 L26 실습 노트북 으로 넘어가면 된다.
A.T 와 A.conj().T 가 어떻게 다른 답을 내는지 보고, F H F = N I F^{\mathsf H}F = NI F H F = N I 를 직접 확인하며,
FFT를 재귀 함수 열 줄로 짜서 numpy.fft 와 대조하고 속도를 재 볼 수 있다.