8 minute read

📌 오늘의 학습 목표

행렬을 배웠다면, 이제 그 행렬을 가지고 마법을 부릴 차례임! 숫자 나누셈처럼 행렬도 나눌 수 있을까? 데이터를 예쁘게 분해하거나 특수한 모양의 행렬을 만들면 인공지능 연산이 얼마나 빨라질까?

  • 가역과 역행렬: 행렬의 “나눗셈” 역할을 하는 역행렬의 개념과 존재 여부(행렬식)를 파악함.
  • 특수행렬과 대칭행렬: 단위행렬, 영행렬, 대각행렬, 그리고 (반)대칭행렬의 성질을 이해함.
  • 역행렬을 이용한 방정식 해법: $Ax = B$를 $x = A^{-1}B$로 한 방에 풀어내는 마법을 배움.
  • LU 분해: 복잡한 행렬을 두 개의 쉬운 삼각형 모양으로 쪼개는 기법을 실습함.
  • 치환(Permutation)과 짝/홀 치환: 행렬식을 구하는 밑바탕인 치환의 반전(Inversion) 개념을 정립함.
  • SageMath 실습: 위 모든 개념을 파이썬 코드로 자동 계산해 보는 예제(5, 6, 8, 9, 10, 11, 15)를 수행함.

1. 가역행렬과 역행렬 (Invertibility and Inverse Matrix)

초등학교 때 우리는 숫자 곱셈의 반대되는 개념으로 ‘역수’를 배웠음. 예를 들어 숫자 $3$에 그 역수인 $1/3$을 곱하면 아무 의미 없는 기본 숫자 $1$이 됨.
행렬의 세계에서도 똑같은 치트키가 존재함! 똑같은 크기의 네모난 표(행렬) 두 개를 곱했는데, 아무 효과가 없는 투명 망토 같은 단위행렬($I$) (대각선에만 1이 있고 나머지는 0인 표)이 튀어나온다면?

이때 곱해준 행렬을 원래 행렬의 “역행렬(Inverse Matrix)” 이라고 부름 ($A^{-1}$로 표기함). 그리고 이렇게 짝꿍 역행렬을 가진, 뒤집기가 가능한 행렬을 “가역행렬(Invertible Matrix)” 이라고 함.

하지만 주의할 점! 숫자 0에는 역수가 없듯이, 모든 행렬이 역행렬을 가질 수 있는 건 아님. 이 행렬이 역행렬이 존재하는지 아닌지를 단번에 판별해 주는 판독기가 바로 “행렬식(Determinant, $det$)” 임. 행렬식 값이 0이 아니면 무조건 역행렬이 존재함!

🧮 [예제 5, 6] 가역 여부 체크 및 역행렬 구하기 실습 (in SageMath)

손으로 3x3 표의 역행렬을 구하려면 수십 번의 덧셈 뺄셈 곱셈을 하다가 지쳐 쓰러짐. 하지만 컴퓨터는 클릭 한 번으로 가역성(Invertibility) 판독부터, 실제 역행렬을 구하는 것까지 끝내버림.

# SageMath 환경에서 실행
# 3x3 크기의 임의의 행렬 A를 만듦
A = matrix(QQ, [
    [ 1,  2,  3],
    [ 2,  5,  3],
    [ 1,  0,  8]
])

print("--- 원본 행렬 A ---")
print(A)

# [예제 5] 행렬 A가 가역(역행렬이 존재하는지)인지 체크함!
# 방법 1: 내장 함수 is_invertible() 사용
is_inv = A.is_invertible()

# 방법 2: 행렬식(Determinant)이 0인지 아닌지 확인
det_A = A.det()

print(f"\nA의 행렬식 값: {det_A}")
if is_inv:
    print("결과: 행렬식이 0이 아니므로 역행렬이 존재함! (가역행렬)")
else:
    print("결과: 행렬식이 0이므로 역행렬이 없음! (특이행렬)")


# [예제 6] 역행렬 구하기 실습
if is_inv:
    # 역행렬을 구하는 마법의 명령어 .inverse()
    A_inv = A.inverse()
    print("\n--- A의 역행렬 (A^-1) ---")
    print(A_inv)
    
    # 정말 역행렬이 맞는지 확인해보기 (A와 A_inv를 곱함)
    # 곱하면 대각선만 1인 '단위행렬'이 나와야 함!
    print("\n--- 검증: A * A^-1 (단위행렬이 나와야 함) ---")
    print(A * A_inv)

💡 결과 직관적 이해

  • 행렬식을 계산해 보니 값이 -1 (즉 0이 아님)이 나옴. 따라서 역수가 존재하는 멀쩡한 행렬임!
  • 만들어진 A_inv를 원래 A와 곱해 보면 [[1,0,0], [0,1,0], [0,0,1]] 이라는 깔끔한 투명 망토 투명행렬(단위행렬)로 싹 정화되는 것을 볼 수 있음.

(아래는 행렬을 곱해 찌그러진 공간을, 역행렬을 곱해서 다시 원래의 반듯한 격자 공간으로 완벽히 복구시키는 마법을 시각화한 것임) 역행렬의 공간 복구 시각화


2. 역행렬을 대포로 쏴서 선형연립방정식 단번에 풀기

앞선 포스트에서 연립방정식을 풀 때 가우스-조던 소거법(가감법 청소)을 써서 매번 0으로 청소를 해줬음. 하지만 방금 배운 “역행렬”을 무기로 쓰면 훨씬 더 통쾌하고 간결하게 방정식을 박살낼 수 있음.

$Ax = B$ (A: 계수행렬, x: 미지수, B: 결과값)

이 식에서 우리가 궁금한 건 미지수 $x$임. 그럼 양변 앞쪽에 $A$의 천적인 역행렬 $A^{-1}$를 곱해서 $A$를 깔끔하게 없애버리면 됨!

$A^{-1} * A * x = A^{-1} * B$
➡️ 수치 1로 변해서 사라짐: $I * x = A^{-1} * B$
➡️ 최종 공식: $x = A^{-1} * B$

완전 마법의 공식임! 미지수를 찾고 싶으면 결과값 B에 역행렬만 툭 곱해주면 정답이 바로 나옴.

🧮 [예제 8] 역행렬을 이용한 선형연립방정식 풀이 (in SageMath)

# SageMath 환경에서 실행
# 풀고자 하는 연립방정식 체계
#  1x + 2y + 3z = 1
#  2x + 5y + 3z = 6
#  1x + 0y + 8z = -6

# 1. 왼쪽 숫자들만 모은 계수행렬 A
A = matrix(QQ, [
    [1, 2, 3],
    [2, 5, 3],
    [1, 0, 8]
])

# 2. 오른쪽 결과만 세로로 모은 벡터 B
B = vector(QQ, [1, 6, -6])

# 3. 해답 구하기! (x = A^-1 * B)
# A의 역행렬을 구한 뒤 곧바로 B와 곱해버림!
x = A.inverse() * B

print("\n--- 역행렬을 이용해 구한 정답 x, y, z ---")
print(x)

💡 놀라운 점

가우스 소거법으로 add_multiple_of_row를 수십 번 타이핑하던 수고로움 없이, 역행렬 하나 구해서 우변에 딱 한 번 곱해주자마자 미지수 세 개의 정답이 리스트로 툭 튀어나옴! 수학적 우아함의 극치임.


3. 컴퓨터 연산을 날개 달아주는 여러 가지 “특수행렬”

딥러닝 모델에는 1억 개, 10억 개의 숫자가 행렬에 꽉꽉 들어참. 이걸 매번 무식하게 다 연산하려면 슈퍼컴퓨터도 뻗어버림. 그래서 특별한 위치에 0이 가득 차 있는 특수한 모양의 스켈레톤(뼈대) 행렬들을 활용하면, 곱셈을 할 때 0 때문에 연산이 몽땅 무효화되어서 스피드가 미친 듯이 빨라짐.

🧮 [예제 9] 대각행렬, 단위행렬, 영행렬 (in SageMath)

  1. 대각행렬 (Diagonal Matrix): 정중앙 대각선에만 알맹이(숫자)가 있고 나머지는 싹 다 0으로 비워둔 행렬.
  2. 단위행렬 (Identity Matrix): 대각행렬 중에서도 대각선 숫자가 무조건 ‘1’인 행렬 (마치 숫자계의 투명 망토 1과 같음).
  3. 영행렬 (Zero Matrix): 모든 칸이 0으로 가득 찬 행렬 (숫자계의 0과 같음).
# SageMath 환경에서 실행
n = 3 # 3x3 크기 지정

# 1. 영행렬 (세상 쓸모없어 보이지만 초기화할 때 필수임)
Z = zero_matrix(QQ, n)
print("--- 영행렬 ---")
print(Z)

# 2. 단위행렬 (가장 완벽한 투명 망토. 아이덴티티!)
I = identity_matrix(QQ, n)
print("\n--- 단위행렬 ---")
print(I)

# 3. 대각행렬 (대각선에 내가 원하는 숫자만 콕콕 박아둠)
D = diagonal_matrix(QQ, [2, -5, 7])
print("\n--- 대각행렬 ---")
print(D)

4. (반)대칭행렬 (Symmetric / Skew-symmetric Matrix)

표(행렬)의 대각선을 축으로 해서, 마치 데칼코마니(종이접기)하듯 양옆 숫자들이 거울상으로 매칭되는지 확인하는 개념임.

  • 대각선을 전치(Transpose, 행과 열 위치를 뒤바꿈) 시켰을 때:
    • 아수라 백작처럼 원본 표와 똑같으면: 대칭행렬 (Symmetric, $A = A^T$)
    • 원본 표 숫자들에 마이너스(-) 부호만 싹 붙인 거랑 똑같으면: 반대칭행렬 (Skew-symmetric, $A = -A^T$)

🧮 [예제 10] (반)대칭행렬 체크 (in SageMath)

# SageMath 환경에서 실행

# 데칼코마니 대칭행렬 만들기
# 대각선 [1, 5, 9]를 기준으로 위아래 모서리 숫자가 거울처럼 같음!
S = matrix(QQ, [
    [1, 2, 3],
    [2, 5, -4],
    [3, -4, 9]
])

# 반대칭행렬 만들기 (대각선은 무조건 0이어야 거울 반대가 됨!)
# 대각선 기준 거울 너머 방에 부호만 정확히 정반대인 애들이 살고 있음.
K = matrix(QQ, [
    [ 0,  2, -3],
    [-2,  0,  4],
    [ 3, -4,  0]
])

# 전치행렬(.transpose()) 기능으로 변신시켜보고 결과 비교하기!
print("S가 대칭행렬인가? S == S^T :", S == S.transpose())
print("K가 반대칭행렬인가? K == -K^T :", K == -K.transpose())
  • 거울에 비친 듯 데이터가 완벽히 균형 잡혀 있는지 확인할 때 유용함 (AI에서는 공분산 행렬(Covariance matrix)이 대표적인 대칭행렬임).

5. 큰 바위를 모래로 쪼갠다, LU 분해 (LU Decomposition)

수만 줄짜리 엄청나게 거대한 연립방정식 행렬을 통째로 풀기엔 너무 하드코어 함. 수학자들은 또 잔머리를 굴려서 복잡한 네모 표 1개를, 비교적 다루기 쉬운 세모 표 2개로 미리 쪼개어 분해해 두는 비법을 고안했음. 이것이 바로 “LU 분해” 임.

  1. L (Lower triangular): 위쪽은 다 0이고 아래쪽에만 숫자가 모인 하삼각행렬 (밑이 무거운 세모)
  2. U (Upper triangular): 밑쪽은 다 0이고 위쪽에만 숫자가 모인 상삼각행렬 (위가 뚱뚱한 세모)

$A = L * U$ 로 쪼개두면, 무거운 바위($A$)를 들고 역행렬 계산을 꾸역꾸역 안 해도, 가벼운 두 모래 덩어리($L, U$)를 이용해 순식간에 답을 유도해 낼 수 있어 컴퓨터 연산 메모리가 획기적으로 절약됨.

🧮 [예제 11] LU 분해 실습 (in SageMath)

# SageMath 환경에서 실행
# 매우 평범하고 꽉 찬 행렬 A 
A = matrix(QQ, [
    [ 2, -1,  1],
    [ 4,  1, -1],
    [ 2, -7,  3]
])

print("--- 원본 꽉 찬 행렬 A ---")
print(A)

# 마법의 명령어 LU 분해 시작! (P, L, U 세 가지를 반환해 줌)
# P는 행 순서를 조정한 순열(Permutation) 행렬의 껍데기인데, 일단 무시해도 좋음.
P, L, U = A.LU()

print("\n--- 아래쪽이 뚱뚱한 L (하삼각행렬) ---")
print(L)

print("\n--- 위쪽이 뚱뚱한 U (상삼각행렬) ---")
print(U)

# 검증: P * L * U 를 다시 곱하면 원래의 A로 부활하는가?
print("\n--- P * L * U 원상복구 체크 ---")
print(P * L * U == A)

(아래는 정육면체를 한 번에 찌그러뜨리는 복잡한 행렬 A 대신, U(오렌지)로 가로축을 먼저 밀고, L(초록)로 세로축을 마저 밀어서 최종적으로 A와 똑같은 결과를 두 번에 나누어 쉽게 달성하는 시각화임) LU 분해 변환 시각화


6. 치환(Permutation)과 짝/홀 반전의 마법

행렬의 성질이 가역인지 판별하려면 “행렬식(Determinant)”이라는 최종 합격 점수를 매긴다고 했음. 이 점수를 세부적으로 쪼개고 또 쪼개면 가장 밑바닥에는 자연수들의 위치 자리 바꿈인 치환(Permutation) 이라는 게임 규칙이 숨어있음.

  • $1, 2, 3, 4$ 라면 올바른 오름차순(기본 상태)임.
  • 그런데 $2, 1, 4, 3$ 처럼 친구들이 자리를 서로 엉뚱하게 바꾸어버린 것을 ‘치환되었다’고 함.
  • 이때 앞에 있는 숫자가 자기보다 뒤에 있는 숫자보다 덩치가 크면 “어? 족보가 꼬였는데?” 하고 반칙 스티커를 발부함. 이 반칙 스티커 발부 횟수를 “반전 수(Inversions)” 라고 부름.
  • 이 반칙 스티커 개수가 짝수 개면 짝치환(Even permutation), 홀수 개면 홀치환(Odd permutation) 이라 부름!

🧮 [예제 15] 짝/홀 치환 판정 실습 (in SageMath)

# SageMath 환경에서 실행
# [3, 1, 4, 2] 로 자리를 바꾼(치환) 배열을 만듦
# 원래 자리 1, 2, 3, 4 에서 3은 1번으로, 1은 2번으로 갔다는 뜻.
p = Permutation([3, 1, 4, 2])
print(f"현재 치환 배열: {p}")

# 1. 족보가 꼬인 횟수(반전 수 Inversions) 구하기
# 3 > 1 (꼬임) / 3 > 2 (꼬임) / 4 > 2 (꼬임) -> 총 3번 꼬임!
inv_count = p.number_of_inversions()
print(f"족보 파괴(반전) 횟수: {inv_count}")

# 2. 짝치환인가 홀치환인가?
# 반칙 스티커가 3개(홀수) 이므로 홀치환일 것임.
print(f"짝치환(Even) 인가?: {p.is_even()}")
if p.is_even():
    print("결론: 반전 횟수가 짝수인 짝치환입니다.")
else:
    print("결론: 반전 횟수가 홀수인 홀치환입니다.")

💡 왜 치환을 배울까?

나중에 복잡하고 커다란 수십 칸짜리 행렬의 “행렬식 점수”를 구할 때, 짝치환인 것들은 + 부호를 주고, 홀치환으로 자리가 꼬인 항목들은 - 부호를 줘서 전부 더하게 됨. 즉, 이 족보의 꼬임 횟수가 인공지능이 계산하는 행렬식 플러스/마이너스 룰의 가장 원초적인 규칙임!


🎯 오늘의 요약 (Summary)

  • 역행렬: 행렬계의 역수. 특이하게도 행렬식(Determinant)이 0만 아니면 역수를 구할 수 있고, 이를 활용해 연립방정식 덩어리를 x = A의 역행렬 * B 로 아주 우아하고 시크하게 한 방에 폭발시켜 답을 잡을 수 있음.
  • 특수행렬과 (반)대칭: 계산할 때 0을 통해 치명타를 입히거나(영행렬, 대각행렬), 데칼코마니처럼 거울 반사벽을 쳐서(대칭행렬) 인공지능의 계산 메모리와 속도를 비약적으로 혁신시키는 뼈대 기술임.
  • LU 분해: 엑스칼리버처럼 단단한 큰 표 하나를, 가벼운 하삼각(L)/상삼각(U) 표 두 개로 예쁘게 등분하여 쪼개는 기술임.
  • 치환과 꼬임: 자리 바꿈(순열) 속에서 큰 숫자가 작은 숫자보다 앞에 서는 “하극상(반전)”이 몇 번 발생했는지 카운트하여, 나중에 행렬 폭발의 부호(+나 -)를 결정짓는 근원임.

Comments