Materi
Persamaan normal benar secara matematis, tetapi Austin §6.5 mencatat bahwa galat pembulatan kecil dapat menumpuk saat dihitung di komputer, dan menunjukkan jalan yang lebih andal lewat QR. Bila A = QR dengan Q berkolom ortonormal, proyeksi b ke Col A adalah QQᵀb, sehingga Ax̂ = QQᵀb, yaitu QRx̂ = QQᵀb. Mengalikan dari kiri dengan Qᵀ memberi Rx̂ = Qᵀb. R segitiga atas, jadi x̂ didapat dengan substitusi balik dari baris terakhir, tanpa pernah membentuk AᵀA.
Ukuran seberapa dekat sebuah matriks ke singular adalah bilangan kondisinya, κ(A) = σ₁/σₙ, perbandingan nilai singular terbesar dan terkecil (Austin §7.5; nilai singular dibahas di modul terakhir). Semakin besar κ, semakin besar galat pembulatan dapat membengkak pada penyelesaian. Nilai singular A adalah akar nilai eigen AᵀA, sehingga κ(AᵀA) = κ(A)²: membentuk AᵀA mengkuadratkan masalahnya. Bilangan float64 hanya membawa sekitar 16 digit desimal (dokumentasi Python §15), jadi matriks dengan κ mendekati 10¹⁶ praktis singular bagi komputer. np.linalg.cond menghitung κ dengan norma 2 secara bawaan.
Matriks desain polinom dengan t yang berdekatan adalah contoh klasik: kolom 1, t, t², … hampir sejajar. np.linalg.lstsq sendiri tidak membentuk AᵀA; fungsi itu bekerja dengan nilai singular A dan membuang nilai singular yang terlalu kecil lewat rcond. Jadi ada tiga jalan dengan ketelitian berbeda: persamaan normal, QR dengan Rx̂ = Qᵀb, dan lstsq.
Substitusi balik: R = [[4, 2], [0, 5]], Qᵀb = (10, 15)
baris 2: 5x̂₂ = 15 → x̂₂ = 3
baris 1: 4x̂₁ + 2·3 = 10 → x̂₁ = 1
import numpy as np
t = np.linspace(2, 3, 25)
A = np.vander(t, 7, increasing=True) # kolom 1, t, …, t⁶
np.linalg.cond(A) # orde 1e9
np.linalg.cond(A.T @ A) # orde 1e17 atau lebih
x_benar = np.array([2., -1, 3, 0.5, -2, 1, 4]); b = A @ x_benar
x_normal = np.linalg.solve(A.T @ A, A.T @ b)
Q, R = np.linalg.qr(A)
x_qr = np.linalg.solve(R, Q.T @ b)
np.abs(x_normal - x_benar).max() # orde 1e1: tidak ada digit yang benar
np.abs(x_qr - x_benar).max() # orde 1e-7