Sepanjang proyek, kita sudah memakai dua pendekatan: solve di bagian kedua dan inv sebagai jalur alternatif. Kini kita tambahkan pemain ketiga, np.linalg.lstsq dari bab Least Squares, lalu meletakkan ketiganya bersebelahan. Perbandingan menjadi lebih bermakna karena ada satu wilayah yang hanya milik lstsq: sistem dengan persamaan lebih banyak daripada variabel.

Sistem Overdetermined dan Least Squares #

Kehidupan nyata jarang memberikan tepat tiga persamaan. Gudang bisa melaporkan catatan pemakaian dari empat sumber berbeda, sehingga kita memiliki empat persamaan untuk tiga variabel — sistem overdetermined. Kalau semua catatan konsisten, solusi tunggal tetap ada dan lstsq akan menemukannya:

python
import numpy as np

A_over = np.array([[2, 1, 1], [1, 3, 2], [1, 1, 4], [1, 1, 4]])
b_over = np.array([135, 175, 185, 185])
print(A_over.shape)
x_ls, residuals, rank, sv = np.linalg.lstsq(A_over, b_over, rcond=None)
print(x_ls)
print(residuals)
print(residuals.shape)
print(np.allclose(x_ls, [40, 25, 30]))

Output yang Dihasilkan:

text
(4, 3)
[40. 25. 30.]
[3.57806954e-28]
(1,)
True

Mari kita bedah:

  1. Baris 3-4 (A_over): baris keempat mengulang [1, 1, 4] dengan total 185 — informasi redundan, bukan kontradiksi. shape (4, 3) menegaskan empat persamaan untuk tiga variabel.
  2. Baris 6 (np.linalg.lstsq(..., rcond=None)): fungsi memfaktorkan A lewat SVD (bab Decompositions) dan mengembalikan empat nilai: solusi, residual kuadrat, rank, dan singular values.
  3. Baris 8-9 (residuals): bentuknya (1,) — satu nilai untuk sisa persamaan (4 - 3). Isinya 3.57806954e-28, praktis nol: jejak pembulatan SVD pada sistem yang sebenarnya konsisten.
  4. Baris 10 (np.allclose(...)): solusi terbaiknya identik dengan hasil solve, [40, 25, 30].

Ketika Data Pembawa Noise #

Catatan pemakaian yang sebenarnya tidak pernah mulus: ada kebocoran, ada penghitungan ganda. Kita meniru kondisi itu dengan menggeser setiap total pemakaian beberapa desimal:

python
import numpy as np

A_data = np.array([[2, 1, 1], [1, 3, 2], [1, 1, 4], [1, 1, 4]])
b_data = np.array([135.2, 174.8, 185.1, 184.9])
x_ls, residuals, rank, sv = np.linalg.lstsq(A_data, b_data, rcond=None)
print(x_ls)
print(residuals)
print(np.linalg.norm(A_data @ x_ls - b_data))

Output yang Dihasilkan:

text
[40.1625 24.8875 29.9875]
[0.02]
0.14142135623730145

Penjelasan singkat:

  1. Baris 3-4 (b_data): empat total pemakaian kini membawa noise kecil (misal 135.2 alih-alih 135); tidak ada x yang bisa memenuhi keempat persamaan sekaligus.
  2. Baris 6 (lstsq): solusi terbaik bergeser tipis menjadi [40.1625 24.8875 29.9875] — kompromi yang meminimalkan jumlah kuadrat selisih, bukan solusi eksak.
  3. Baris 8-9: SSE bernilai 0.02, dan norma residualnya sekitar 0.141 — ukuran total meleset sistem dalam satuan liter atau lembar.

Dua hal penting terlihat di sini. Pertama, solusi tetap dekat dengan kunci jawaban [40, 25, 30]: lstsq justru dirancang untuk data yang tidak sempurna. Kedua, solve dan inv sama sekali tidak bisa dipakai di kasus ini — keduanya menuntut matriks persegi dan akan melempar LinAlgError dengan pesan “Last 2 dimensions of the array must be square” bila menerima A berukuran 4x3.

Tabel Perbandingan Tiga Metode #

Kini ketiga metode bisa kita letakkan bersebelahan:

MetodeSyarat inputKeluaranStabilitasKapan dipakai
np.linalg.solveA persegi, non-singularx tunggalbaik (faktorisasi langsung)sistem persegi eksak; pilihan pertama
np.linalg.inv(A) @ bA persegi, non-singularx lewat A⁻¹lebih lemah; lebih mahalsaat A⁻¹ memang dibutuhkan berulang kali
np.linalg.lstsqA berukuran apa punx + SSE + rank + singular valuespaling tahan (SVD)overdetermined, data ber-noise, regresi linear

Untuk A kita, angka kondisi 4,06 kecil sehingga ketiga metode yang berlaku sepakat pada jawaban yang sama. Pada sistem ill-conditioned, urutan preferensi dalam tabel berlaku ketat — uraian lengkapnya ada pada bab Stabilitas Numerik.

Laporan Proyek #

Seluruh angka penting dari proyek ini terkumpul dalam satu tabel laporan:

BesaranNilaiAlat
Solusi x (Pabrik A, B, C)40, 25, 30 unit per harinp.linalg.solve
Verifikasi A @ x = ballclose Truenp.allclose
Solusi jalur inverseidentik bit-per-bit dengan solvenp.array_equal
Residual[0. 0. 0.], norma 0.0b - A @ x
Determinan A16.000000000000007 (sekitar 16)np.linalg.det
Rank A3 (penuh)np.linalg.matrix_rank
Angka kondisi A4.059145176318882np.linalg.cond
lstsq konsisten (4x3)x sama, SSE 3.57806954e-28np.linalg.lstsq
lstsq ber-noisex [40.1625 24.8875 29.9875], SSE 0.02np.linalg.lstsq

Bila proyek ini dikembangkan menjadi aplikasi, struktur berkasnya natural: src/sistem.py menyimpan soal (A, b) beserta fungsi selesaikan() dan periksa(), sedangkan src/laporan.py merangkum besaran di atas dari keduanya. Tabel di atas adalah versi naratif dari keluaran dua modul itu.

Pemeriksaan Akhir #

Pemeriksaan otomatis berikut merangkum seluruh klaim proyek ke dalam satu script. Jalankan ulang kapan saja untuk memastikan tidak ada yang bergeser:

python
import numpy as np

A = np.array([[2, 1, 1], [1, 3, 2], [1, 1, 4]])
b = np.array([135, 175, 185])

x = np.linalg.solve(A, b)
x_inv = np.linalg.inv(A) @ b
residual = b - A @ x

np.testing.assert_allclose(A @ x, b)
np.testing.assert_allclose(x, x_inv)
np.testing.assert_allclose(residual, np.zeros(3), atol=1e-12)
assert np.linalg.matrix_rank(A) == 3
print("Semua pemeriksaan lulus")

Output yang Dihasilkan:

text
Semua pemeriksaan lulus

Cara kerjanya:

  1. np.testing.assert_allclose(...): tiga assertion membandingkan A @ x dengan b, x dengan x_inv, dan residual dengan nol. Setiap assertion melempar AssertionError bila gagal, sehingga script yang tiba di baris terakhir berarti lolos semuanya.
  2. assert np.linalg.matrix_rank(A) == 3: menjaga struktur soal — bila soal berubah sehingga tak lagi non-singular, pemeriksaan ini yang akan berbunyi lebih dulu.

Proyek selesai: soal cerita kini menjadi sistem yang terdefinisi, terjawab, teraudit, dan terbandingkan — dengan laporan serta pemeriksaan yang dapat direproduksi.