LinearBook 3장과 4장에서 소거, 후진 대입, A=LU, 행 교환, 띠 행렬을 배웠고, 16장에서 조건수와 후방 안정성을 맛보았습니다. 이 장은 같은 알고리즘을 계산하는 사람의 눈으로 다시 봅니다. 연산이 정확히 몇 번인지, 피벗팅이 무엇을 지키고 무엇은 못 지키는지(성장 인자), 대칭 양정치 행렬에서 비용을 절반으로 줄이는 촐레스키 분해, O(n) 토머스 알고리즘의 안정 조건, 답이 몇 자리까지 믿을 만한지 알려 주는 조건수 한계, 단정밀도로 분해하고 배정밀도 답을 얻는 반복 개선, 그리고 10⁶ 미지수의 희소 행렬에서 메모리를 정하는 채움과 재배열까지 다룹니다.
가우스 소거와 LU 분해의 연산 수 32n3을 유도하고, 분해와 대입의 비용을 구별해 여러 우변 문제에 적용한다.
작은 피벗의 위험을 유효 숫자 계산으로 보이고, 부분·척도 부분·완전 피벗팅을 비교하며, 성장 인자와 후방 오차의 관계를 설명한다.
PA = LU를 손으로 계산하고, 대칭 양정치 행렬의 촐레스키 분해와 그 비용·안정성을 안다.
삼중대각계를 토머스 알고리즘으로 O(n)에 풀고, 대각 우세가 안정성을 보장하는 이유를 안다.
벡터·행렬 노름과 조건수를 계산하고, ∥δx∥/∥x∥≤κ∥δb∥/∥b∥로 잃는 자릿수를 예측한다. 작은 잔차가 작은 오차를 뜻하지 않음을 안다.
반복 개선(혼합 정밀도)의 원리와 한계를 실험으로 확인한다.
희소 행렬의 채움을 이해하고, 재배열(RCM, 최소 차수, 중첩 분할)이 메모리와 시간을 줄이는 원리를 안다.
이 수식이 없었다면구조 해석과 회로 해석
다리, 비행기 날개, 반도체 칩 단면을 유한 요소로 나누면 수십만에서 수천만 개의 미지수를 가진 연립방정식 Ku=f가 나옵니다. 회로 시뮬레이터는 시간 걸음마다 노드 전압에 대한 연립방정식을 풉니다. 이 방정식들이 현실적인 시간에 풀리는 것은 행렬이 거의 0으로 차 있고(희소), 대칭 양정치인 경우가 많으며(촐레스키), 피벗팅과 재배열로 반올림 오차와 메모리를 동시에 통제하는 직접 해법 라이브러리가 있기 때문입니다. 1940년대 말 튜링(Alan Turing)은 소거의 반올림 오차를 분석하며 "조건수"라는 이름을 붙였고, 1960년대 윌킨슨(James H. Wilkinson)의 후방 오차 분석이 부분 피벗팅 소거를 믿고 쓸 수 있는 근거를 주었습니다.
A x = b→피벗팅 + 분해→PA = LU, A = LLᵀ→구조·회로·FEM 해석기
가우스 소거의 연산 수
미지수가 10배면 시간은 1000배
미지수 1,000개짜리 계를 푸는 데 0.1초가 걸렸습니다. 같은 프로그램으로 10,000개짜리를 풀면 몇 초가 걸릴까요? 메모리는 몇 배가 필요할까요? 답을 미리 알아야 계산을 설계할 수 있습니다.
열 우선 순서로 쓴 LU 분해의 핵심은 세 겹의 반복문입니다. k번째 단계에서 피벗 아래의 n−k개 행마다 승수를 하나 구하고(나눗셈), 남은 (n−k)×(n−k) 부분 행렬을 갱신합니다(곱셈과 뺄셈).
for k = 1 … n−1
for i = k+1 … n
a[i,k] ← a[i,k] / a[k,k] // 승수 ℓᵢₖ를 그 자리에 저장for j = k+1 … n
a[i,j] ← a[i,j] − a[i,k]·a[k,j] // 슈어 보수 갱신: 곱 1 + 뺄셈 1// 끝나면 a의 아래 삼각 = L(대각 1 생략), 위 삼각 = U
정리 12.1 (LU 분해와 대입의 연산 수) 크기 n인 LU 분해의 부동소수점 연산 수(곱셈·뺄셈·나눗셈 각각 1회로 셈)는
m=1∑n−1(2m2+m)=32n3−21n2−61n≈32n3,
전진 대입 Ly=b와 후진 대입 Ux=y는 각각 약 n2회다. 따라서 분해 한 번이 대입 한 번의 약 n배 비용이다.
셈. 단계 k에서 m=n−k라 하면 나눗셈 m번, 곱셈·뺄셈 m2번씩이다. ∑m=1n−1m2=6(n−1)n(2n−1), ∑m=2n(n−1)를 넣어 정리하면 위 식이다. ∎
그래서 문제 상자의 답은 103배, 약 100초입니다. 메모리는 n2이라 100배(배정밀도 104×104 행렬은 800 MB)입니다. 실제 라이브러리(LAPACK의 dgetrf)는 같은 연산을 블록 단위로 묶어 행렬–행렬 곱(BLAS 3단계)으로 바꿉니다. 연산 수는 같지만 캐시에 올린 데이터를 여러 번 재사용하므로, 위의 단순한 세 겹 반복보다 수십 배 빠르게 돕니다.
작업
연산 수(주항)
n = 1000일 때
LU 분해 PA=LU
32n3
6.7×10⁸
촐레스키 A=LLT (4절)
31n3
3.3×10⁸
전진 + 후진 대입 (우변 하나)
2n2
2.0×10⁶
역행렬 A−1 전체
2n3
2.0×10⁹
토머스 알고리즘 (삼중대각, 5절)
8n
8.0×10³
표의 넷째 줄이 "역행렬을 계산하지 말라"는 격언의 근거입니다. A−1b를 구하려고 역행렬을 만들면 분해만 할 때의 세 배를 쓰고, 정확도도 대개 더 나쁩니다. 우변이 m개 있으면 분해는 한 번만 하고 대입을 m번 하면 됩니다(32n3+2mn2).
피벗팅: 작은 피벗과 성장 인자
정확히 풀리는 2×2가 틀린다
0.0001x+y=1, x+y=2의 해는 x=1.0001, y=0.9999쯤입니다. 소수 세 자리 유효 숫자로 계산하는 기계(매 연산마다 유효 숫자 3개로 반올림)로 첫 식을 피벗으로 소거하면 x=0이 나옵니다. 배정밀도에서도 피벗이 10−17이면 같은 일이 생깁니다(LinearBook 3장의 시뮬레이터).
예제 12.1 (유효 숫자 3자리 소거) 위 계를 매 연산 뒤 유효 숫자 3자리로 반올림하며 풀어라.
피벗팅 없이: 승수 ℓ=1/0.0001=104. 2행: 1−104⋅1=−9999→−1.00×104, 우변 2−104=−9998→−1.00×104. 그러면 y=1.00, x=(1−1.00)/0.0001=0. x가 완전히 틀렸다. 2행의 원래 정보 "1"과 "2"가 104에 묻혀 사라졌다.
부분 피벗팅: 절댓값이 큰 1을 피벗으로 행을 바꾼다. ℓ=0.0001, 2행: 1−0.0001=0.9999→1.00, 우변 1−0.0002=0.9998→1.00. y=1.00, x=2−1.00=1.00. 3자리 기계가 낼 수 있는 최선의 답이다.
부분 피벗팅은 승수를 ∣ℓik∣≤1로 묶습니다. 그런데 행 하나에 큰 수를 곱해 두면 이 장치가 속습니다. 10x+105y=105, x+y=2(첫 식에 105를 곱한 것과 같은 계)에서 부분 피벗팅은 10을 피벗으로 골라 3자리 계산에서 다시 x=0을 냅니다. 행마다 가장 큰 원소 si=maxj∣aij∣로 나눈 상대 크기 ∣aik∣/si로 피벗을 고르면(10/105 대 1/1) 둘째 행이 선택되어 x=y=1.00이 나옵니다. 이것이 척도 부분 피벗팅(scaled partial pivoting)입니다. 실무에서는 행렬을 미리 평형화(행·열 척도 조정)하고 부분 피벗팅을 씁니다.
정의 12.2 (피벗팅 전략과 성장 인자)k번째 단계에서 부분 피벗팅은 k열의 k행 이하에서 절댓값 최대인 원소를, 완전 피벗팅은 남은 부분 행렬 전체에서 절댓값 최대인 원소를 골라 행(과 열)을 바꾼다. 소거 중 나타나는 모든 중간 원소 aij(k)에 대해
ρn=maxi,j∣aij∣maxi,j,k∣aij(k)∣
를 성장 인자(growth factor)라 한다. 완전 피벗팅은 PAQ=LU를 만들며, 원소 비교가 단계마다 O((n−k)2)이라 전체 O(n3)번의 비교가 더 든다.
정리 12.3 (소거의 후방 오차, 윌킨슨) 부동소수점에서 피벗팅을 쓴 가우스 소거로 얻은 해 x^는 약간 흔들린 계 (A+ΔA)x^=b의 정확한 해이고,
∥A∥∞∥ΔA∥∞≤cn3ρnu
가 성립한다(u는 단위 반올림, c는 크지 않은 상수; 실제 오차는 이 상한보다 훨씬 작다). 부분 피벗팅의 성장 인자는 ρn≤2n−1이고, 이 상한에 도달하는 행렬이 있다.
요지.ρn이 작으면 소거는 후방 안정하다(2장). 부분 피벗팅에서는 ∣ℓik∣≤1이라 한 단계에 원소가 많아야 두 배가 되어 2n−1이 나온다. 피벗팅이 없으면 ρn에 상한이 없다.
상한에 닿는 예가 윌킨슨의 행렬입니다. 대각이 1, 대각 아래가 모두 −1, 마지막 열이 모두 1인 행렬은 부분 피벗팅이 행을 바꾸지 않고(첫 열의 최댓값이 이미 대각에 있음), 마지막 열이 단계마다 두 배가 되어 unn=2n−1입니다. 조건수는 n 정도로 작으니 문제는 전혀 예민하지 않은데, 알고리즘이 답을 망칩니다.
윌킨슨 행렬, 배정밀도
n = 30
n = 40
n = 50
n = 55
n = 60
부분 피벗팅 성장 인자 ρₙ
5.4×10⁸
5.5×10¹¹
5.6×10¹⁴
1.8×10¹⁶
5.8×10¹⁷
부분 피벗팅 상대 오차
9.4×10⁻⁹
8.2×10⁻⁶
1.4×10⁻²
1.5×10⁻¹
1.6
완전 피벗팅 ρₙ / 상대 오차
2 / 10⁻¹⁶ 이하
2 / 10⁻¹⁶ 이하
2 / 10⁻¹⁶ 이하
2 / 10⁻¹⁶ 이하
2 / 10⁻¹⁶ 이하
표는 참해를 무작위로 정하고 b=Ax로 만든 실험입니다. 그런데 이런 행렬은 실제 응용에서 거의 나오지 않습니다. 무작위 50×50 행렬 200개에 부분 피벗팅을 쓰면 성장 인자의 중앙값은 7.5, 최댓값은 15.8이었습니다. 이 경험적 사실 때문에 LAPACK의 일반 풀이기는 부분 피벗팅을 기본으로 쓰고, 완전 피벗팅은 거의 쓰지 않습니다.
SIMULATOR
피벗팅 전략 비교: 유효 숫자 d자리 기계의 소거
피벗팅 없음부분척도 부분완전
참해 x—
없음 · 부분—
척도 · 완전—
성장 인자 (없음 / 부분)—
해볼 것: ① '작은 피벗'에서 d = 3, ε = 10⁻⁴는 예제 12.1입니다. 막대(상대 오차, 로그)가 피벗팅 없음만 10⁰ 근처입니다. ② '행 척도 차이'로 바꾸면 부분 피벗팅도 속고, 척도 부분과 완전 피벗팅만 맞습니다. ③ d를 16으로 올리면 거의 모두 맞지만, ε를 10⁻¹²로 내리면 d = 10에서 피벗팅 없음이 다시 틀립니다. '윌킨슨 24×24'에서는 d = 6이면 부분 피벗팅도 틀리고 완전 피벗팅만 맞습니다(성장 2²³ ≈ 8×10⁶). 모델: 매 사칙연산 결과를 유효 숫자 d자리로 반올림(입력도 반올림). 오차는 ‖x̂ − x‖∞/‖x‖∞, 0은 10−17로 표시.
SIMULATOR
성장 인자: 윌킨슨 행렬과 무작위 행렬
성장 인자 ρₙ와 상대 오차 vs n (로그 눈금)
부분 피벗팅 후 |U|의 히트맵 (로그 색)
행렬
부분 피벗팅 ρₙ완전 피벗팅 ρₙ부분 피벗팅 상대 오차2ⁿ⁻¹
ρₙ 부분 · 완전—
상대 오차 부분 · 완전—
max |uᵢⱼ| 위치—
해볼 것: ① 윌킨슨 행렬에서 부분 피벗팅 ρₙ(주황)이 2ⁿ⁻¹ 직선에 정확히 붙어 있고, n ≈ 50부터 오차(빨강)가 10⁻²를 넘습니다. ② 히트맵에서 큰 값은 U의 마지막 열에만 모입니다. ③ '무작위'로 바꾸면 ρₙ가 n에 따라 아주 천천히 자랍니다. 같은 알고리즘인데 최악과 평균이 이렇게 다릅니다. 모델: 참해는 시드 고정 무작위 벡터, 배정밀도, 무작위 행렬은 n마다 하나.
PA = LU와 여러 우변
부분 피벗팅을 쓴 소거는 행 교환을 기록하는 치환 행렬 P와 함께 PA=LU를 만듭니다(LinearBook 4장의 정리 4.3). 구현에서는 P를 행렬로 저장하지 않고 정수 배열 piv로 두며, 승수 ℓik를 소거로 0이 된 칸에 덮어써서 L과 U를 원래 배열 하나에 담습니다. 행을 바꿀 때는 이미 저장한 승수도 함께 바뀐다는 것이 손계산의 함정입니다.
정리 12.4 (PA = LU의 존재와 사용) 가역 행렬 A에 대해 PA=LU(L은 대각이 1이고 ∣ℓij∣≤1인 하삼각, U는 상삼각)인 치환 행렬 P가 언제나 존재한다. 이 분해로
Ax=b: Ly=Pb를 전진 대입, Ux=y를 후진 대입(2n2).
행렬식: detA=(−1)s∏uii(s는 행 교환 횟수). 공식 전개(n!항) 대신 O(n3).
전치 계 ATx=c: UTz=c, LTw=z, x=PTw. 같은 분해를 재사용한다.
예제 12.2 (부분 피벗팅 PA = LU 손계산)A=142232−113, b=(2,8,7)를 풀어라.
후진: x3=3/3=1, x2=(0+1.25⋅1)/1.25=1, x1=(8−3−1)/4=1. 해 x=(1,1,1).
SIMULATOR
분해 한 단계씩: PA = LU · 촐레스키 A = LLᵀ
분해
단계—
이번 단계 연산 수 · 누계—
piv (행 순서)—
‖PA − LU‖ (또는 ‖A − LLᵀ‖)—
해볼 것: ① 예제 12.2를 'PA = LU'로 한 단계씩 넘기며 피벗(초록 칸)과 새로 바뀐 칸(보라 바탕), 저장된 승수(보라 글씨)를 확인하세요. 1단계의 행 교환 때 이미 저장한 승수가 없으니 L은 그대로입니다. ② 'LU (교환 없음)'으로 같은 행렬을 분해하면 승수 4, 2가 1보다 큽니다. ③ SPD 행렬을 촐레스키로 분해하면 L의 대각이 모두 양수이고 연산 수가 LU의 약 절반입니다. '대칭 부정치'에서는 촐레스키가 음수의 제곱근에서 멈춥니다. 모델: 배정밀도, 표시는 소수 넷째 자리.
촐레스키 분해: 대칭 양정치 행렬
강성 행렬은 대칭이고 양정치다
스프링과 막대로 이루어진 구조물의 강성 행렬 K는 대칭이고, 변위 u=0이면 탄성 에너지 21uTKu가 양수입니다. 이 성질을 쓰면 피벗팅 없이도 안정하고, 연산과 메모리를 절반으로 줄일 수 있습니다.
정리 12.5 (촐레스키 분해) 실대칭 행렬 A가 양정치(positive definite)(모든 x=0에 대해 xTAx>0)일 필요충분조건은 대각 원소가 양수인 하삼각 행렬 L로 A=LLT라 쓸 수 있는 것이다. 이 L은 유일하며, 원소는 열 순서로
연산 수는 약 31n3(LU의 절반)과 제곱근 n개, 저장은 아래 삼각만 필요하다. 피벗팅 없이 후방 안정하다.
왜 피벗팅이 필요 없나.aij를 L의 i행·j행의 내적으로 보면 aii=∑kℓik2이므로 ∣ℓik∣≤aii. L의 원소가 A의 대각으로 묶이니 성장이 일어나지 않는다. 또한 제곱근 안이 음수나 0이 되면 A가 양정치가 아니라는 증거다. 그래서 촐레스키 분해는 양정치성의 가장 값싼 판정법으로도 쓰인다. ∎
대칭이지만 양정치가 아닌 행렬(구조물의 좌굴 해석, 제약 조건이 있는 최적화의 KKT 행렬)에는 대칭성을 살리면서 1×1과 2×2 블록 피벗을 쓰는 PAPT=LDLT 분해(번치–카우프만 피벗팅)를 씁니다. LinearBook 4장의 LDLT에 피벗팅을 더한 것입니다.
띠 행렬과 토머스 알고리즘
백만 개의 격자점을 1초 안에
1차원 열전도·확산 문제, 3차 스플라인(6장), 크랭크–니콜슨 시간 걸음(16장), 경계값 문제의 유한 차분(11장)은 모두 삼중대각계를 냅니다. n=106이면 꽉 찬 LU는 7×1017번의 연산이지만, 삼중대각 구조를 쓰면 8×106번입니다.
삼중대각계 aixi−1+bixi+cixi+1=di(a1=cn=0)에 피벗팅 없는 소거를 쓰면 L과 U도 두 줄짜리 띠로 남습니다. 행마다 위쪽 행을 한 번 빼서 정규화한 계수 ci′, di′를 만들고(전진 소거), 아래에서 위로 대입합니다.
function thomas(a, b, c, d) // 길이 n, a[1] = c[n] = 0
c'[1] ← c[1] / b[1]; d'[1] ← d[1] / b[1]
for i = 2 … n // 전진 소거: 행마다 상수 번
m ← b[i] − a[i]·c'[i−1] // 이번 행의 피벗
c'[i] ← c[i] / m
d'[i] ← (d[i] − a[i]·d'[i−1]) / m
x[n] ← d'[n]
for i = n−1 … 1 // 후진 대입
x[i] ← d'[i] − c'[i]·x[i+1]
return x // 총 약 8n 연산, 메모리 O(n)
정리 12.6 (토머스 알고리즘의 안정 조건) 행렬이 대각 우세(diagonally dominant)이면, 즉 모든 i에서 ∣bi∣≥∣ai∣+∣ci∣이고 적어도 한 행에서 부등호가 엄격하면(그리고 기약이면), 토머스 알고리즘의 모든 피벗 mi가 0이 아니고 ∣ci′∣≤1이다. 따라서 피벗팅 없이 안정하다. 대칭 양정치 삼중대각 행렬에서도 같은 결론이 성립한다.
귀납.∣ci−1′∣≤1이면 ∣mi∣≥∣bi∣−∣ai∣∣ci−1′∣≥∣bi∣−∣ai∣≥∣ci∣이므로 ∣ci′∣=∣ci∣/∣mi∣≤1. 엄격한 행에서 ∣mi∣>0이 보장된다. 그래서 오차가 후진 대입에서 ∣ci′∣배 이상 커지지 않는다. ∎
예제 12.4 (4×4 토머스)bi=2, ai=ci=−1, d=(1,0,0,1)(양 끝 온도가 1인 막대의 정상 상태).
i
피벗 mᵢ
c′ᵢ
d′ᵢ
xᵢ
1
2
−1/2
1/2
1
2
2 − 1/2 = 3/2
−2/3
(0 + 1/2)/(3/2) = 1/3
1
3
2 − 2/3 = 4/3
−3/4
(1/3)/(4/3) = 1/4
1
4
2 − 3/4 = 5/4
0
(1 + 1/4)/(5/4) = 1
1
피벗은 (i+1)/i로 1보다 크고, ∣ci′∣=i/(i+1)<1이다. 후진 대입: x4=1, x3=1/4+3/4=1, … 모두 1. 막대 전체가 1도가 된다(정상 상태 온도는 양 끝 사이의 직선).
반띠폭이 w인 띠 행렬로 일반화하면 연산 O(nw2), 저장 O(nw)입니다(LinearBook 정리 4.5). 부분 피벗팅을 쓰면 U의 위쪽 띠가 2w까지 넓어질 수 있으므로 LAPACK의 띠 풀이기(dgbsv)는 그만큼 여분의 자리를 마련합니다.
SIMULATOR
토머스 알고리즘 한 행씩, 그리고 실제 걸리는 시간
c′ᵢ(막대)와 해 xᵢ(점): 전진 소거 → 후진 대입
이 브라우저에서 잰 시간 (로그–로그)
c′ᵢxᵢ꽉 찬 LU 시간토머스 시간
단계—
피벗 mᵢ · c′ᵢ—
max |c′ᵢ|—
잰 기울기 LU · 토머스—
해볼 것: ① '다음 행'으로 전진 소거를 진행하면 c′ᵢ 막대가 하나씩 생기고, 끝에 닿으면 후진 대입이 오른쪽에서 왼쪽으로 xᵢ를 채웁니다. ② bᵢ = 2(대각 우세의 경계)에서 |c′ᵢ| = i/(i + 1)이 1에 다가가고, bᵢ를 키우면 빨리 수렴합니다. bᵢ < 2로 내리면 대각 우세가 깨지고 피벗이 0을 지나며 |c′ᵢ|가 커질 수 있습니다(bᵢ = 1.2 근처). ③ '시간 재기'는 꽉 찬 LU(n = 50 … 400)와 토머스(n = 10³ … 10⁶)를 실제로 돌려 기울기 3과 1을 측정합니다. 모델: n = 24 (단계 보기), 시간은 같은 n을 여러 번 반복한 평균.
노름, 조건수, 그리고 오차 한계
답이 몇 자리까지 맞는가
LU 분해로 얻은 해를 원래 식에 넣었더니 잔차 b−Ax^가 10−15입니다. 그럼 x^도 15자리까지 맞을까요? 그렇지 않을 수 있습니다. 잔차는 후방 오차를, 우리가 알고 싶은 것은 전방 오차를 재고, 두 오차 사이의 환율이 조건수입니다(2장).
정의 12.7 (벡터 노름과 유도 행렬 노름)∥x∥1=∑∣xi∣, ∥x∥2=∑xi2, ∥x∥∞=max∣xi∣. 각 벡터 노름이 유도하는 행렬 노름 ∥A∥=maxx=0∥Ax∥/∥x∥는
ATA=[10141420]의 고윳값의 제곱근이 σ1=5.4650, σ2=0.36597이므로 κ2=14.93. 노름에 따라 값은 다르지만 크기 정도(자릿수)는 같다. 모든 노름은 n에 의존하는 상수배 안에서 동등하다.
정리 12.8 (오차 한계와 잔차)Ax=b의 근사해 x^의 잔차를 r=b−Ax^라 하면
κ(A)1∥b∥∥r∥≤∥x∥∥x−x^∥≤κ(A)∥b∥∥r∥.
우변 섭동 δb에 대해 ∥δx∥/∥x∥≤κ∥δb∥/∥b∥, 행렬 섭동 δA에 대해(κ∥δA∥/∥A∥<1이면) ∥x∥∥δx∥≤1−κ∥δA∥/∥A∥κ∥δA∥/∥A∥.
증명의 핵심.x−x^=A−1r이므로 ∥x−x^∥≤∥A−1∥∥r∥, 한편 ∥b∥≤∥A∥∥x∥. 두 식을 곱하면 오른쪽 부등식. 왼쪽은 ∥r∥≤∥A∥∥x−x^∥, ∥x∥≤∥A−1∥∥b∥에서 나온다. ∎
정리 12.3과 합치면 실용적인 경험 법칙이 나옵니다. 후방 안정한 풀이기는 ∥δA∥/∥A∥≈u≈10−16인 문제를 정확히 푸는 셈이므로, 상대 오차 ≈ κ·u, 즉 대략 log10κ자리를 잃는다. κ = 10⁶이면 16자리 중 약 10자리가 남습니다. LAPACK은 κ를 직접 계산하지 않고(A−1이 필요하므로) LU 분해를 재사용해 O(n2)에 ∥A−1∥1을 추정합니다(dgecon).
작은 잔차 ≠ 작은 오차A=[1111.0001], b=(2,2.0001), 참해 (1,1), κ∞=4.0×104. 근사해 (2,0)은 잔차 (0,10−4)이지만 오차가 1(100%)이다. 근사해 (1.0001,1.0001)은 잔차 (−2×10−4,−2×10−4)로 두 배 크지만 오차는 10−4이다. 잔차로 해를 고르면 나쁜 쪽을 고른다.
SIMULATOR
두 직선의 교점: 조건수가 측정 오차를 키우는 모습
두 직선의 방향 손잡이를 끌기
직선 1직선 2흔들린 b의 해 (구름)상한 κ‖δb‖/‖b‖의 상자
κ∞(A)—
κ₂(A)—
관찰한 최대 증폭률—
잃는 자릿수 ≈ log₁₀ κ—
해볼 것: ① 두 직선을 거의 평행하게 돌리면 해의 구름이 직선 방향으로 길게 늘어나고 κ가 수백으로 커집니다. 관찰한 증폭률(‖δx‖/‖x‖ ÷ ‖δb‖/‖b‖)은 언제나 κ∞ 이하입니다. ② '척도 ×1000'은 직선은 그대로인데 한 식에 1000을 곱한 것입니다. 해의 구름은 변하지 않는 방향도 있는데 κ는 1000배 가까이 커집니다. 조건수는 행렬의 척도에 민감하고, 평형화가 필요한 이유입니다. 모델: A의 각 행은 직선의 법선 벡터, 참해 x = (1, 1), δb는 ∞-노름 상자 안의 균등 난수(시드 고정).
힐베르트 행렬: 깔끔한 분수, 사라지는 자릿수
Hij=1/(i+j−1)인 힐베르트 행렬(Hilbert matrix)은 구간 [0,1]에서 단항식 1,x,…,xn−1으로 최소제곱 근사를 할 때 나오는 그람 행렬입니다(7장). 단항식들이 [0,1]에서 서로 거의 같은 모양이라(높은 차수일수록 xk과 xk+1은 구별하기 어렵다) 열벡터들이 거의 일차 종속이 됩니다. 역행렬은 정수 원소를 가지는 공식이 알려져 있어 조건수를 정확히 계산할 수 있습니다.
n
4
6
8
10
11
12
13
κ∞(Hₙ)
2.8×10⁴
2.9×10⁷
3.4×10¹⁰
3.5×10¹³
1.2×10¹⁵
4.1×10¹⁶
1.3×10¹⁸
부분 피벗팅 해의 오차 ‖x̂ − 1‖∞
6.1×10⁻¹³
5.3×10⁻¹⁰
4.2×10⁻⁷
3.2×10⁻⁴
9.7×10⁻³
0.36
4.2
상대 잔차 ‖b − Hx̂‖∞/‖b‖∞
5×10⁻¹⁷
0
2×10⁻¹⁶
8×10⁻¹⁷
1.5×10⁻¹⁶
2.9×10⁻¹⁶
2.8×10⁻¹⁶
표가 정리 12.8을 그대로 보여 줍니다. 잔차는 n과 무관하게 반올림 수준(후방 안정)인데, 오차는 κ·u를 따라 커져 n = 12에서 한 자리도 남지 않습니다. 단, 표의 b=H1 자체도 반올림된 값이고 H의 원소(1/3 등)도 정확히 저장되지 않으므로, 이 오차의 상당 부분은 풀이기 이전에 이미 입력 단계에서 생긴 것입니다. 어떤 알고리즘도 되돌릴 수 없는 몫입니다.
SIMULATOR
힐베르트 행렬 κ의 증가와 잃는 자릿수
κ∞(Hₙ), 오차, 잔차, κ·u (로그 눈금)
계산한 해 x̂ᵢ (참해는 모두 1)
계산 정밀도
κ∞(Hₙ)‖x̂ − x‖∞/‖x‖∞상대 잔차κ·u
κ∞(Hₙ)—
상대 오차—
남은 자릿수 −log₁₀(오차)—
예측: 16 − log₁₀ κ (단정밀도 7.2 − …)—
해볼 것: ① n을 하나 늘릴 때마다 κ가 약 30배(1.5자리) 커지고 오차 곡선이 κ·u 선을 따라 올라가는 것을 보세요. ② 잔차(초록)는 끝까지 바닥에 붙어 있습니다. 후방 안정성은 지켜지지만 답은 틀립니다. ③ 단정밀도로 바꾸면 n = 6에서 이미 자릿수가 거의 남지 않습니다. 모델: b = Hₙ·1을 같은 정밀도로 계산, 부분 피벗팅 LU, κ∞는 정수 원소 역행렬 공식으로 정확히 계산.
반복 개선: 싼 분해로 비싼 정확도를
GPU는 단정밀도가 몇 배 빠르다
많은 가속기에서 단정밀도(유효 숫자 약 7자리) 연산이 배정밀도보다 몇 배 빠르고 메모리도 절반입니다. 그런데 공학 계산은 배정밀도의 답을 원합니다. 32n3의 분해는 싸게 단정밀도로 하고, 답은 배정밀도로 받을 수 있을까요?
근사해 x^의 오차 e=x−x^는 정확히 Ae=r을 만족합니다. 이 보정 방정식을 이미 있는 (부정확한) LU로 풀어 더하면, 보정값의 상대 오차가 처음 해와 같은 수준이어도 보정 자체가 작으므로 해는 한 단계 더 정확해집니다. 핵심은 잔차 r=b−Ax^를 높은 정밀도로 계산하는 것입니다. 거의 같은 두 수의 뺄셈이라 여기서 정밀도를 아끼면 자릿수 상쇄(1장)로 정보가 사라집니다.
function mixed_refine(A, b)
(L, U, P) ← lu(single(A)) // O(n³): 단정밀도, 싸다
x ← solve_single(L, U, P, b) // O(n²)repeat
r ← b − A·x // O(n²): 배정밀도 (원래 A로)
d ← solve_single(L, U, P, r) // 같은 분해 재사용
x ← x + d // 배정밀도로 더하기until ‖d‖ ≤ 작은 수·‖x‖
return x
정리 12.9 (반복 개선의 수렴) 분해의 정밀도를 uf, 잔차와 갱신의 정밀도를 u라 하자. 대략 κ(A)uf<1이면 반복마다 오차가 κuf 정도의 비율로 줄어, 몇 번 만에 ∥x−x^∥/∥x∥≈κ(A)u(배정밀도 풀이기와 같은 수준)에 도달한다. κuf≥1이면 수렴이 보장되지 않는다.
n = 40, 단정밀도 LU + 배정밀도 잔차
처음
1회
2회
3회
6회
κ = 10²
2.5×10⁻⁶
1.1×10⁻¹²
6.8×10⁻¹⁵
4.9×10⁻¹⁵
6.3×10⁻¹⁵
κ = 10⁴
6.8×10⁻⁵
8.3×10⁻¹⁰
3.6×10⁻¹³
4.2×10⁻¹³
4.0×10⁻¹³
κ = 10⁶
3.7×10⁻³
2.1×10⁻⁶
5.7×10⁻⁹
6.8×10⁻¹¹
3.4×10⁻¹¹
κ = 10⁸
2.2×10⁻¹
1.2×10⁻²
1.0×10⁻³
9.5×10⁻⁵
8.5×10⁻⁸
표는 특이값이 1부터 1/κ까지 등비로 퍼진 행렬로 만든 실험입니다(상대 오차, ∞-노름). κ = 10⁴에서는 단 두 번 만에 오차가 6.8×10−5에서 배정밀도 바닥 κu≈10−12 근처로 떨어집니다. 단정밀도의 uf≈6×10−8이므로 κ = 10⁸은 κuf≈6으로 정리의 조건 밖입니다. 이 실험에서는 반복마다 약 한 자리씩 느리게 줄었지만, 다른 행렬에서는 정체하거나 발산할 수 있습니다. 최근의 혼합 정밀도 풀이기는 보정 방정식을 GMRES(13장)로 풀어 이 한계를 넓힙니다.
SIMULATOR
혼합 정밀도 반복 개선: 단정밀도 LU에서 배정밀도 답까지
잔차 계산 정밀도
상대 오차상대 잔차κ·u₆₄, κ·u₃₂
반복—
상대 오차—
줄어든 비율 (이번 / 직전)—
κ·u₃₂—
해볼 것: ① κ = 10⁴에서 '개선 한 번'을 두세 번 누르면 오차가 단정밀도 수준(κ·u₃₂ 선 부근)에서 배정밀도 바닥(κ·u₆₄)까지 내려옵니다. ② 잔차를 단정밀도로 계산하면 오차가 κ·u₃₂ 근처에서 멈춥니다. 잔차의 정밀도가 최종 정확도를 정합니다. ③ κ를 10⁸ 이상으로 올리면 수렴이 느려지거나 멈춥니다(κ·u₃₂ > 1). 모델: A = U Σ Vᵀ, U·V는 하우스홀더 반사 3개의 곱(시드 고정), 특이값은 1 … 1/κ 등비. 단정밀도는 Math.fround로 매 연산을 반올림.
희소 행렬과 채움, 재배열
400개짜리 격자, 저장은 어디까지 커지나
20×20 격자의 푸아송 방정식(16장)은 미지수 400개, 행마다 0이 아닌 원소가 많아야 5개입니다. 대칭이니 아래 삼각만 세면 0 아닌 칸이 1,160개입니다. 촐레스키 분해의 L은 몇 칸을 차지할까요? 꽉 찬 하삼각이면 80,200칸입니다.
희소 행렬은 0 아닌 원소만 저장합니다. 가장 흔한 형식 CSR(compressed sparse row)은 값 배열, 열 번호 배열, 각 행의 시작 위치 배열 세 개로 행렬을 담아 저장량이 원소 수 nnz에 비례합니다. 문제는 분해입니다. 소거 중 aij←aij−ℓikukj에서 aij=0이어도 ℓik와 ukj가 둘 다 0이 아니면 새 원소가 생깁니다.
정의 12.10 (채움과 소거 그래프) 분해 L(또는 L+U)에는 0이 아닌데 A에서는 0인 원소를 채움(fill-in)이라 한다. 대칭 행렬을 그래프(정점 = 미지수, 변 = 0 아닌 aij)로 보면, 정점 k를 소거하는 것은 k의 아직 소거되지 않은 이웃들을 모두 서로 잇는 것(클리크 만들기)과 같다. 새로 생긴 변이 채움이다. 채움의 양은 소거 순서, 곧 행렬의 재배열(reordering)PAPT에 따라 크게 달라진다.
그림 12-1. 소거 그래프. 정점 k를 소거하면 k와 이어져 있던 정점들이 모두 서로 이어진다. 차수가 큰 정점을 일찍 소거하면 큰 클리크가 생기고, 차수가 작은 정점부터 소거하면 채움이 적다. 이것이 최소 차수 재배열의 아이디어다.
대표적인 재배열은 셋입니다. 역 커트힐–맥키(reverse Cuthill–McKee, RCM)는 너비 우선 탐색으로 번호를 매겨 띠폭을 줄입니다. 최소 차수(minimum degree)는 매 단계 소거 그래프에서 차수가 가장 작은 정점을 고릅니다(실무에서는 근사 최소 차수 AMD). 중첩 분할(nested dissection)은 격자를 분리자(separator) 한 줄로 반씩 나누고, 분리자를 맨 마지막에 번호 매기는 일을 재귀적으로 반복합니다. 2차원 k×k 격자에서 중첩 분할의 채움은 O(nlogn), 연산은 O(n3/2)(n=k2)로 점근적으로 최적이라는 것이 알려져 있습니다.
k × k 격자 푸아송, nnz(L) (대각 포함)
k = 10 (n = 100)
k = 20 (n = 400)
k = 30 (n = 900)
A의 아래 삼각
280
1,160
2,640
자연 순서 (행 단위 번호)
1,009
8,019
27,029
RCM
805
5,910
19,315
중첩 분할 (단순 구현)
781
4,867
13,874
최소 차수 (탐욕)
656
3,729
10,351
꽉 찬 하삼각 n(n+1)/2
5,050
80,200
405,450
표는 소거 그래프로 기호적 채움을 센 값입니다(숫자 상쇄는 무시). 문제 상자의 답은 자연 순서 8,019칸, 최소 차수 3,729칸입니다. 이 크기에서는 탐욕적 최소 차수가 가장 적지만, 격자가 커질수록 중첩 분할의 점근적 우위가 드러나고 병렬화에도 유리해 대규모 FEM 풀이기(MUMPS, PARDISO 등)는 두 방법을 섞어 씁니다. 채움을 아무리 줄여도 3차원 문제에서는 L이 너무 커지는 경우가 많아, 그때는 반복 해법(13장)으로 넘어갑니다.
SIMULATOR
희소 채움 시각화: 같은 격자, 다른 번호 매기기
왼쪽: PAPᵀ의 0 아닌 칸 · 오른쪽: L (빨강 = 채움)
격자 위의 번호 순서 (밝을수록 나중)
재배열
n · nnz(A 아래 삼각)—
nnz(L) · 채움—
촐레스키 연산 수 ∑ (열 길이)²—
꽉 찬 경우 대비—
해볼 것: ① '자연' 순서에서 채움(빨강)이 띠 안을 가득 채우는 것을 보세요. 띠폭이 k입니다. ② 'RCM'은 띠를 조금 좁히고, '중첩 분할'은 분리자(가운데 줄)를 마지막에 두어 오른쪽 아래 블록에 채움을 모읍니다. ③ '최소 차수'는 구조가 무질서해 보이지만 채움이 가장 적습니다. '소거 진행'을 움직이면 소거 그래프에서 채움이 생기는 순서를 따라갈 수 있습니다. 모델: 5점 스텐실 격자 라플라시안, 기호적 촐레스키(숫자 상쇄 무시), 연산 수는 열마다 (그 열의 0 아닌 원소 수)²의 합.