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:
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:
(4, 3)
[40. 25. 30.]
[3.57806954e-28]
(1,)
TrueMari kita bedah:
- 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. - 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. - 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. - Baris 10 (
np.allclose(...)): solusi terbaiknya identik dengan hasilsolve,[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:
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:
[40.1625 24.8875 29.9875]
[0.02]
0.14142135623730145Penjelasan singkat:
- 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. - Baris 6 (
lstsq): solusi terbaik bergeser tipis menjadi[40.1625 24.8875 29.9875]— kompromi yang meminimalkan jumlah kuadrat selisih, bukan solusi eksak. - 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:
| Metode | Syarat input | Keluaran | Stabilitas | Kapan dipakai |
|---|---|---|---|---|
np.linalg.solve | A persegi, non-singular | x tunggal | baik (faktorisasi langsung) | sistem persegi eksak; pilihan pertama |
np.linalg.inv(A) @ b | A persegi, non-singular | x lewat A⁻¹ | lebih lemah; lebih mahal | saat A⁻¹ memang dibutuhkan berulang kali |
np.linalg.lstsq | A berukuran apa pun | x + SSE + rank + singular values | paling 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:
| Besaran | Nilai | Alat |
|---|---|---|
| Solusi x (Pabrik A, B, C) | 40, 25, 30 unit per hari | np.linalg.solve |
| Verifikasi A @ x = b | allclose True | np.allclose |
| Solusi jalur inverse | identik bit-per-bit dengan solve | np.array_equal |
| Residual | [0. 0. 0.], norma 0.0 | b - A @ x |
| Determinan A | 16.000000000000007 (sekitar 16) | np.linalg.det |
| Rank A | 3 (penuh) | np.linalg.matrix_rank |
| Angka kondisi A | 4.059145176318882 | np.linalg.cond |
| lstsq konsisten (4x3) | x sama, SSE 3.57806954e-28 | np.linalg.lstsq |
| lstsq ber-noise | x [40.1625 24.8875 29.9875], SSE 0.02 | np.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:
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:
Semua pemeriksaan lulusCara kerjanya:
np.testing.assert_allclose(...): tiga assertion membandingkan A @ x dengan b, x dengan x_inv, dan residual dengan nol. Setiap assertion melemparAssertionErrorbila gagal, sehingga script yang tiba di baris terakhir berarti lolos semuanya.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.