행렬 연산 복습: 덧셈·곱셈·전치·역행렬

행렬의 덧셈

두 행렬의 덧셈은 대응되는 원소끼리 더하는 방식으로 정의한다. 먼저 행렬의 크기(차원)가 동일해야 한다. 예를 들어 행렬 $\mathbf{A}$와 $\mathbf{B}$가 각각 $m\times n$ 크기를 가진다고 하자. 이때 두 행렬의 합 $\mathbf{C} = \mathbf{A} + \mathbf{B}$은 동일 위치의 원소끼리 합한 것으로 구성된 $m\times n$ 행렬이 된다. 이를 식으로 표현하면

C=A+B=[a11+b11a1n+b1nam1+bm1amn+bmn].\begin{align} \mathbf{C} &= \mathbf{A} + \mathbf{B}\\ &= \begin{bmatrix} a_{11} + b_{11} & \cdots & a_{1n} + b_{1n} \\ \vdots & \ddots & \vdots \\ a_{m1} + b_{m1} & \cdots & a_{mn} + b_{mn} \end{bmatrix}. \end{align}

여기서 $a_{ij}$와 $b_{ij}$는 각각 행렬 $\mathbf{A}$와 $\mathbf{B}$의 $(i,j)$ 원소다. 결과 $\mathbf{C}$ 역시 같은 $m\times n$ 크기를 지닌다.

스칼라(실수 또는 복소수 등) $k$에 대한 행렬의 스칼라 곱도 유사한 방식으로 정의되며, $\mathbf{A}$의 모든 원소에 $k$를 곱해서 만든 행렬이 $k\mathbf{A}$가 된다. 이때도 각 원소가 독립적으로 스칼라의 곱을 받는다.

행렬의 덧셈은 교환법칙과 결합법칙이 성립한다. 즉, $\mathbf{A}+\mathbf{B} = \mathbf{B}+\mathbf{A}$이고, $(\mathbf{A}+\mathbf{B})+\mathbf{C} = \mathbf{A}+(\mathbf{B}+\mathbf{C})$가 성립한다. 스칼라와 행렬 간의 결합 관계에서도 $(k + \ell)\mathbf{A} = k\mathbf{A} + \ell\mathbf{A}$ 등과 같은 성질을 만족한다.

행렬의 곱셈

행렬 곱셈은 두 행렬의 대응되는 차원을 통해 정의된다. 예를 들어 $m\times n$ 크기를 갖는 행렬 $\mathbf{A}$와 $n\times p$ 크기를 갖는 행렬 $\mathbf{B}$를 곱하는 경우를 생각하자. 이때 곱 $\mathbf{C} = \mathbf{A}\mathbf{B}$는 $m\times p$ 크기를 갖는 행렬이 된다. 곱셈의 결과 $\mathbf{C}$의 $(i,j)$ 원소는 다음과 같이 주어진다.

cij=k=1naikbkj.c_{ij} = \sum_{k=1}^{n} a_{ik}\,b_{kj}.

따라서 $\mathbf{A}\mathbf{B}$에서 행렬 $\mathbf{A}$의 $i$번째 행과 행렬 $\mathbf{B}$의 $j$번째 열의 내적이 곧 결과 행렬 $\mathbf{C}$의 $(i,j)$ 원소가 된다.

행렬 곱셈은 일반적으로 교환법칙을 만족하지 않는다. 즉, $\mathbf{A}\mathbf{B}$와 $\mathbf{B}\mathbf{A}$의 차원이 같고 둘 다 정의되더라도, 두 결과가 다를 수 있으며 심지어 어떤 경우에는 한쪽 곱이 정의되지만 반대쪽은 정의되지 않는 상황도 발생한다. 다만 결합법칙과 분배법칙은 성립한다. 예를 들어 $(\mathbf{A}\mathbf{B})\mathbf{C} = \mathbf{A}(\mathbf{B}\mathbf{C})$와 같은 결합법칙, 그리고 $\mathbf{A}(\mathbf{B}+\mathbf{C}) = \mathbf{A}\mathbf{B} + \mathbf{A}\mathbf{C}$ 등은 유효하다.

행렬 곱셈의 기본 원리는 많은 응용에 쓰인다. 예를 들어 선형 변환을 나타내는 행렬이 연달아 작용할 때, 이를 행렬 곱으로 단순화하여 표현할 수 있다. 수치해석에서 선형 방정식을 풀 때도 행렬 곱 연산이 핵심을 이룬다.

전치 행렬

행렬의 전치란, 주어진 행렬의 행과 열을 서로 교환하여 얻는 연산이다. $m\times n$ 크기의 행렬 $\mathbf{A}$가 있을 때 그 전치 행렬을 $\mathbf{A}^\mathsf{T}$라 표기한다. 이때 $\mathbf{A}^\mathsf{T}$는 $n\times m$ 크기를 가지며 다음과 같이 정의된다.

(AT)ij=aji.(\mathbf{A}^\mathsf{T})_{ij} = a_{ji}.

전치 연산을 다시 두 번 수행하면 원래 행렬로 돌아온다. 즉, $(\mathbf{A}^\mathsf{T})^\mathsf{T} = \mathbf{A}$가 성립한다.

행렬 전치와 스칼라 곱, 덧셈, 곱셈에는 다음과 같은 관계들이 있다. 스칼라 $k$에 대해 $(k\mathbf{A})^\mathsf{T} = k(\mathbf{A}^\mathsf{T})$가 성립하며, $\mathbf{A} + \mathbf{B}$의 전치는 $\mathbf{A}^\mathsf{T} + \mathbf{B}^\mathsf{T}$와 같다. 또한 행렬 곱셈의 전치에 대해서는 다음이 성립한다.

(AB)T=BTAT.(\mathbf{A}\mathbf{B})^\mathsf{T} = \mathbf{B}^\mathsf{T}\mathbf{A}^\mathsf{T}.

이 성질은 전치 연산이 곱셈 순서를 뒤집는다는 점에서 매우 중요하다.

전치 연산은 대칭(symmetric) 행렬, 직교(orthogonal) 행렬, 혹은 에르미트(Hermitian) 행렬 등과 같은 특별한 행렬을 논할 때 매우 중요한 역할을 한다. 예를 들어 실수 정방행렬 $\mathbf{A}$가 대칭 행렬이라 함은 $\mathbf{A}^\mathsf{T} = \mathbf{A}$를 만족함을 의미한다.

역행렬

정방행렬 $\mathbf{A}$가 주어졌을 때, 어떤 행렬 $\mathbf{B}$가 존재하여 $\mathbf{A}\mathbf{B} = \mathbf{B}\mathbf{A} = \mathbf{I}$를 만족한다면, 그 행렬 $\mathbf{B}$를 $\mathbf{A}$의 역행렬이라 한다. 이를 $\mathbf{A}^{-1}$으로 표기한다. 역행렬은 정방행렬만이 가질 수 있으며, 모든 정방행렬이 역행렬을 갖는 것은 아니다. 역행렬이 존재하는 행렬을 가역행렬(invertible matrix)이라고 한다.

역행렬 $\mathbf{A}^{-1}$이 존재하기 위한 필요충분 조건으로, $\mathbf{A}$의 행렬식(determinant)이 0이 아니어야 한다는 점이 널리 알려져 있다. 즉, 정방행렬 $\mathbf{A}$에 대해 $\det(\mathbf{A}) \ne 0$이면 역행렬이 존재한다. 역행렬의 explicit한 계산 방법은 다양한 방식이 있으나, 학부 수준에서는 소규모 행렬에 대해 가우스 소거(Gaussian elimination) 기법이나 수식을 통한 고전적 공식(예: $2\times 2$, $3\times 3$ 행렬의 역행렬 공식)을 직접 적용하기도 한다. 실제 대형 시스템에서는 수치해석적으로는 역행렬을 직접 구하기보다, 선형 방정식을 해결할 때 LU 분해나 QR 분해 등을 활용해 효율적으로 문제를 푸는 것이 일반적이다.

역행렬이 존재한다면, 선형 연립 방정식을 풀 때 이를 간단히

Ax=b\mathbf{A}\mathbf{x} = \mathbf{b}

라 할 때

x=A1b\mathbf{x} = \mathbf{A}^{-1}\mathbf{b}

의 형태로 쓸 수 있다. 그러나 대규모 문제에서는 $\mathbf{A}^{-1}$를 직접 구해서 곱하는 방식이 수치적으로도 비효율적이고 계산 안정성도 떨어진다. 따라서 수치해석에서는 역행렬의 존재여부가 여러 분석에 유용하지만, 실제로 역행렬을 구하는 작업은 다른 방식(분해 기법, 직접적인 방법 등)으로 대체되는 경우가 많다.

수치적인 맥락에서 중요한 점은, 역행렬이 존재하더라도 행렬 $\mathbf{A}$가 매우 커지면(또는 조건수가 나빠지면) 실제로 역행렬을 계산하고 사용하는 과정에서 오차가 커질 수 있다는 점이다. 또한 $\det(\mathbf{A})$가 매우 작은 경우, 행렬이 역행렬을 갖더라도 수치 오차가 매우 크게 확산될 수 있다. 이를 방지하기 위해 다양한 정규화(normalization)나 분해 알고리즘이 발전해 왔다.

행렬 연산을 정확히 이해하는 것은 선형 방정식을 풀기 위한 다양한 알고리즘에서 핵심이 된다. 예를 들어 LU 분해 기법은 정확히 행렬 곱셈과 소거 연산 과정 위에 서 있으며, QR 분해나 특이값 분해(SVD) 역시 행렬 곱셈과 전치 연산을 다룰 줄 알아야 원리를 파악하기가 쉽다. 가우스-조던 소거(Gauss-Jordan elimination) 과정 중의 각 단계 역시, 스칼라 곱과 덧셈, 그리고 특정 형태의 역을 부분적으로 계산해 가는 과정이라고 볼 수 있다.

블록 행렬 연산

수치해석 또는 대형 연산에서 자주 쓰이는 기법 중 하나는 큰 행렬을 여러 개의 작은 블록으로 나누어 다루는 것이다. 예를 들어 다음과 같은 블록 행렬을 생각해 보자.

A=[A11A12A21A22].\mathbf{A} = \begin{bmatrix} \mathbf{A}_{11} & \mathbf{A}_{12} \\ \mathbf{A}_{21} & \mathbf{A}_{22} \end{bmatrix}.

이때 $\mathbf{A}{11}, \mathbf{A}{12}, \mathbf{A}{21}, \mathbf{A}{22}$는 각각 적절한 차원을 갖는 하위 행렬들이다. 블록 단위로 덧셈이나 곱셈을 정의할 때도, 일반 행렬 연산과 동일한 규칙이 단순히 블록에 적용될 뿐이다. 예를 들어 블록 행렬의 곱셈은 블록을 하나의 원소처럼 간주하여, 차원이 맞는 블록끼리 곱하고(혹은 더하고) 결합하는 식으로 진행한다.

특히 블록 행렬의 역행렬에 대해 다음과 같은 고전적인 공식이 알려져 있다(적절한 가정하에).

[A11A12A21A22]1=[S1],\begin{bmatrix} \mathbf{A}_{11} & \mathbf{A}_{12} \\ \mathbf{A}_{21} & \mathbf{A}_{22} \end{bmatrix}^{-1} = \begin{bmatrix} \mathbf{S}^{-1} & \cdots \\ \vdots & \cdots \end{bmatrix},

여기서 $\mathbf{S} = \mathbf{A}{11} - \mathbf{A}{12},\mathbf{A}{22}^{-1},\mathbf{A}{21}$와 같은 이른바 슈어 보강(Schur complement)이 등장한다. 블록 행렬은 다양한 알고리즘(예: 블록 LU 분해, 분할 정복 알고리즘)에서 계산 효율을 높이기 위해 사용된다.

역행렬 존재성과 군(group) 구조

정방행렬들의 집합 중에서 역행렬이 존재하는 행렬만을 모은 집합을 일반선형군(GL, General Linear group)이라 부른다. 예를 들어 실수 계수의 $n\times n$ 행렬 중 역행렬이 존재하는 것들의 집합을 $\mathrm{GL}_n(\mathbb{R})$라 한다. 이 집합은 행렬의 곱셈 연산에 대해 군(group) 구조를 이룬다. 이는 역행렬, 항등행렬의 존재, 곱셈 결합법칙, 그리고 모든 원소(가역행렬)에 대한 역원(역행렬)의 존재 등으로부터 보장된다.

실수 행렬이 아닌 복소수 행렬의 경우에는 $\mathrm{GL}_n(\mathbb{C})$를 정의할 수 있으며, 이 역시 군을 이룬다. 마찬가지로, 행렬식이 1인 가역행렬만 모아 놓으면 특수선형군(SL)이라 불리는 부분군이 형성된다. 이렇게 확장하면, 행렬식이 특정 값을 갖는 행렬, 또는 직교성(orthogonality)이나 유니타리(unitary) 같은 성질을 갖는 행렬 등도 군의 한 예로 볼 수 있다.

전치와 역행렬의 결합

앞서 언급한 전치와 역행렬의 결합 결과로서, 정방행렬 $\mathbf{A}$에 대해

(A1)T=(AT)1(\mathbf{A}^{-1})^\mathsf{T} = (\mathbf{A}^\mathsf{T})^{-1}

가 성립한다는 사실이 중요하다. 이는 $\mathbf{A}\mathbf{A}^{-1} = \mathbf{I}$ 양변에 전치 연산을 취해보면 바로 확인할 수 있다. 대칭 행렬(실수에서)이면서 가역행렬인 경우, $\mathbf{A}$가 스스로의 전치와 같으므로, 역행렬 역시 대칭 형태로 유지된다. 이는 대칭 행렬이 갖는 중요한 장점 중 하나다(수치적인 측면에서도 계산 안정성을 꾀할 수 있음).

매트릭스 노름과 조건수

행렬 곱셈이 실제 수치계산에서 매우 큰 연산량을 소모하는 작업이라는 것은 잘 알려져 있다. 이와 더불어 행렬 연산에서 발생할 수 있는 오차와 민감도(sensitivity)를 평가하기 위해 매트릭스 노름(matrix norm)과 조건수(condition number)가 자주 사용된다.

$\ell_2$-노름(스펙트럴 노름)에서의 행렬 조건수 $\mathrm{cond}_2(\mathbf{A})$는

cond2(A)=A2A12=σmax(A)σmin(A),\mathrm{cond}_2(\mathbf{A}) = \lVert \mathbf{A} \rVert_2 \,\lVert \mathbf{A}^{-1} \rVert_2 = \frac{\sigma_{\max}(\mathbf{A})}{\sigma_{\min}(\mathbf{A})},

로 정의할 수 있다. 여기서 $\sigma_{\max}, \sigma_{\min}$은 각각 $\mathbf{A}$의 최대·최소 특이값(singular value)이다. 이 값이 매우 크게 되면, 역행렬 계산이 수치적으로 불안정해지며, 연립 방정식을 해석하는 데 있어 오차 전파가 심화될 수 있다.

고차원 시스템에서 $\mathbf{A}$의 조건수가 높다는 것은, 아주 작은 교란에도 해가 크게 변화할 수 있음을 의미한다. 따라서 행렬 연산을 단순히 “정확하다”고 믿기보다, 기계 정밀도(machine precision) 하에서 얼마나 안전하게 계산이 가능한지를 “조건수”로 가늠해 보는 관점이 중요하다.

행렬 계산 예시 (Python)

(적절한 크기를 갖는 행렬을 설정하여 간단히 확인할 수 있다. 아래는 Python 예시다.)

위와 같이 $\mathbf{A}$의 역행렬, 전치 등을 직접 계산해 보면, 이론적으로 예상되는 결과(항등행렬 등)를 확인할 수 있다. 큰 문제에서 이런 직접적 접근은 비효율적이지만, 작은 예제로부터 개념을 익히고 수치적인 오차를 살펴보기에는 충분하다.

응용: 가우스 소거와 BLAS 연산

수치선형대수에서는, 행렬 곱셈 연산을 효율적으로 수행하기 위한 수준별 라이브러리(예: BLAS, LAPACK)들이 존재한다. BLAS(Basic Linear Algebra Subprograms)의 기초적인 루틴에서 벡터-벡터, 벡터-행렬, 행렬-행렬 연산 등이 최적화되어 제공된다. 대규모 연립 방정식을 풀 때 가우스 소거(Gaussian elimination)를 직접 구현하기보다는, 이러한 라이브러리에 내장된 고성능 루틴을 사용해 계산 시간을 대폭 줄일 수 있다.

행렬 연산을 단순한 좌표(원소) 수준에서 표현하는 대신, BLAS 레벨3의 핵심인 행렬-행렬 곱(gemm, General Matrix-Matrix multiplication)과 같은 고성능 커널을 적극적으로 활용하면, 현대 CPU와 GPU의 벡터화(vectorization), 메모리 계층 구조(cache hierarchy)를 최대한 활용해 성능을 극적으로 개선할 수 있다. 실제로 오늘날의 많은 수치해석 알고리즘은 ‘행렬 곱셈을 얼마나 효율적으로 재구성해 내는가’를 핵심으로 삼고 있다.

LU 분해와 역행렬

정방행렬 $\mathbf{A}$가 가역행렬이라면, $\mathbf{A}$에 대해 LU 분해가 가능하다고 가정해 보자(적절한 조건, 예: 선형독립인 부분행 벡터 존재 등). LU 분해란, 적당한 하삼각행렬 $\mathbf{L}$과 상삼각행렬 $\mathbf{U}$를 찾아

A=LU\mathbf{A} = \mathbf{L}\mathbf{U}

가 되도록 하는 것이다. $\mathbf{L}$과 $\mathbf{U}$ 모두 역행렬을 갖는다면, $\mathbf{A}^{-1}$를

A1=U1L1\mathbf{A}^{-1} = \mathbf{U}^{-1}\mathbf{L}^{-1}

로 표현할 수 있다. 실제 계산 단계에서는, $\mathbf{L}$과 $\mathbf{U}$ 역시 삼각행렬이므로 역행렬을 구하기가 비교적 단순하다(삼각행렬에 대한 전진·후진 대입). 이러한 LU 분해는 가우스 소거와 밀접하게 연관되어 있으며, 큰 규모의 선형 시스템을 풀 때 역행렬을 직접 구하지 않고도 유사한 효과를 얻을 수 있는 강력한 기법이 된다.

LU 분해 과정에서, $\mathbf{A}$가 피벗(pivot)이 0이 되어버리는 등 문제를 일으킬 때는 부분 피벗팅(partial pivoting)이나 완전 피벗팅(full pivoting) 등 전략을 도입하여 행이나 열을 적절히 교환한다. 이를 행렬적으로 보면, 행 교환을 나타내는 정방행렬(퍼뮤테이션 행렬) $\mathbf{P}$가 함께 곱해져

A=LU\mathbf{A} = \mathbf{L}\mathbf{U}

의 형태를 이루며, 이때 역시 $\mathbf{A}$의 역행렬을 $\mathbf{L}$, $\mathbf{U}$, 그리고 $\mathbf{P}$의 역연산 결합을 통해 얻을 수 있다.

QR 분해와 직교 행렬

행렬 $\mathbf{A}$에 대해, $\mathbf{Q}$가 열 벡터들이 정규직교(orthonormal)된 직교행렬이고, $\mathbf{R}$이 상삼각행렬이 되도록

A=QR\mathbf{A} = \mathbf{Q}\mathbf{R}

라 분해하는 것을 QR 분해라고 한다. 여기서 $\mathbf{Q}$가 정방정교행렬이면 $\mathbf{Q}^{-1} = \mathbf{Q}^\mathsf{T}$가 성립한다. QR 분해는 최소제곱문제(Least Squares) 등을 포함해 각종 최적화 및 추정 문제에서 널리 쓰인다.

행렬 $\mathbf{Q}$는 역행렬을 구할 필요 없이 전치 연산만으로 역이 계산된다는 점에서, 수치적으로 매우 안정적인 형태를 제공한다. 실제로 가우스 소거보다는 QR 분해를 사용해 선형 least squares 문제를 풀었을 때 작은 오차가 누적되는 정도가 줄어드는 등 수치 안정성 향상 효과가 있다. 이때도 역시 ‘곱셈—전치—삼각행렬 구조’가 긴밀히 연관되어 있다.

유니타리 행렬과 에르미트 전치

복소수 행렬에서, 행 벡터들이 정규직교성을 갖도록 구성된 유니타리(unitary) 행렬 $\mathbf{U}$에 대해서는 $\mathbf{U}^{-1} = \mathbf{U}^$가 성립한다. 여기서 $^$는 에르미트 전치(켤레 전치)를 의미한다. 즉,

U=UT,\mathbf{U}^* = \overline{\mathbf{U}}^\mathsf{T},

이며, 실제 수치해석에서 대규모 복소행렬 연산 시에도 직교계나 유니타리계의 구조를 잘 활용하면, 저장 공간이나 연산량을 절약할 뿐만 아니라 오차 전파 특성을 유리하게 설계할 수 있다.

고차원 행렬과 블록 삼각 분해

행렬이 매우 큰 차원을 가질 때, 혹은 행렬이 희소(sparse) 구조를 지니고 있을 때는, 이를 잘 분할하여 블록 삼각 형태로 분해하는 것이 유리할 수 있다. 예를 들어, 대각 블록들이 각각 비교적 작은 규모의 정방행렬(가역행렬)을 이루는 블록 대각 행렬(block diagonal matrix) 구조가 있다면, 전체 시스템을 여러 하위 시스템으로 분할하여 병렬 처리하거나 부분적 업데이트를 수행하기가 훨씬 수월해진다. 전산유체역학(CFD), 유한요소해석(FEM) 등에서 생성되는 행렬들은 그 특성에 따라 특별한 희소 구조(대각지배, 밴드 구조 등)를 갖기도 하므로, 전치나 역행렬 연산 역시 효율적인 형태로 재구성하는 전략이 중요해진다.

수치적 안정성: 반올림과 오차 누적

컴퓨터로 행렬 연산을 수행할 때, 모든 연산은 유한 정밀도 하에서 이루어지므로 반올림 오차가 존재한다. 특히 행렬 곱셈이나 역행렬 계산 같이 연산 횟수가 많은 작업에서는 오차가 여러 차례 누적될 수 있다. 이런 맥락에서, 조건수가 높은 행렬($\mathrm{cond}_2(\mathbf{A})$가 큰 행렬)을 다룰 때는, 매우 작은 오차라도 반복된 연산 이후에는 결과 해에 큰 차이가 생길 수 있다.

IEEE 부동소수점 규격(예: 이중정밀도(double precision))을 사용한다고 해도, 대규모 문제에서 행렬 연산을 반복 수행하면 채우기(fill-in)에 따른 연산 비용 증대와 오차 축적 문제가 동시에 발생한다. 실전에서는 이를 최소화하기 위해 정규화 기법이나 선택적 피벗팅, 재구성된 곱셈(예: 통계적으로 의미 있는 순서로의 재배열) 등을 시도한다. 또한 양의 정부호(positive definite) 행렬의 경우에 한해, 고전적인 LU 분해 대신 더 간단하고 안정적인 촐레스키(Cholesky) 분해를 활용하면 역행렬 계산 등 선형연산을 훨씬 효율적이고 정확하게 처리할 수 있다.

큰 행렬에 대한 SVD(특이값 분해)

특이값 분해(SVD)는 임의의 $m\times n$ 행렬 $\mathbf{A}$에 대해 다음과 같은 분해

A=UΣV\mathbf{A} = \mathbf{U}\,\mathbf{\Sigma}\,\mathbf{V}^*

를 제공한다. 여기서 $\mathbf{U}$와 $\mathbf{V}$는 각각 유니타리 행렬, $\mathbf{\Sigma}$는 대각 원소가 비음이 아닌 실수인 사각 대각 행렬이다. 스펙트럴 노름이나 조건수 계산에 효과적이며, 최소제곱 해, 차원 축소(PCA), 데이터 분석 등에도 광범위하게 응용된다.

SVD 관점에서 행렬 역행렬을 살펴보면, $\mathbf{A}$가 가역행렬일 때, $\mathbf{\Sigma}$의 대각 원소(특이값)가 모두 0이 아니므로 $\mathbf{A}^{-1}$ 역시

A1=VΣ1U\mathbf{A}^{-1} = \mathbf{V}\,\mathbf{\Sigma}^{-1}\,\mathbf{U}^*

로 분해된다. 이때 $\mathbf{\Sigma}^{-1}$의 대각 원소는 $1/\sigma_i$이며, $\sigma_i$가 매우 작은 경우(즉 조건수가 큰 경우) $1/\sigma_i$ 값이 아주 커짐에 따라 수치적으로 불안정해진다. 이러한 해석은 역행렬의 존재 조건과 함께, 실제 계산 시 발생할 오차의 크기까지 정량적으로 파악하도록 해준다.

실습 예시 (Octave)

아래는 Octave에서 행렬의 역행렬과 SVD를 간단히 확인해 볼 수 있는 예시 코드다.

분해된 $\mathbf{U}, \mathbf{S}, \mathbf{V}$를 통해 $\mathbf{A}$의 특이값과 좌·우 특이벡터들을 직접 관찰할 수 있으며, $\mathbf{A}^{-1}$와 비교해가며 해석하는 훈련을 해볼 수도 있다. 실제로는 $n$이 매우 큰 문제에서 직접 이 과정을 계산하기보다는, 수치해석 라이브러리(예: LAPACK) 혹은 전문화된 SVD 알고리즘(예: 고속 SVD, 분산 환경에서의 SVD)을 활용한다.

행렬식(determinant)과 코팩터 확장

정방행렬의 핵심적 특성 중 하나가 바로 행렬식(determinant)이다. $n\times n$ 행렬 $\mathbf{A}$의 행렬식을 $\det(\mathbf{A})$ 또는 $|\mathbf{A}|$라 표기한다. 간단한 예로 $2\times2$ 행렬

A=[a11a12a21a22]\mathbf{A}= \begin{bmatrix} a_{11}& a_{12}\\ a_{21}& a_{22} \end{bmatrix}

에 대해서는

det(A)=a11a22a12a21\det(\mathbf{A})=a_{11}a_{22}-a_{12}a_{21}

라는 공식을 잘 알려져 있다. $3\times3$ 이상의 행렬에서는 코팩터 확장(cofactor expansion) 기법을 통해 행렬식이 정의된다. 예를 들어 $n\times n$ 행렬 $\mathbf{A}$가 있을 때 $a_{ij}$가 해당하는 소행렬(minor)을 $\mathbf{M}{ij}$라 하면, 그 소행렬의 행렬식을 $M{ij}$라 하고, 코팩터(cofactor) $C_{ij}=(-1)^{i+j}M_{ij}$로 정의한다. 이를 바탕으로 $i$번째 행을 기준으로 행렬식을 전개하는 경우,

det(A)=j=1naijCij.\det(\mathbf{A}) = \sum_{j=1}^{n} a_{ij}\,C_{ij}.

열을 기준으로도 동일한 방식의 전개가 가능하다. 이러한 방식으로 행렬식의 재귀적 정의가 이루어지며, 실제 계산 시에는 가우스 소거(LU 분해 등)를 활용해 효율적으로 행렬식을 구한다(소거 단계를 거치며 대각원소의 곱 등으로 계산).

코팩터 행렬(cofactor matrix)을 모아 만든 행렬을 보통 어드조인트(adjoint matrix) 또는 어저그레이트(adjugate matrix)라 부르는데, 이를 $\mathrm{adj}(\mathbf{A})$라 하면 다음이 성립한다.

A1=1det(A)adj(A).\mathbf{A}^{-1} = \frac{1}{\det(\mathbf{A})}\,\mathrm{adj}(\mathbf{A}).

이는 행렬식이 0이 아닐 때(곧 가역행렬일 때)에 유효하다. 행렬의 크기가 크면 이 공식을 직접 쓰는 것은 비효율적이지만, 이론적으로는 모든 원소를 코팩터로 구성해 역행렬을 얻는 방법이다.

행렬 랭크(rank)와 부분행렬

행렬의 랭크는 행과 열로 구성된 선형독립성의 최대 개수를 말한다. 예를 들어 $m\times n$ 행렬 $\mathbf{A}$에 대해, 그 행벡터들의 최대 선형독립 개수(혹은 열벡터들의 최대 선형독립 개수)를 $\mathrm{rank}(\mathbf{A})$라 한다. 만일 정방행렬 $\mathbf{A}$에 대해 $\mathrm{rank}(\mathbf{A}) = n$이면, 이는 곧 $\mathbf{A}$가 가역임을 의미하고, 행렬식이 0이 아니라는 사실과 동치가 된다.

$\mathrm{rank}(\mathbf{A})$가 $n$ 미만이면, 행렬식도 0이 되고, 역행렬이 존재하지 않으며, 선형 방정식 $\mathbf{A}\mathbf{x}=\mathbf{b}$는 특정 조건 하에서만 해가 존재하거나(또는 무수히 많거나) 아예 해가 없을 수도 있다. 따라서 랭크 개념은 수치해석에서 많은 의미를 갖는다. 특히 대형 문제에서, 랭크 결정을 위해 희소 구조나 분해 기법, 정규방정식(normal equations) 접근 등을 활용하기도 한다.

유사역행렬(pseudoinverse)과 무어-펜로즈(Moore-Penrose) 역

정방행렬이 아닌 경우(직사각행렬) 혹은 정방행렬이라도 가역이 아닐 때, 일반적인 역행렬이 존재하지 않는다. 이럴 때 사용하는 개념 중 하나가 유사역행렬(pseudoinverse)이다. 특히 무어-펜로즈(Moore-Penrose) 역행렬 $\mathbf{A}^+$가 널리 쓰인다. $m\times n$ 행렬 $\mathbf{A}$에 대해, 다음과 같은 조건을 만족하는 유일한 행렬 $\mathbf{A}^+$가 존재한다.

AA+A=A,A+AA+=A+,(AA+)T=AA+,(A+A)T=A+A.\begin{align} \mathbf{A}\,\mathbf{A}^+\,\mathbf{A} &= \mathbf{A},\\ \mathbf{A}^+\,\mathbf{A}\,\mathbf{A}^+ &= \mathbf{A}^+,\\ (\mathbf{A}\,\mathbf{A}^+)^\mathsf{T} &= \mathbf{A}\,\mathbf{A}^+,\\ (\mathbf{A}^+\,\mathbf{A})^\mathsf{T} &= \mathbf{A}^+\,\mathbf{A}. \end{align}

직사각행렬 $\mathbf{A}$를 SVD로 분해했을 때,

A=UΣV,\mathbf{A} = \mathbf{U}\,\mathbf{\Sigma}\,\mathbf{V}^*,

$\mathbf{\Sigma}$가 대각원소(특이값)를 $\sigma_1,\ldots,\sigma_r, 0,\ldots,0$ 형태로 가진다고 하자($r$은 행렬의 랭크). 이때

A+=VΣ+U,\mathbf{A}^+ = \mathbf{V}\,\mathbf{\Sigma}^+\,\mathbf{U}^*,

로 주어지는데, $\mathbf{\Sigma}^+$는 $\mathbf{\Sigma}$에서 0이 아닌 대각원소 $\sigma_i$를 $1/\sigma_i$로 뒤집어 배열해 둔 또 다른 사각 대각 행렬이다. 이를 통해 $\mathbf{A}$가 정방행렬이고 가역인 경우에는, 무어-펜로즈 역이 곧 일반적인 역행렬과 일치한다.

최소제곱 문제(Least Squares Solution)에서 $\mathbf{A}\mathbf{x}=\mathbf{b}$를 풀 때, $\mathrm{rank}(\mathbf{A})=n \le m$인 상황이거나, 일반적으로 $\mathbf{b}$가 열공간(column space)에 있지 않아도, 무어-펜로즈 역을 사용해 최소제곱 해를

x=A+b\mathbf{x} = \mathbf{A}^+\mathbf{b}

의 형태로 구할 수 있다. 이는 데이터 핏팅, 신호 처리, 머신러닝 등 다양한 분야에서 중요한 역할을 한다.

예시 (C++): 무어-펜로즈 역

아래와 같은 C++ 코드를 통해, 무어-펜로즈 역을 직접 구하는 대신, 라이브러리(예: Eigen)을 활용해 SVD를 계산하고 이를 조합해 유사역행렬을 얻을 수 있다.

여기서 svd.cols(), svd.rows()A의 열·행 차원에 맞춰진 값이 된다. 실제로 m \ge n인 경우, $S$의 사이즈는 n이 되며, 역행렬(또는 유사역행렬)을 구성하기 위해서는 적절히 블록 크기를 조정해 주어야 한다. 그 결과 생성되는 $\mathbf{A}^+$는 $n\times m$ 크기를 갖는다. 만약 $\mathrm{rank}(\mathbf{A}) < n$이라면 0이 아닌 특이값들에 대해서만 역을 취하고, 0에 해당되는 부분은 그대로 두어야 하므로, 작은 임계값($10^{-12}$ 등)을 두어 조건수가 지나치게 클 경우를 처리해 주기도 한다.

고유값·고유벡터와 대각화

정방행렬 $\mathbf{A}$에 대해 스칼라 $\lambda$와 0이 아닌 벡터 $\mathbf{v}$가 존재하여

Av=λv\mathbf{A}\,\mathbf{v} = \lambda\,\mathbf{v}

를 만족하면, $\lambda$를 $\mathbf{A}$의 고유값(eigenvalue), $\mathbf{v}$를 해당 고유값에 대응하는 고유벡터(eigenvector)라 한다. 고유값은 복소수 범위에서 생각해야 일반적으로 모든 정방행렬에 대해 정의되지만, 만약 $\mathbf{A}$가 실수 대칭행렬이면 모든 고유값이 실수가 되고 직교행렬에 의해 대각화 가능함이 알려져 있다(스펙트럴 정리).

행렬이 대각화(diagonalizable) 가능하다는 것은, $\mathbf{A}$가 (정방)행렬 $\mathbf{P}$에 의해

P1AP=D\mathbf{P}^{-1}\,\mathbf{A}\,\mathbf{P} = \mathbf{D}

형태로 변환될 수 있음을 의미한다. 여기서 $\mathbf{D}$는 대각행렬이다. $\mathbf{A}$의 고유값들을 대각선에 배치하고, 그에 대응하는 고유벡터들을 $\mathbf{P}$의 열벡터들로 삼으면 된다. 이때 모든 고유벡터가 선형독립일 때(중복 고유값의 기하적 차원이 충분히 클 때) 대각화가 가능하다. 행렬이 완벽히 대각화되면, 거듭제곱·지수행렬 등의 연산을 매우 손쉽게 처리할 수 있다.

예를 들어, 어떤 $\mathbf{A}$의 고유분해(eigendecomposition)가

Avi=λivi\mathbf{A}\,\mathbf{v}_i = \lambda_i \,\mathbf{v}_i

를 만족하는 고유값 $\lambda_i$와 고유벡터 $\mathbf{v}_i$들로 구성되어 있고, 이들을 모아

P=[∣∣v1⋯vn∣∣],D=[λ10⋱0λn]

P=[v1vn],D=[λ100λn]\begin{align} \mathbf{P} &= \begin{bmatrix} | & & | \\ \mathbf{v}_1 & \cdots & \mathbf{v}_n \\ | & & | \end{bmatrix}, \\ \mathbf{D} &= \begin{bmatrix} \lambda_1 & & 0 \\ & \ddots & \\ 0 & & \lambda_n \end{bmatrix} \end{align}

로 두면,

AP=PD\mathbf{A}\mathbf{P} = \mathbf{P}\mathbf{D}

에 의해 $\mathbf{P}^{-1}\mathbf{A}\mathbf{P} = \mathbf{D}$임을 알 수 있다.

조던 표준형과 닐포텐트 부분

만약 중복 고유값에 대응하는 고유벡터가 부족하여 충분히 대각화되지 않는 경우에는, 좀 더 일반화된 형태인 조던 표준형(Jordan normal form)으로 $\mathbf{A}$를 나타낼 수 있다. 조던 표준형은 블록 대각행렬이며, 각 블록이 조던 블록(Jordan block)을 이룬다. 예를 들어 고유값이 $\lambda$인 3차 조던 블록은

[λ100λ100λ].\begin{bmatrix} \lambda & 1 & 0\\ 0 & \lambda & 1\\ 0 & 0 & \lambda \end{bmatrix}.

이 블록은 부분적으로 닐포텐트(nilpotent)한 구조를 내포하고 있다. 즉, 위 대각선부(초대각선)의 1들이 연속적으로 존재하므로, 이를 고유벡터만으로는 완전히 분해할 수 없다는 점이 대각화 가능한 경우와의 핵심적 차이다.

조던 표준형을 얻기 위해서는 대각화 과정보다 더 세분화된 단계가 필요하며, 특히 $\mathbf{A}$의 각 고유값에 대해 일반 고유벡터(generalized eigenvector)의 체인을 구성한다. 수치해석적으로도 큰 크기의 행렬에서 조던 표준형을 직접 구하는 일은 매우 민감(조건수가 나쁨)하고 비용이 크므로, 실제로는 주어진 문제 특성에 맞는 다른 분해법(예: Schur 분해)을 사용하는 경우가 많다.

슈어 분해(Schur decomposition)

복소 정방행렬 $\mathbf{A}$에 대해, 유니타리 행렬(실수인 경우 직교행렬) $\mathbf{Q}$가 존재하여

QAQ=T\mathbf{Q}^*\mathbf{A}\,\mathbf{Q} = \mathbf{T}

가 되도록 할 수 있으며, $\mathbf{T}$는 상삼각형 구조를 갖는다. 이를 슈어 분해(Schur decomposition)라 한다. $\mathbf{T}$의 대각성분이 곧 $\mathbf{A}$의 고유값들을 나타낸다. 대각화나 조던 표준형과 달리, 슈어 분해는 수치적으로 비교적 안정적으로 구할 수 있으며, 복소수 영역에서 항상 가능하다.

실수 행렬이라면, $\mathbf{Q}$가 직교행렬, $\mathbf{T}$는 실수 상(上)헤센베르그나 상삼각 형태를 취하게 되지만, 만일 고유값이 실수가 아닌 경우 복소 켤레쌍 블록 형태를 가지는 게 일반적이다. 이 과정을 부분적으로 확장하면, 실부-허수부를 동시에 다루는 실 슈어 분해(real Schur decomposition)까지 정의 가능하다.

지수행렬과 선형미분방정식

선형미분방정식

dxdt=Ax,x(0)=x0\frac{d\mathbf{x}}{dt} = \mathbf{A}\mathbf{x}, \quad \mathbf{x}(0)=\mathbf{x}_0

을 풀면, 해는 지수행렬(matrix exponential) $\exp(\mathbf{A}t)$를 활용하여

x(t)=exp(At)x0\mathbf{x}(t) = \exp(\mathbf{A}t)\,\mathbf{x}_0

라 쓸 수 있음이 잘 알려져 있다. 스칼라 지수함수를 확장한 지수행렬은

exp(M)=k=01k!Mk\exp(\mathbf{M}) = \sum_{k=0}^{\infty} \frac{1}{k!}\mathbf{M}^k

로 정의된다. 만약 $\mathbf{M}$이 대각화 가능하면

exp(M)=Pexp(D)P1,\exp(\mathbf{M}) = \mathbf{P}\,\exp(\mathbf{D})\,\mathbf{P}^{-1},

꼴로 손쉽게 계산할 수 있고, $\exp(\mathbf{D})$는 대각 원소에 각각 스칼라 지수 $\exp(\lambda_i)$를 두는 형태가 된다. 조던 표준형이거나 슈어 분해 등을 통해서도 지수행렬을 효과적으로 계산할 수 있다. 수치적 관점에서는 패드(Padé) 근사, 스케일-스퀘어(scale-and-square) 방법 등이 실제 구현에서 널리 쓰인다.

다항식 행렬 연산과 케일리-해밀턴 정리

행렬에 대한 다항식(polynomial in matrix) 역시,

(A)=a0I+a1A++akAk(\mathbf{A}) = a_0 \mathbf{I} + a_1 \mathbf{A} + \cdots + a_k \mathbf{A}^k

형태로 정의할 수 있다. 이때 $p(\lambda)$가 스칼라 다항식으로서, 만약 $\lambda$가 $\mathbf{A}$의 고유값이라면, $p(\mathbf{A})$ 역시 고유값과 밀접한 연관이 있다. 케일리-해밀턴 정리(Cayley-Hamilton theorem)는 행렬 $\mathbf{A}$가 자기 자신의 특성다항식(characteristic polynomial)을 만족한다는 정리를 말한다.

χA(λ)=det(λIA),\chi_\mathbf{A}(\lambda)=\det(\lambda \mathbf{I} - \mathbf{A}),

라 할 때 $\chi_\mathbf{A}(\mathbf{A})=\mathbf{0}$가 된다는 것이다. 이를 통해, 예를 들어 역행렬이 존재할 때,

A1=1det(A)()\mathbf{A}^{-1} = \frac{1}{\det(\mathbf{A})}\bigl( \cdots \bigr)

와 같은 꼴로 특성다항식의 계수를 활용해 간접적으로 표현하기도 한다. 큰 문제에서는 직접 이 공식을 쓰기보다는 다른 분해(예: LU 분해)나 알고리즘을 쓰지만, 이론적으로는 행렬에 대한 고차 다항식이나 해석적 연산을 전개할 때 중요한 역할을 한다.

수치 알고리즘과 안정성

실제로 $\mathbf{A}$의 고유값이나 슈어 분해, 혹은 SVD를 구하는 대부분의 알고리즘은 연산 과정에서 블록 연산, QR 알고리즘, 행렬 멱제곱, 행·열 교환 등을 복합적으로 사용한다. 이 때 발생하는 반올림 오차나 수렴 속도가 문제의 규모에 따라 큰 변동을 보인다. 현대 수치알고리즘(예: QR 알고리즘, Divide-and-Conquer SVD, MRRR 알고리즘 등)은 이 같은 안정성과 효율성을 모두 고려하여 설계된다.

고유값 문제(eigenvalue problem)나 특이값 문제(singular value problem)를 매우 큰 스케일에서 다룰 때는, 단순 반복연산 대신 크릴로프 하부공간(Krylov subspace) 기반 기법(GMRES, Arnoldi 방법 등)을 사용해 특정 부분스펙트럼만 효율적으로 찾기도 한다. 이는 연립방정식 풀이에서의 반복법(GMRES, Conjugate Gradient 등)과 원리가 맞닿아 있다.

결국 행렬의 덧셈, 곱셈, 전치, 역행렬 같은 기초연산부터 블록 분할, 분해 기법, 고유값·특이값 계산 알고리즘까지 모두 연결되어, 수치해석 전반에서 매우 중요한 부분을 차지한다. 이런 연산을 빠르고 안전하게 수행하는 것이 선형대수학 기반 알고리즘의 핵심이 되며, 이를 최적화하기 위해 하드웨어 수준에서의 병렬화, 캐시 구조 활용, GPU 가속 등이 활발히 연구·활용되고 있다.

Last updated