지금까지 이 책의 계산은 모두 "정확한 실수"로 했습니다. 하지만 실제로 행렬을 푸는 것은 컴퓨터이고, 컴퓨터의 실수는 유효 숫자 약 16자리에서 잘린 부동소수점 수입니다. 미지수가 수백만 개인 행렬에서는 소거의 n3 연산조차 너무 비쌉니다. 그래서 두 가지 질문이 남습니다. 틀린 숫자로 계산해도 답을 믿을 수 있는가? 그리고 정확한 분해 대신 근사를 되풀이해 더 빨리 풀 수 있는가? 첫 질문의 답은 문제의 예민함을 재는 조건수와 알고리즘의 정직함을 재는 안정성이고, 둘째 질문의 답은 거듭제곱법·QR 알고리즘·반복법·켤레 기울기법입니다. 이 장은 선형대수가 실제 컴퓨터 안에서 돌아가는 방식을 다룹니다.
부동소수점 모델 fl(x)=x(1+δ), ∣δ∣≤u와 기계 엡실론 2−52을 이해하고, 상쇄 오차가 생기는 식을 안정한 식으로 바꾼다.
유도 행렬 노름과 조건수 κ(A)=∥A∥∥A−1∥를 정의하고, 섭동 한계 ∥δx∥/∥x∥≤κ∥δb∥/∥b∥를 증명한다.
거듭제곱법이 ∣λ2/λ1∣k로 수렴함을 보이고, 이동 역반복과 레일리 몫 반복으로 원하는 고유값을 빠르게 찾는다.
QR 알고리즘 Ak+1=RkQk가 닮음 변환이며 상삼각 꼴로 수렴하는 이유를 설명한다.
야코비·가우스–자이델·SOR의 수렴을 반복 행렬의 스펙트럼 반지름으로 판정하고, 켤레 기울기법이 SPD 행렬에서 최대 n단계에 끝나는 이유와 전처리의 역할을 안다.
희소 행렬을 CSR로 저장하고, 문제 크기와 구조에 따라 직접법과 반복법을 고른다.
이 수식이 없었다면슈퍼컴퓨터 순위표
세계에서 가장 빠른 컴퓨터를 뽑는 TOP500 목록은 1993년부터 해마다 두 번 발표됩니다. 순위를 정하는 시험은 놀랍게도 단 하나, 거대한 밀집 연립방정식 Ax=b를 부분 피벗팅 LU 분해로 푸는 데 걸린 시간입니다(HPL, High-Performance LINPACK). 연산량이 32n3으로 정확히 알려져 있으니 걸린 시간으로 나누면 초당 부동소수점 연산 수(flop/s)가 나옵니다.
그런데 이 시험에는 조건이 하나 더 붙습니다. 답이 맞아야 합니다. 정확히는 잔차 ∥Ax^−b∥가 행렬 크기·기계 엡실론으로 정한 한계보다 작아야 결과를 인정합니다. 수십억 번의 반올림을 거친 답을 어떻게 "맞다"고 판정할까요? 그 근거가 이 장의 후방 안정성과 조건수입니다. 그리고 행렬이 수십억 차원으로 커지면 n3도 감당할 수 없어서, 실제 과학 계산은 행렬을 곱하기만 하는 반복법으로 넘어갑니다.
대부분의 프로그래밍 언어에서 0.1 + 0.1 + … + 0.1(열 번)을 계산하면 0.9999999999999999가 나옵니다. 0.1은 2진수로 끝나지 않는 무한소수라 가장 가까운 2진 분수로 잘리기 때문입니다. 오차는 10−16 정도로 작지만, 소거는 이런 연산을 n3번 합니다. 오차가 쌓여서 답을 망칠까요, 아니면 서로 상쇄될까요? 답하려면 먼저 "잘림"을 수학으로 적어야 합니다.
컴퓨터의 실수는 연속된 직선이 아니라 유한한 점들의 격자입니다. IEEE 754 배정밀도(double)는 수를 ±(1.f1f2⋯f52)2×2e로 저장합니다. 가수부 52비트, 지수 11비트, 부호 1비트, 모두 64비트입니다. 지수가 같은 구간 [2e,2e+1) 안에서 점들은 같은 간격2e−52으로 놓이고, 구간이 두 배 커질 때마다 간격도 두 배가 됩니다. 그래서 절대 간격은 수의 크기에 따라 달라지지만 상대 간격은 거의 일정합니다.
그림 16-1. 부동소수점 수는 2의 거듭제곱 구간마다 같은 개수로 놓인다. 실수 x는 가장 가까운 점 fl(x)로 반올림되고, 그 상대 오차는 구간에 관계없이 간격의 절반 / 구간 시작점 이하, 즉 단위 반올림 u 이하다. 실제 double은 구간마다 점이 2⁵²개다.
정의 16.1 (기계 엡실론과 단위 반올림) 1과 그다음 부동소수점 수 사이의 간격을 기계 엡실론(machine epsilon)εmach이라 한다. 배정밀도에서 εmach=2−52≈2.22×10−16이다. 가장 가까운 수로 반올림할 때 생기는 최대 상대 오차 u=εmach/2=2−53≈1.11×10−16를 단위 반올림(unit roundoff)이라 한다.
정리 16.1 (부동소수점 산술의 표준 모델) 넘침·바닥 넘침이 없으면, 정규화된 실수 x와 부동소수점 수 a,b, 연산 ∘∈{+,−,×,÷}에 대해
fl(x)=x(1+δ),fl(a∘b)=(a∘b)(1+δ),∣δ∣≤u.
IEEE 754는 사칙연산과 제곱근이 "정확한 결과를 구한 뒤 한 번 반올림한 것"과 같도록 규정하므로 이 모델이 성립한다. 즉 연산 하나하나는 상대 오차 u 이내로 정확하다.
연산 하나는 이렇게 정확한데 왜 계산이 틀릴까요? 범인은 대부분 상쇄 오차(catastrophic cancellation)입니다. 거의 같은 두 수 a≈b를 빼면, 뺄셈 자체는 정확해도 a와 b에 이미 들어 있던 반올림 오차가 결과의 크기에 비해 엄청나게 커집니다. 예를 들어 x=10−8일 때 cosx=1−5×10−17인데 double은 이 값을 1과 구별하지 못하므로 1−cosx는 0이 됩니다. 정답은 5×10−17인데 말이죠.
해결책은 대수적으로 같지만 뺄셈이 없는 식으로 바꾸는 것입니다. 1−cosx=2sin2(x/2), 1+x−1=x/(1+x+1), 그리고 ex−1은 라이브러리의 expm1이 정확히 계산합니다. 아래 시뮬레이터에서 x를 0으로 보내며 두 식을 비교해 보세요.
SIMULATOR
상쇄 오차: 같은 식, 다른 정확도
식
그대로 계산—
고친 식—
그대로 계산의 상대 오차—
맞는 자릿수—
그대로 계산한 식의 상대 오차고친 식(1e−17 바닥에 붙으면 오차 없음)예측 u/|a−b| 비율
해볼 것: ① (1−cos x)/x²에서 k를 −8 아래로 내리면 결과가 0이 되어 오차가 100%가 됩니다. ② 세 식 모두 오차 곡선이 예측선(빼는 두 수 크기 ÷ 결과 크기 × u)을 따라 올라갑니다. 상쇄 오차는 "얼마나 많이 지워졌나"에 비례합니다. ③ 고친 식은 어느 x에서나 거의 기계 정밀도입니다. 모델: 브라우저의 JavaScript Number(IEEE 754 배정밀도)로 실제 계산한 값이며, 참값은 고친 식으로 구했습니다.
예제 16.1 · 이차방정식의 근x2−108x+1=0의 작은 근을 근의 공식 x=(108−1016−4)/2로 구하면, 거의 같은 두 수를 빼서 double에서 7.45×10−9처럼 자릿수가 거의 남지 않은 값이 나옵니다(참값 ≈1.000×10−8). 대신 큰 근 x1=(108+1016−4)/2≈108을 먼저 구하고 근과 계수의 관계 x1x2=1로 x2=1/x1을 구하면 뺄셈 없이 유효 숫자 16자리를 모두 얻습니다. 같은 수학, 다른 알고리즘입니다.
더 자세한 부동소수점 이야기(0.1의 2진 표현, 반올림 오차가 쌓여 생긴 실제 사고)는 MathBook "컴퓨터는 어떻게 틀리는가"에 있습니다. 이 장의 관심은 행렬입니다. 연산 하나의 오차가 u라면, n×n 행렬을 다루는 알고리즘 전체의 오차는 얼마일까요?
조건수: 문제 자체가 얼마나 예민한가
측정값이 0.01% 틀리면 답은 몇 % 틀릴까
센서 두 개로 측정한 값 b로 Ax=b를 풀어 위치를 구한다고 합시다. 측정에는 언제나 작은 오차 δb가 섞입니다. 두 센서가 거의 같은 방향을 본다면(두 직선이 거의 평행하다면) 교점은 측정 오차에 매우 민감할 것입니다. 이 민감도를 숫자 하나로 잴 수 있을까요? 그 숫자는 알고리즘이 아니라 행렬 A 자체의 성질이어야 합니다. 어떤 알고리즘으로 풀든 입력이 흔들리면 답도 흔들리니까요.
벡터의 크기는 1장의 노름으로 잽니다. 행렬의 크기는 "벡터를 최대 몇 배까지 늘이는가"로 재는 것이 자연스럽습니다.
정의 16.2 (유도 행렬 노름) 벡터 노름 ∥⋅∥에 대해 행렬 A의 유도 노름(induced norm)은
∥A∥=x=0max∥x∥∥Ax∥.
정의에서 곧바로 ∥Ax∥≤∥A∥∥x∥, ∥AB∥≤∥A∥∥B∥가 나온다. 2-노름에서는 ∥A∥2=σ1(가장 큰 특이값, 14장), ∞-노름에서는 ∥A∥∞=maxi∑j∣aij∣(행 절댓값 합의 최댓값)이다.
정의 16.3 (조건수) 가역 행렬 A의 조건수(condition number)는 κ(A)=∥A∥∥A−1∥이다. 언제나 κ(A)≥∥AA−1∥=1이고, 2-노름에서는 κ2(A)=σ1/σn, 즉 가장 많이 늘이는 방향과 가장 적게 늘이는 방향의 비다. 특이 행렬은 κ=∞로 둔다.
정리 16.2 (우변 섭동의 한계)Ax=b, A(x+δx)=b+δb, b=0이면
∥x∥∥δx∥≤κ(A)∥b∥∥δb∥.
또한 행렬 자체가 A+δA로 흔들리고 κ(A)∥δA∥/∥A∥<1이면 ∥x+δx∥∥δx∥≤κ(A)∥A∥∥δA∥가 성립한다.
증명. 두 식을 빼면 Aδx=δb, 즉 δx=A−1δb이므로 ∥δx∥≤∥A−1∥∥δb∥. 한편 ∥b∥=∥Ax∥≤∥A∥∥x∥이므로 1/∥x∥≤∥A∥/∥b∥. 두 부등식을 곱하면 결론이다. 행렬 섭동의 경우는 (A+δA)(x+δx)=b에서 Aδx=−δA(x+δx)로 같은 논리를 쓴다. ∎
등호가 성립하는 경우도 있습니다. x가 A가 가장 많이 늘이는 방향이고 δb가 가장 적게 늘이는 방향일 때입니다. 그러니 κ는 과장된 상한이 아니라 실제로 일어날 수 있는 최악의 증폭률입니다. 기하로 보면, 우변 오차가 반지름 r인 원 위에 있을 때 해의 오차는 A−1이 그 원을 보낸 타원 위에 있고, 타원의 긴 축이 r/σn, 짧은 축이 r/σ1입니다.
SIMULATOR
거의 평행한 두 직선: 측정 오차가 해를 흔든다
원: δb의 모든 방향 · 타원: 해가 갈 수 있는 곳
κ₂(A) = σ₁/σ₂—
‖δx‖/‖x‖ (현재 방향)—
상한 κ·‖δb‖/‖b‖—
증폭률—
해볼 것: ① d를 줄이면 두 직선이 거의 겹치고 타원이 직선 방향으로 길게 늘어납니다. d = 0.01이면 κ ≈ 400입니다. ② φ를 돌려 증폭률이 최대가 되는 방향(타원의 긴 축)을 찾아보세요. 그때 상대 오차가 상한과 거의 같아집니다. ③ d = 1이면 κ ≈ 2.6이라 측정 오차가 거의 그대로 전달됩니다. 모델: 참해 x = (1, 1), b = Ax. 2-노름을 씁니다.
조건수가 큰 행렬의 고전적인 예는 힐베르트 행렬(Hilbert matrix)Hij=1/(i+j−1)입니다. 구간 [0, 1]에서 다항식 1,x,x2,…로 함수를 최소제곱 근사할 때 나오는 행렬이고(Hij=∫01xi−1xj−1dx), 모든 원소가 깔끔한 분수인데도 n이 커질수록 조건수가 대략 e3.5n꼴로 폭발합니다. 아래에서 참해가 x=(1,1,…,1)이 되도록 b=H1을 만들고 부분 피벗팅 소거로 풀어 봅니다.
SIMULATOR
힐베르트 행렬이 잃는 자릿수
κ∞(Hₙ)—
해의 상대 오차—
맞는 자릿수 (측정)—
어림 16 − log₁₀κ—
—
해볼 것: ① n = 8이면 κ ≈ 3×10¹⁰이라 16자리 중 6~7자리만 맞습니다. ② n = 12를 넘기면 κ가 10¹⁶에 다가가 해의 성분이 1과 전혀 다른 값이 됩니다. ③ 그런데도 잔차(초록)는 끝까지 10⁻¹⁶ 근처입니다. 소거는 "거의 같은 행렬의 정확한 답"을 냈고, 틀린 것은 문제의 예민함 때문입니다(다음 절). 모델: κ∞는 힐베르트 역행렬의 정수 공식으로 계산하고, 해는 double로 부분 피벗팅 소거를 해서 구했습니다.
예제 16.2 · 조건수를 손으로A=[1111.0001]이면 detA=10−4, A−1=104[1.0001−1−11]. ∞-노름으로 ∥A∥∞=2.0001, ∥A−1∥∞=104×2.0001이므로 κ∞≈4.0×104. 우변이 10−4만큼 흔들리면 해는 최대 4배, 즉 100% 넘게 흔들릴 수 있습니다. 실제로 b=(2,2.0001)이면 x=(1,1)이지만, b=(2,2.0002)로 바꾸면 x=(0,2)입니다.
행렬식은 조건수가 아니다detA가 작다고 해서 조건이 나쁜 것은 아닙니다. A=0.1I20의 행렬식은 10−20이지만 κ=1로 완벽한 조건입니다. 행렬식은 크기 배율(스케일)에 민감하고, 조건수는 모양(늘이는 비율의 차이)에 민감합니다. 행렬이 특이에 얼마나 가까운지는 σn/σ1=1/κ2가 알려 줍니다.
안정성: 알고리즘이 문제를 망치지 않는가
틀린 답의 책임은 누구에게 있나
힐베르트 행렬의 해는 크게 틀렸습니다. 소거 알고리즘이 나쁜 걸까요, 문제가 나쁜 걸까요? 이 둘을 분리해야 "더 좋은 알고리즘을 찾아야 할지", "문제를 다시 세워야 할지" 결정할 수 있습니다.
답 x^의 오차를 재는 방법은 두 가지입니다. 전방 오차(forward error)는 답이 참값에서 얼마나 떨어졌나 ∥x^−x∥/∥x∥입니다. 후방 오차(backward error)는 거꾸로 묻습니다. "x^가 정확한 답이 되려면 문제를 얼마나 바꿔야 하나?" 즉 (A+δA)x^=b를 만족하는 가장 작은 ∥δA∥/∥A∥입니다.
그림 16-2. 컴퓨터가 낸 답 x̂는 조금 바뀐 문제 (A+δA, b)의 정확한 답이다. 문제를 바꾼 크기가 후방 오차, 답이 빗나간 크기가 전방 오차다. 후방 오차를 전방 오차로 옮기는 증폭률이 조건수다.
정의 16.4 (후방 안정성) 알고리즘이 내놓은 답이 언제나 상대 크기 O(u)만큼만 바뀐 입력의 정확한 답이면 그 알고리즘을 후방 안정(backward stable)하다고 한다.
정리 16.3 (부분 피벗팅 소거의 후방 안정성, 윌킨슨) 부분 피벗팅을 쓴 가우스 소거(PA = LU)와 삼각 대입으로 구한 x^는
(A+δA)x^=b,∥A∥∞∥δA∥∞≤cn3ρu
를 만족한다. 여기서 c는 작은 상수, ρ는 소거 중 원소가 커지는 정도인 성장 인자(growth factor)다. 실제 계산에서 n에 대한 의존은 이 상한보다 훨씬 약하게 나타나고, 부분 피벗팅을 쓰면 실제 응용의 행렬에서 ρ가 작게 유지되므로 사실상 후방 안정하다. 피벗팅을 하지 않으면 ρ가 무한히 커질 수 있다(3장의 작은 피벗 예).
두 정리를 합치면 실무의 어림셈이 나옵니다. 후방 안정한 알고리즘의 후방 오차는 u≈10−16 정도이므로 정리 16.2에 의해
∥x∥∥x^−x∥≲κ(A)⋅u⟹맞는자릿수≈16−log10κ(A).
힐베르트 시뮬레이터의 "어림" 값이 바로 이것이다. 조건수 10ᵏ마다 유효 숫자 k자리를 잃는다. 알고리즘이 아무리 좋아도 이보다 나을 수는 없고, 후방 안정한 알고리즘은 이 한계에 거의 닿는다.
잔차가 작다고 답이 맞는 것은 아니다 잔차 r=b−Ax^는 계산하기 쉬워서 자주 확인합니다. ∥r∥/(∥A∥∥x^∥)는 사실상 후방 오차이므로, 잔차가 작다는 것은 "알고리즘이 정직했다"는 증거일 뿐입니다. 전방 오차는 여전히 κ배까지 클 수 있습니다. 힐베르트 시뮬레이터에서 잔차는 10⁻¹⁶인데 해가 완전히 틀린 것이 그 예입니다. TOP500의 HPL 검사도 잔차(후방 오차)를 확인합니다.
그렇다면 역행렬은?A−1을 구해 x=A−1b로 곱하는 방법은 LU로 푸는 것보다 약 세 배 비싸고, 후방 안정하지도 않습니다. 구한 역행렬 자체의 오차는 κ·u 수준이지만 A−1^b의 잔차는 κ에 비례해 커질 수 있기 때문입니다. "역행렬을 구하지 말고 분해해서 풀어라"(4장)는 속도뿐 아니라 정확도의 원칙이기도 합니다.
거듭제곱법: 곱하기만 해서 고유벡터 찾기
특성 다항식은 쓸 수 없다
교과서에서는 고유값을 det(A−λI)=0의 근으로 구합니다(11장). 그런데 5차 이상 다항식의 근에는 일반적인 근의 공식이 없고(아벨–루피니 정리), 계수에서 근을 구하는 문제는 조건이 아주 나쁩니다. 1000×1000 행렬의 특성 다항식을 만드는 것 자체가 수치적으로 의미가 없습니다. 고유값은 반복으로만 구할 수 있습니다. 가장 단순한 반복은 무엇일까요?
아무 벡터 x0에 A를 계속 곱해 봅시다. A가 고유벡터 v1,…,vn으로 대각화되고 ∣λ1∣>∣λ2∣≥⋯라 하면, x0=c1v1+⋯+cnvn에서
괄호 안의 둘째 항부터는 ∣λi/λ1∣k→0으로 사라진다. 길이가 넘치거나 사라지지 않도록 매번 정규화하면 방향만 v1로 모인다.
// 거듭제곱법 (power iteration) x ← 임의의 단위 벡터 반복: y ← A x; x ← y / ‖y‖; λ̂ ← xᵀ A x // 레일리 몫
정리 16.4 (거듭제곱법의 수렴) A가 대각화 가능하고 ∣λ1∣>∣λ2∣, 시작 벡터가 c1=0이면, 정규화한 반복 벡터와 ±v1 사이의 각은 O(∣λ2/λ1∣k)로 줄어든다. A가 대칭이면 레일리 몫(Rayleigh quotient)r(x)=xTAx/xTx로 구한 고유값 근사의 오차는 그 제곱 O(∣λ2/λ1∣2k)로 줄어든다.
증명 아이디어. 위 전개식에서 v1 이외 성분과 v1 성분의 비가 ∣λ2/λ1∣k 배율로 준다. 대칭이면 고유벡터가 직교해서 x=v1+ϵ일 때 r(x)−λ1=O(∥ϵ∥2)이다(레일리 몫은 고유벡터에서 정류점). ∎
수렴 속도가 ∣λ2/λ1∣로 정해진다는 것이 약점입니다. 두 고유값이 가까우면 한없이 느립니다. 해결책은 A를 바꿔서 비율을 좋게 만드는 것입니다. (A−σI)−1의 고유값은 1/(λi−σ)이므로, 이동량 σ를 원하는 고유값 근처에 두면 그 고유값이 압도적으로 커집니다. 이것이 이동 역반복(shifted inverse iteration)이고, 매 단계 곱셈 대신 (A−σI)y=x를 풉니다(LU는 한 번만 분해하면 됩니다). 수렴비는 ∣λj−σ∣/∣λk−σ∣(가장 가까운 것 ÷ 두 번째로 가까운 것)입니다.
한 걸음 더 나가면, 매 단계 σ를 현재의 레일리 몫으로 바꿀 수 있습니다. 이것이 레일리 몫 반복(Rayleigh quotient iteration)이고, 대칭 행렬에서는 대개 3차 수렴합니다. 맞는 자릿수가 매 단계 세 배씩 늘어 서너 번이면 기계 정밀도에 닿습니다. 대신 매번 새 행렬을 분해해야 하고, 어느 고유값으로 갈지는 시작점에 달려 있습니다.
SIMULATOR
거듭제곱법 · 역반복 · 레일리 몫 반복
보라 점(시작 벡터)을 끌어 보세요
방법
반복 k0
레일리 몫 λ̂—
고유벡터와의 각 sin θ—
이론 수렴비—
log₁₀ sin θ (단계별)
해볼 것: ① 거듭제곱에서 λ₂/λ₁ = 0.95로 두면 수십 단계가 지나도 수렴이 느립니다. 오차 막대가 점선(이론 기울기)을 따라 내려갑니다. ② λ₂/λ₁을 음수로 하면 반복 벡터가 좌우로 튀면서 수렴합니다. ③ 이동 역반복에서 σ를 λ₂ 근처에 두면 이번에는 작은 고유값의 고유벡터로 빠르게 갑니다. ④ 레일리 몫 반복은 서너 단계 만에 기계 정밀도(10⁻¹⁵)에 닿습니다. 모델: A = R diag(λ₁, λ₂) Rᵀ, 고유벡터는 30°와 120° 방향(대칭 행렬).
각 단계 (1,−1) 성분 대 (1,1) 성분의 비가 1, 1/3, 1/9로 ∣λ2/λ1∣=1/3씩 줄고, 고유값 오차는 0.2, 0.024로 약 1/9씩 준다(대칭이라 제곱 수렴).
거듭제곱법은 단순하지만 아주 큰 행렬에서 여전히 쓰입니다. 곱셈만 하면 되므로 행렬을 저장하지 않고 "곱하는 방법"만 알아도 됩니다. 웹 페이지의 링크 구조에서 중요도를 매긴 초기 페이지랭크가 대표적인 예로, 전이 행렬의 가장 큰 고유값(=1)의 고유벡터를 거듭제곱법으로 구했습니다(12장).
QR 알고리즘: 모든 고유값을 한꺼번에
고유값을 전부 구하는 표준 방법
거듭제곱법은 고유값 하나씩 찾습니다. 행렬의 고유값을 모두 원한다면? MATLAB의 eig, NumPy의 numpy.linalg.eig는 LAPACK을 거쳐 거의 언제나 같은 알고리즘을 부릅니다. 1961년 무렵 영국의 프랜시스와 소련의 쿠블라놉스카야가 각각 독립적으로 발표한 QR 알고리즘(QR algorithm)입니다.
Rk=QkTAk이므로 Ak+1=QkTAkQk. 직교 닮음 변환이라 고유값이 그대로 보존되고, 반올림 오차도 커지지 않는다.
정리 16.5 (QR 알고리즘의 수렴) A의 고유값이 ∣λ1∣>∣λ2∣>⋯>∣λn∣>0으로 절댓값이 모두 다르면(그리고 일반적인 위치 조건을 만족하면), Ak는 대각에 λ1,…,λn이 차례로 놓인 상삼각 행렬로 수렴한다. 대각 아래 (i+1,i) 원소는 대략 ∣λi+1/λi∣k의 비율로 0에 간다. A가 대칭이면 극한은 대각 행렬이다.
증명 아이디어.Q0Q1⋯Qk−1의 열들은 Ak의 열에 그람–슈미트를 한 결과와 같다. 즉 QR 알고리즘은 n개의 벡터에 동시에 거듭제곱법을 하면서 서로 직교하게 유지하는(동시 반복) 방법이다. 첫 열은 v1로, 둘째 열은 그것에 수직인 성분 중 v2 쪽으로 … 모인다. ∎
SIMULATOR
QR 알고리즘: Aₖ₊₁ = RₖQₖ 를 되풀이하면
A₀
반복 k0
대각 원소—
참 고유값—
|a₂₁| (기대 비 |λ₂/λ₁|)|a₃₂| (기대 비 |λ₃/λ₂|)
해볼 것: ① 일반 행렬에서 10단계를 누르면 대각 아래가 사라지고 대각에 5, 3, 1이 나타납니다. a₃₂(비 1/3)가 a₂₁(비 3/5)보다 빨리 줄어듭니다. ② 가까운 고유값에서는 a₂₁의 비가 2.8/3 ≈ 0.93이라 아주 느립니다. 실제 라이브러리가 이동(shift)을 쓰는 이유입니다. ③ 복소 고유값 쌍은 실수 행렬로 대각에 놓을 수 없어 위쪽 2×2 블록이 끝까지 돌기만 합니다. 모델: 이동 없는 QR 알고리즘, QR 분해는 수정 그람–슈미트.
실제 라이브러리의 QR 알고리즘은 세 가지를 더합니다. ① 먼저 직교 닮음 변환으로 A를 헤센베르크 꼴(대각 바로 아래 한 줄 밑은 모두 0)로 만들어 한 단계의 비용을 O(n3)에서 O(n2)로 줄입니다. ② 역반복처럼 이동Ak−σkI=QkRk,Ak+1=RkQk+σkI를 써서 수렴을 2차~3차로 빠르게 합니다. ③ 대각 아래 원소가 충분히 작아지면 그 고유값을 떼어 내고(감축, deflation) 더 작은 행렬로 계속합니다. 그 결과 n×n 행렬의 고유값 전부를 대략 10n3 flop 정도에 구합니다. 복소 고유값 쌍은 실수 연산으로 2×2 블록을 남기는 "실수 슈르 꼴"로 처리합니다.
반복법: 야코비, 가우스–자이델, SOR
백만 개의 미지수, 이웃은 넷뿐
금속판의 정상 상태 온도 분포를 1000×1000 격자로 계산하면 미지수가 10⁶개입니다. 각 격자점의 온도는 상하좌우 네 이웃의 평균이라는 식 하나로 묶여 있어, 행렬은 한 행에 0이 아닌 원소가 다섯 개뿐입니다. 밀집 소거라면 32(106)3≈7×1017 flop이 들고, 띠 LU로도 띠 폭이 1000이라 채움 때문에 10¹² flop과 수 GB의 메모리가 필요합니다. 행렬을 건드리지 않고 "이웃의 평균"만 되풀이해서 풀 수는 없을까요?
A를 쉽게 풀리는 부분 M과 나머지로 쪼갭니다: A=M−N. 그러면 Ax=b는 Mx=Nx+b이고, 이것을 반복식으로 바꿉니다.
Mxk+1=Nxk+b⟺xk+1=xk+M−1(b−Axk).
A=D−L−U(대각, 순하삼각의 부호 반대, 순상삼각의 부호 반대)로 쓰면 야코비는 M=D, 가우스–자이델은 M=D−L, SOR은 M=ω1D−L. 야코비는 이전 값만, 가우스–자이델은 방금 갱신한 값을 바로 쓴다.
오차 ek=xk−x는 ek+1=Gek, G=M−1N을 따릅니다. 그러므로 수렴 여부는 반복 행렬 G의 고유값 크기가 결정합니다. G의 고유값 절댓값의 최댓값을 스펙트럼 반지름(spectral radius)ρ(G)라 합니다.
정리 16.6 (정상 반복법의 수렴 조건) 반복 xk+1=Gxk+c가 모든 시작점에서 수렴할 필요충분조건은 ρ(G)<1이다. 이때 오차는 대략 단계마다 ρ(G)배로 줄어, 오차를 10−m배로 줄이는 데 약 m/(−log10ρ)단계가 든다.
증명 아이디어.ek=Gke0이다. G가 대각화 가능하면 e0를 고유벡터로 전개해 각 성분이 λik배가 되므로 모든 ∣λi∣<1일 때만 0으로 간다. 일반적인 경우는 조르당 꼴로 ∥Gk∥1/k→ρ(G)를 보인다. ∎
A가 엄격한 대각 우세(각 행에서 ∣aii∣>∑j=i∣aij∣)이면 야코비와 가우스–자이델은 반드시 수렴한다(야코비는 ∥G∥∞<1에서 바로 나온다). A가 대칭 양의 정부호이면 가우스–자이델과 0<ω<2인 SOR이 수렴한다.
위의 온도 문제(2차원 라플라스 방정식, 한 변 n칸, h=1/(n+1))에서는 스펙트럼 반지름을 정확히 계산할 수 있습니다. 야코비는 ρJ=cos(πh), 가우스–자이델은 ρGS=ρJ2(두 배 빠름), 최적 ω∗=2/(1+sinπh)를 쓴 SOR은 ρ=ω∗−1≈1−2πh입니다. 격자가 촘촘해질수록 ρJ≈1−2π2h2는 1에 붙어 버리므로 야코비는 O(n2)단계, 최적 SOR은 O(n)단계가 필요합니다.
SIMULATOR
금속판의 온도: 반복법 경주
온도 분포
log₁₀(상대 잔차) — 반복 횟수
방법
격자 한 변 n
반복 횟수0
상대 잔차 ‖b − Ax‖/‖b‖—
이론 ρ(G)—
10⁻⁶까지 예상 반복—
야코비가우스–자이델SOR켤레 기울기(CG)
해볼 것: ① '네 방법 모두 비교'를 누르면 잔차 곡선의 기울기가 log₁₀ρ와 맞는 직선으로 나옵니다. 가우스–자이델의 기울기는 야코비의 두 배입니다. ② n을 40으로 늘리면 야코비와 가우스–자이델은 1500번으로도 한참 모자라고, SOR과 CG는 백몇십 번이면 됩니다. ③ ω를 ω*에서 조금만 벗어나게 해도 SOR이 눈에 띄게 느려집니다. ω ≥ 2이면 발산합니다(이 슬라이더는 1.98까지). 모델: 위쪽 가장자리 온도 1, 나머지 가장자리 0인 정사각형 판의 5점 차분 라플라스 방정식. 시작값은 모두 0.
가우스–자이델이면 x1(1)=0.75를 곧바로 써서 x2(1)=(3+0.75)/4=0.9375. 반복 행렬의 스펙트럼 반지름은 1/16=ρJ2이다.
켤레 기울기법: 가장 빠른 내리막을 똑똑하게
방정식 풀기 = 그릇의 바닥 찾기
SOR은 좋은 ω를 알아야 빠릅니다. ω*는 이렇게 단순한 모델 문제에서나 공식으로 구할 수 있지요. 매개변수 없이, 대칭 양의 정부호 행렬이면 무엇이든 빠르게 풀리는 반복법은 없을까요? 실마리는 연립방정식을 최솟값 문제로 바꾸는 데 있습니다.
정리 16.7 (연립방정식과 이차 함수의 최솟값) A가 대칭 양의 정부호(SPD, 13장)이면 Ax=b의 해는 이차 함수
f(x)=21xTAx−bTx
의 유일한 최솟점이다. 증명. 기울기가 ∇f=Ax−b이므로 정류점은 해이고, f(x∗+e)=f(x∗)+21eTAe>f(x∗) (e=0). ∎
f의 등고선은 A의 고유벡터를 축으로 하는 타원이고, 축 길이 비가 κ입니다. 가장 단순한 전략은 매번 가장 가파른 방향 −∇f=rk(잔차!)로 그 직선 위의 최솟점까지 가는 최급강하법(steepest descent)입니다. 그런데 타원이 길쭉하면 골짜기 벽 사이를 지그재그로 오가며 매 단계 오차가 (κ−1)/(κ+1)배밖에 줄지 않습니다.
1952년 헤스테네스와 슈티펠이 발표한 켤레 기울기법(conjugate gradient, CG)은 방향을 더 똑똑하게 고릅니다. 새 방향을 이전 방향들과 A-켤레(piTApj=0)가 되도록 잡으면, 한 방향에서 최소화한 것을 다음 방향이 망치지 않습니다. 타원을 원으로 펴는 좌표에서 보면 서로 수직인 방향으로 한 번씩만 가는 것과 같습니다.
// 켤레 기울기법 (A는 SPD) r ← b − A x; p ← r 반복: α ← (rᵀr) / (pᵀAp) x ← x + α p; r_new ← r − α A p β ← (r_newᵀ r_new) / (rᵀr); p ← r_new + β p; r ← r_new
정리 16.8 (켤레 기울기법의 성질) A가 n×n SPD이면 정확한 산술에서 CG는 최대 n단계 만에 정확한 해에 닿는다. k단계의 xk는 크릴로프 공간 x0+span{r0,Ar0,…,Ak−1r0}에서 A-노름 오차 ∥e∥A=eTAe를 최소로 하는 점이며,
∥ek∥A≤2(κ+1κ−1)k∥e0∥A.증명 아이디어. 잔차들이 서로 직교하고 방향들이 서로 A-켤레임을 귀납법으로 보인다. ℝⁿ에서 0이 아닌 직교 벡터는 n개를 넘을 수 없으니 n단계 안에 잔차가 0이 된다. 오차 한계는 크릴로프 공간의 최적성을 체비쇼프 다항식으로 어림해서 얻는다. ∎
최급강하법의 (κ−1)/(κ+1)과 비교하면 CG는 κ 대신 κ가 들어갑니다. κ = 10⁴이면 최급강하법은 오차를 10배 줄이는 데 약 1만 단계, CG는 약 100단계가 듭니다. 실제 계산에서는 반올림 때문에 정확히 n단계에 끝나지 않지만, 행렬이 클 때 CG는 n보다 훨씬 적은 단계에서 충분히 정확해지므로 "직접법"이 아니라 반복법으로 씁니다.
SIMULATOR
최급강하법 대 켤레 기울기법
시작점을 끌어 보세요 · 바닥은 원점
보기
최급강하: 오차 10⁻⁶까지—
CG: 정확한 해까지—
최급강하 단계당 비 (κ−1)/(κ+1)—
CG 한계의 비 (√κ−1)/(√κ+1)—
해볼 것: ① κ = 1이면 등고선이 원이라 두 방법 모두 한 걸음에 바닥에 닿습니다. ② κ를 키우면 최급강하(빨강)가 골짜기에서 지그재그로 수십~수백 단계를 헤맵니다. 연속한 두 걸음은 언제나 서로 수직입니다. ③ CG(보라)는 κ와 시작점에 관계없이 정확히 두 걸음 만에 끝납니다(2차원이므로 n = 2). 시작점을 고유벡터 축 위에 두면 둘 다 한 걸음입니다. 모델: f(x) = ½xᵀAx, A = R diag(κ, 1) Rᵀ, 해 x* = 0. 각 걸음은 그 방향의 정확한 직선 최소화.
CG의 수렴은 κ에 달려 있으니, 같은 해를 갖되 조건수가 작은 문제로 바꿔 풀면 됩니다. 이것이 전처리(preconditioning)입니다. A와 비슷하면서 풀기 쉬운 SPD 행렬 M을 골라 M−1Ax=M−1b를 풉니다(대칭성을 지키도록 변형한 "전처리 CG"는 매 단계 Mz=r 하나를 더 풉니다). M = A면 κ = 1이지만 M을 푸는 것이 원래 문제만큼 어렵고, M = I면 쉽지만 효과가 없습니다. 그 사이의 절충으로 대각만 쓰는 야코비 전처리, 채움을 버린 불완전 콜레스키, 격자 문제에서 거의 최적인 다중 격자 같은 방법이 쓰입니다. 큰 문제를 빨리 푸는 기술의 대부분은 좋은 전처리기를 설계하는 기술입니다.
희소 행렬과 계산 규모
0을 저장하지 않으면
10⁶×10⁶ 행렬을 double로 다 저장하면 8 TB입니다. 그런데 온도 격자의 행렬은 0이 아닌 원소가 약 5×10⁶개(40 MB)뿐입니다. 99.9995%가 0입니다. 0을 저장하지도, 곱하지도 않는 자료 구조가 있으면 반복법의 한 단계(행렬–벡터 곱)가 n2이 아니라 0이 아닌 원소 수에 비례하게 됩니다.
대부분의 원소가 0인 행렬을 희소 행렬(sparse matrix)이라 하고, 0이 아닌 원소 수를 nnz라 씁니다. 가장 널리 쓰는 저장 형식은 CSR(compressed sparse row)입니다. 0이 아닌 값을 행 순서대로 늘어놓은 values, 각 값의 열 번호 col_idx, 그리고 각 행이 values의 어디서 시작하는지 적은 row_ptr(길이 n+1) 세 배열로 행렬을 표현합니다.
그림 16-3. 삼중대각 행렬의 CSR 저장. 행렬–벡터 곱 y = Ax는 각 행 i마다 values[row_ptr[i]]부터 values[row_ptr[i+1]−1]까지만 돌면 되므로 연산량이 약 2·nnz다.
희소 행렬에도 직접법을 쓸 수 있습니다. 다만 LU 분해가 0이었던 칸을 채우는 채움 때문에 미지수의 번호를 잘 매기는 것(재순서화)이 성능을 좌우합니다. 2차원 격자 문제는 희소 직접법이 여전히 강하고, 3차원 문제나 아주 큰 문제는 채움이 감당할 수 없을 만큼 커져서 전처리한 반복법이 주로 쓰입니다. 대략적인 비교는 다음과 같습니다.
방법
대상
연산량(대략)
메모리
특징
밀집 LU (부분 피벗팅)
모든 가역 행렬
32n3
n2
후방 안정, 예측 가능. n이 수만까지 현실적
콜레스키
SPD
31n3
n2/2
피벗팅 불필요
띠 LU
띠 폭 w
≈2nw2
nw
1차원 문제는 O(n)
희소 직접법
희소 행렬
채움에 좌우
nnz + 채움
재순서화가 핵심. 여러 우변에 강함
야코비·가우스–자이델
대각 우세 등
반복당 ≈ 2·nnz
nnz
단순하지만 느림. 다중 격자의 평활기
CG (+전처리)
SPD
반복당 ≈ 2·nnz, 반복 수 ∝ √κ
nnz + 벡터 몇 개
큰 희소 SPD 문제의 주력
GMRES 등 크릴로프 방법
일반 희소
반복당 행렬 곱 1번 + 직교화
nnz + 벡터 k개
비대칭 행렬용 CG의 일반화
직접 짜지 말고 라이브러리를 쓰자 밀집 행렬 계산은 1970년대 말부터 정해진 BLAS(Basic Linear Algebra Subprograms; 벡터·행렬–벡터·행렬–행렬 연산의 표준 인터페이스)와 그 위에 1990년대 초 발표된 LAPACK(연립방정식, 최소제곱, 고유값, SVD)이 사실상 표준입니다. MATLAB, NumPy·SciPy, R, Julia 같은 환경의 선형대수 함수는 대부분 LAPACK을 부르고, CPU·GPU 제조사는 자기 하드웨어에 맞춘 BLAS를 제공합니다. 같은 32n3 알고리즘이라도 캐시와 벡터 명령을 잘 쓰는 블록 구현은 교과서식 삼중 루프보다 수십 배 빠를 수 있습니다(2장의 행렬 곱 비용). 희소 행렬은 SciPy의 scipy.sparse, PETSc, SuiteSparse 같은 라이브러리가 저장 형식·직접법·반복법·전처리기를 제공합니다.
이 도구가 쓰이는 곳
수치 선형대수는 계산 과학과 공학 시뮬레이션의 밑바닥입니다. 이 책의 앞 장들이 "무엇을 계산하는가"였다면 이 장은 "어떻게 믿을 만하게, 충분히 빨리 계산하는가"입니다.