Sistem persamaan linear

Menyelesaikan sistem persamaan dengan linalg.solve.

Daftar isi

Banyak persoalan data berujung pada pencarian beberapa nilai sekaligus dari sekumpulan persamaan linear, misalnya menaksir harga satuan dari beberapa nota belanja atau menyeimbangkan aliran antar pos pembukuan. Topik ini membahas bentuk matriks Ax=bA x = b, penyelesaiannya dengan np.linalg.solve, syarat matriks koefisiennya, verifikasi solusi dengan np.allclose, serta alasan solve lebih diutamakan daripada perhitungan invers. Setelah topik ini dipahami, sistem persamaan kecil maupun besar dapat diselesaikan dan diverifikasi dengan pola kode yang sama.

Bentuk matriks A x = b#

Sistem persamaan linear yang ringkas dapat ditulis ulang sebagai satu perkalian matriks. Vektor x memuat nilai-nilai yang dicari, matriks A memuat koefisien setiap persamaan pada barisnya masing-masing, sedangkan vektor b memuat sisi kanan setiap persamaan. Semua informasi sistem dengan demikian terangkum dalam dua array, dan penyelesaiannya menjadi urusan satu fungsi.

Contoh berikut memuat dua nota belanja. Nota pertama berisi dua pensil dan tiga buku dengan total 19000, sedangkan nota kedua berisi satu pensil dan dua buku dengan total 11000. Apabila x1x_1 menyatakan harga satu pensil dan x2x_2 menyatakan harga satu buku, sistemnya dituliskan sebagai berikut.

2x1+3x2=19000x1+2x2=11000[2312][x1x2]=[1900011000] \begin{aligned} 2x_1 + 3x_2 &= 19000 \\ x_1 + 2x_2 &= 11000 \end{aligned} \qquad \begin{bmatrix} 2 & 3 \\ 1 & 2 \end{bmatrix} \begin{bmatrix} x_1 \\ x_2 \end{bmatrix} = \begin{bmatrix} 19000 \\ 11000 \end{bmatrix}

Bentuk di kanan adalah bentuk matriks Ax=bA x = b yang diminta oleh NumPy, yaitu A sebagai array dua dimensi dan b sebagai array satu dimensi. Pemetaan dari persamaan ke matriks bersifat langsung, yaitu setiap baris A diambil dari koefisien satu persamaan dengan urutan yang sama seperti urutan angka pada b. Penulisan ini pula yang memungkinkan sistem dengan puluhan variabel diperlakukan dengan kode yang persis sama.

Menyelesaikan dengan linalg.solve#

Fungsi np.linalg.solve(A, b) mengembalikan vektor x yang memenuhi Ax=bA x = b. Fungsi ini menuntut A berupa matriks persegi yang berperingkat penuh, yaitu banyaknya persamaan sama dengan banyaknya variabel dan tidak ada persamaan yang merupakan kelipatan atau gabungan dari persamaan lain.

python
import numpy as np

A = np.array([[2.0, 3.0],
              [1.0, 2.0]])
b = np.array([19000.0, 11000.0])
x = np.linalg.solve(A, b)
print(x)
text Keluaran
[5000. 3000.]

Hasilnya menyatakan harga satu pensil bernilai 5000 dan satu buku bernilai 3000. Nilai tersebut dapat dicek dengan substitusi, yaitu 2×5000+3×3000=190002 \times 5000 + 3 \times 3000 = 19000 untuk nota pertama. Pengecekan semacam ini sebaiknya dilakukan oleh kode, bukan oleh mata, karena perhitungan pecahan pada komputer dibulatkan di setiap langkahnya.

python
import numpy as np

A = np.array([[2.0, 3.0],
              [1.0, 2.0]])
b = np.array([19000.0, 11000.0])
x = np.linalg.solve(A, b)
print(np.round(A @ x, 6))
print(np.allclose(A @ x, b))
text Keluaran
[19000. 11000.]
True

Pola verifikasinya adalah menghitung A @ x lalu membandingkannya dengan b menggunakan np.allclose, yaitu fungsi perbandingan pecahan yang menoleransi selisih pembulatan sekecil apa pun dalam batas wajar. Pembanding == tidak digunakan untuk tujuan ini, dan alasannya dibahas pada bagian Kesalahan umum.

Sistem dengan tiga variabel#

API yang sama berlaku tanpa perubahan untuk sistem yang lebih besar. Setiap baris pada A mewakili satu persamaan, sedangkan setiap kolomnya mewakili satu variabel, sehingga sistem tiga persamaan tiga variabel ditulis sebagai matriks 3x3 dengan vektor hasil sepanjang tiga. Syaratnya tetap, yaitu A persegi dan berperingkat penuh, sedangkan b mengikuti banyaknya baris A.

python
import numpy as np

A = np.array([[1.0, 2.0, 1.0],
              [2.0, 1.0, 1.0],
              [1.0, 1.0, 2.0]])
b = np.array([9.0, 8.0, 7.0])
x = np.linalg.solve(A, b)
print(x)
print(np.allclose(A @ x, b))
text Keluaran
[2. 3. 1.]
True

Kode di atas sama persis dengan versi dua variabel, hanya bentuk operandnya yang berubah, sehingga penambahan variabel tidak menambah kerumitan kode. Banyaknya persamaan juga dapat lebih banyak daripada variabelnya, tetapi kasus itu tidak lagi cocok untuk solve karena sistemnya tidak menentukan satu solusi tunggal. NumPy menyediakan np.linalg.lstsq untuk kasus berlebih tersebut, yaitu pencarian solusi yang meminimalkan jumlah kuadrat sisanya, dan pembahasannya berada di luar lingkup topik ini. Nilai pada A dan b ditulis dengan tanda desimal agar perhitungan berjalan pada dtype float64, mengikuti kebiasaan yang sama seperti sistem dua variabel pada bagian sebelumnya.

Matriks singular dan error LinAlgError#

Matriks koefisien disebut singular apabila salah satu barisnya merupakan kelipatan baris lain, sehingga sistemnya kehilangan informasi dan tidak memiliki solusi tunggal. Fungsi solve menolak kasus ini dengan LinAlgError, seperti pada matriks yang baris keduanya dua kali baris pertamanya berikut.

python
import numpy as np

A = np.array([[1.0, 2.0],
              [2.0, 4.0]])
b = np.array([3.0, 6.0])
try:
    x = np.linalg.solve(A, b)
    print(x)
except Exception as e:
    print(f"{type(e).__name__}: {e}")
text Keluaran
LinAlgError: Singular matrix

Pesan Singular matrix menegaskan bahwa masalahnya ada pada A, bukan pada b, sehingga mengubah nilai b tidak akan pernah memperbaiki kasus ini. Pada matriks contoh, persamaan keduanya hanyalah dua kali persamaan pertamanya, sehingga kedua persamaan itu sesungguhnya menyatakan satu hal yang sama. Kode yang menerima matriks dari data eksternal sebaiknya membungkus pemanggilan solve dengan try dan except, karena singularitas sering baru terlihat saat eksekusi dan error ini mudah ditangani secara terprogram.

Mengapa solve, bukan invers#

Cara lain yang secara matematis setara adalah menghitung invers dengan np.linalg.inv, lalu mengalikannya dengan b, karena x=A−1bx = A^{-1} b. Keluaran kedua cara ini sama untuk sistem yang kecil dan sehat.

python
import numpy as np

A = np.array([[2.0, 3.0],
              [1.0, 2.0]])
b = np.array([19000.0, 11000.0])
x_solve = np.linalg.solve(A, b)
x_inv = np.linalg.inv(A) @ b
print(np.round(x_solve, 6))
print(np.round(x_inv, 6))
print(np.allclose(x_solve, x_inv))
text Keluaran
[5000. 3000.]
[5000. 3000.]
True

Meskipun hasilnya sama, solve tetap menjadi pilihan utama. Secara internal, solve memfaktorkan A dengan dekomposisi LU, lalu menyelesaikan dua sistem segitiga, sedangkan jalur invers membentuk matriks invers secara penuh terlebih dahulu sebelum mengalikannya. Jalur pertama memerlukan operasi aritmetika yang lebih sedikit dan mengakumulasi kesalahan pembulatan yang lebih kecil, terutama pada matriks besar atau matriks yang mendekati singular. Karena itu, kebiasaan yang dianjurkan adalah menggunakan solve untuk semua persoalan Ax=bA x = b dan menyisakan inv untuk kasus yang benar-benar membutuhkan matriks inversnya sendiri.

Kesalahan umum#

Kesalahan pertama adalah memverifikasi solusi dengan ==. Perbandingan persis pada pecahan bergantung pada hasil pembulatan tahap akhir, sehingga sistem yang sama dapat lolos atau gagal bergantung pada angkanya.

python
import numpy as np

A = np.array([[2.5, 1.5],
              [1.5, 3.5]])
b = np.array([7.0, 8.0])
x = np.linalg.solve(A, b)
print(np.round(x, 6))
print(A @ x == b)
print(np.allclose(A @ x, b))
text Keluaran
[1.923077 1.461538]
[False False]
True

Solusi pada contoh itu sudah benar, tetapi A @ x == b tetap bernilai False karena sisa pembulatan, sedangkan np.allclose menilai keduanya sama dengan tepat. Perbaikannya adalah menjadikan np.allclose sebagai pembanding baku untuk semua hasil pecahan.

Kesalahan kedua adalah menghitung np.linalg.inv terlebih dahulu padahal tujuan akhirnya adalah nilai x. Kebiasaan ini membebani perhitungan dengan operasi yang tidak diperlukan dan menambah peluang kesalahan pembulatan, sehingga pemanggilannya diganti langsung dengan np.linalg.solve.

Kesalahan ketiga adalah membiarkan LinAlgError tidak ditangani pada data yang berasal dari luar. Sistem yang disusun dari data pengukuran dapat saja memiliki baris ganda, dan program yang membungkus solve dengan try serta except dapat melaporkan persoalannya dengan rapi, alih-alih berhenti mendadak.

Ringkasan#

  • Sistem persamaan linear ditulis sebagai Ax=bA x = b, yaitu matriks koefisien persegi A, vektor variabel x, dan vektor sisi kanan b.
  • np.linalg.solve(A, b) mengembalikan solusi tunggal apabila A berperingkat penuh, dan memunculkan LinAlgError berupa Singular matrix apabila tidak.
  • Solusi diverifikasi dengan menghitung A @ x lalu membandingkannya dengan b menggunakan np.allclose, bukan ==.
  • solve diutamakan daripada inv karena memerlukan operasi lebih sedikit dan mengakumulasi kesalahan pembulatan yang lebih kecil.

Referensi#