float64 hanya menyimpan sekitar 16 digit signifikan, dan setiap operasi aritmetika bisa menambah galat pembulatan kecil. Untuk kebanyakan kasus galat itu tak terlihat, tetapi ada dua kondisi yang membuat hasil perhitungan linear algebra layak diragukan: matriks yang kurang kondisi baik (ill-conditioned), yang memperbesar galat masukan berkali-kali lipat, dan rumus yang secara matematis benar tetapi secara numerik buruk. Keduanya tidak menimbulkan error sama sekali — program berjalan lancar dan mencetak angka yang salah.

Bab penutup section ini memberi kita tiga alat pengaman: condition number untuk mengukur sensitivitas sebuah matriks, contoh cancellation yang memperlihatkan rumus “setara” berbeda hasil, dan daftar aturan praktis yang menjaga akurasi.

Condition Number: Termometer Sensitivitas #

Condition number sebuah matriks, dihitung np.linalg.cond, adalah rasio nilai singular terbesar terhadap yang terkecil. Angka ini menyatakan seberapa besar galat relatif pada b bisa dibesarkan saat kita menyelesaikan A x = b. Aturan praktisnya: sekitar log10(cond) digit terakhir dari hasil layak diragukan.

python
import numpy as np

A_baik = np.array([[2.0, 0.0],
                   [0.0, 1.0]])
A_buruk = np.array([[1e16, 0.0],
                    [0.0, 1.0]])
print(np.linalg.cond(A_baik), np.linalg.cond(A_buruk))
A = np.array([[1.0, 1.0],
              [1.0, 1.0 + 1e-10]])
b = np.array([2.0, 2.0 + 1e-10])
print(np.linalg.cond(A))
print(np.linalg.solve(A, b))
b_perturbasi = np.array([2.0, 2.0 + 2e-10])
print(np.linalg.solve(A, b_perturbasi))

Output yang Dihasilkan:

text
2.0 1e+16
39999991794.0589
[1. 1.]
[0. 2.]
  1. Baris 3-6: dua matriks diagonal. Condition number matriks diagonal adalah rasio diagonal terbesar terhadap terkecil: 2.0 untuk yang pertama, 1e+16 untuk yang kedua.
  2. Baris 7: keduanya dicetak bersebelahan sebagai pembanding ekstrem.
  3. Baris 8-9 (A): baris kedua hampir identik dengan baris pertama — bedanya hanya 1e-10. Matriks seperti ini nyaris singular, dan cond menunjukkan 39999991794.0589, sekitar 4e10.
  4. Baris 12 (print(np.linalg.solve(A, b))): solusi [1. 1.] — benar secara analitik untuk b yang dipilih.
  5. Baris 13-14: b kita geser sedikit: entri kedua dari 2 + 1e-10 menjadi 2 + 2e-10, perubahan pada digit kesepuluh. Solusinya berubah total: [0. 2.].

Tabel ini merangkum tiga tingkatan yang baru kita lihat:

MatrikscondArti praktis di float64
diag(2, 1)2.0sangat stabil; hasil layak dipercaya penuh
[[1, 1], [1, 1 + 1e-10]]~4e10sekitar 10 digit terakhir tidak reliabel
diag(1e16, 1)1e16seluruh presisi float64 habis; hasil tidak berguna

Perhatikan bahwa tidak ada satu pun error yang muncul: solve bahkan tidak memperingatkan kita. Perubahan sekecil digit kesepuluh pada b menggeser solusi dari [1, 1] menjadi [0, 2] — itulah sifat masalah, bukan cacat program.

Cancellation: Saat Pengurangan Menipu #

Catastrophic cancellation terjadi ketika dua angka hampir sama besar dikurangkan: digit-digit utama saling menghapus, dan yang tersisa hanyalah derau pembulatan. Contoh klasiknya bahkan tidak butuh matriks:

python
import numpy as np

x = 1e16
print((x + 1.0) - x)

data = np.array([1e8, 1e8 + 1.0, 1e8 + 2.0])
dua_tahap = np.mean((data - data.mean()) ** 2)
satu_tahap = np.mean(data ** 2) - np.mean(data) ** 2
print(dua_tahap)
print(satu_tahap)

Output yang Dihasilkan:

text
0.0
0.6666666666666666
2.0
  1. Baris 3-4 ((x + 1.0) - x): hasilnya 0.0, padahal secara matematis harusnya 1. Penjelasannya: jarak antar bilangan float di sekitar 1e16 adalah 2, sehingga 1e16 + 1 dibulatkan kembali menjadi 1e16. Hasil salah ini deterministik — selalu 0.0.
  2. Baris 6 (data): tiga nilai sekitar 1e8 yang variansnya kecil.
  3. Baris 7 (dua_tahap): kurangi rata-rata lebih dulu, baru kuadratkan. Pengurangan besar terjadi pada angka yang sudah kecil; hasilnya 0.6666666666666666 — benar.
  4. Baris 8 (satu_tahap): rumus ringkas mean(x²) - mean(x)². Kedua suku bernilai sekitar 1.02e16 dan hampir sama besar; pengurangan mereka membuang hampir semua digit, menyisakan 2.0 — meleset sekitar tiga kali lipat.

Aturan Praktis Menjaga Akurasi #

Aturan paling terkenal: pilih np.linalg.solve daripada np.linalg.inv. Keduanya secara matematis setara, tetapi jalur inverse jauh lebih rentan terhadap galat pada matriks kurang kondisi baik. Kita buktikan pada matriks Hilbert 10x10 — H[i, j] = 1/(i + j + 1), contoh ill-conditioned yang termasyhur — dengan b yang solusi eksaknya semua satu:

python
import numpy as np

n = 10
i = np.arange(n)
H = 1.0 / (i[:, None] + i[None, :] + 1.0)
b = H @ np.ones(n)
x_solve = np.linalg.solve(H, b)
x_inv = np.linalg.inv(H) @ b
print(np.linalg.norm(x_solve - 1.0))
print(np.linalg.norm(x_inv - 1.0))

Output yang Dihasilkan:

text
3.9500387539846654e-05
0.0076846148676455546
  1. Baris 3-5 (H): matriks Hilbert 10x10 disusun lewat broadcasting: i[:, None] + i[None, :] + 1.0 membentuk seluruh penyebut sekaligus.
  2. Baris 6 (b = H @ np.ones(n)): dengan solusi x = 1, b dihitung dari H. Karena cond(H) mencapai ordo 1e13, soal ini menantang bagi kedua metode.
  3. Baris 9: galat jalur solve, diukur norm terhadap solusi eksak: ~3.95e-05.
  4. Baris 10: galat jalur inv(A) @ b: ~7.68e-03 — sekitar 200 kali lebih besar, dari persoalan yang sama persis.

Dari semua eksperimen bab ini, kita dapat merangkum lima aturan praktis:

  1. Utamakan np.linalg.solve(A, b), bukan np.linalg.inv(A) @ b — lebih cepat dan lebih akurat.
  2. Pakai np.linalg.lstsq untuk least squares, bukan inv(A.T @ A) @ A.T @ b; selain stabil, ia tidak membentuk matriks yang condition number-nya sudah dikuadratkan.
  3. Hindari mengurangkan angka yang hampir sama besar; susun ulang rumus, misalnya varians dua tahap daripada mean(x²) - mean(x)².
  4. Skala data agar kolom-kolom sebanding besarannya sebelum solve atau lstsq.
  5. Periksa np.linalg.cond sebelum mempercayai hasil — semakin besar nilainya, semakin banyak digit yang harus diragukan.

Dengan SVD, rank, least squares, dekomposisi, dan kesadaran akan stabilitas numerik, perangkat linear algebra numpy.linalg yang kita pelajari sepanjang section ini kini lengkap — bukan hanya bisa menghitung, tetapi juga tahu kapan hasilnya layak dipercaya.