Praktikum P1 membahas Representasi & Analisis Galat dalam Komputasi Numerik. Praktikum ini digunakan untuk melihat secara langsung bagaimana round-off error muncul dalam komputasi menggunakan R.
Tujuan praktikum adalah memahami pengaruh keterbatasan representasi bilangan, membandingkan absolute error dan relative error, serta menggunakan fungsi sederhana untuk memeriksa angka signifikan.
Sebelum menjalankan kode, diperkirakan apakah a dan
b akan bernilai sama.
a <- 1 + 1e-16 + 1e-16 + 1e-16 +
1e-16 + 1e-16 + 1e-16 + 1e-16
print(a, digits = 17)
## [1] 1
b <- 1e-16 + 1e-16 + 1e-16 + 1e-16 +
1e-16 + 1e-16 + 1e-16 + 1
print(b, digits = 17)
## [1] 1.0000000000000007
identical(a, b)
## [1] FALSE
Jelaskan dengan bahasamu sendiri kenapa a dan b berbeda, padahal secara matematis keduanya menjumlahkan bilangan yang sama persis.
Nilai a dan b dapat berbeda karena komputer
menggunakan representasi floating-point yang memiliki
ketelitian terbatas. Pada a, bilangan 1e-16
ditambahkan satu per satu ke angka 1, sehingga perubahan
yang sangat kecil dapat hilang akibat pembulatan. Pada b,
bilangan-bilangan kecil dijumlahkan terlebih dahulu sebelum ditambahkan
ke angka 1. Akibatnya, urutan penjumlahan dapat memengaruhi
hasil akhir.
Coba modifikasi kode di atas: ganti 1e-16 menjadi 1e-10. Apakah hasilnya masih berbeda? Kenapa?
Jika 1e-16 diganti menjadi 1e-10, pengaruh
bilangan kecil lebih mudah terlihat karena 1e-10 jauh lebih
besar daripada 1e-16. Dengan demikian, pembulatan tidak
menghilangkan pengaruh bilangan tersebut dengan cara yang sama seperti
pada kasus 1e-16.
Secara matematis,
\[ f(x)=\frac{e^x-e^{-x}}{x} \]
seharusnya mendekati 2 ketika x mendekati
0.
f <- function(x) (exp(x) - exp(-x)) / x
xs <- 10^-(1:16)
hasil <- data.frame(x = xs, f_x = sapply(xs, f))
print(hasil, digits = 12)
## x f_x
## 1 1e-01 2.00333500040
## 2 1e-02 2.00003333350
## 3 1e-03 2.00000033333
## 4 1e-04 2.00000000333
## 5 1e-05 2.00000000002
## 6 1e-06 1.99999999995
## 7 1e-07 1.99999999895
## 8 1e-08 1.99999998785
## 9 1e-09 2.00000005446
## 10 1e-10 2.00000016548
## 11 1e-11 2.00000016548
## 12 1e-12 2.00006677886
## 13 1e-13 1.99951166735
## 14 1e-14 1.99840144433
## 15 1e-15 2.10942374679
## 16 1e-16 1.11022302463
# Bandingkan dengan bentuk yang setara secara aljabar:
g <- function(x) 2 + x^2 / 3
hasil$g_x <- sapply(xs, g)
print(hasil, digits = 12)
## x f_x g_x
## 1 1e-01 2.00333500040 2.00333333333
## 2 1e-02 2.00003333350 2.00003333333
## 3 1e-03 2.00000033333 2.00000033333
## 4 1e-04 2.00000000333 2.00000000333
## 5 1e-05 2.00000000002 2.00000000003
## 6 1e-06 1.99999999995 2.00000000000
## 7 1e-07 1.99999999895 2.00000000000
## 8 1e-08 1.99999998785 2.00000000000
## 9 1e-09 2.00000005446 2.00000000000
## 10 1e-10 2.00000016548 2.00000000000
## 11 1e-11 2.00000016548 2.00000000000
## 12 1e-12 2.00006677886 2.00000000000
## 13 1e-13 1.99951166735 2.00000000000
## 14 1e-14 1.99840144433 2.00000000000
## 15 1e-15 2.10942374679 2.00000000000
## 16 1e-16 1.11022302463 2.00000000000
Hasil f(x) mulai terlihat tidak stabil ketika
x semakin kecil, terutama sekitar 10^-8 dan
menjadi semakin jelas pada nilai yang lebih kecil. Hal ini terjadi
karena exp(x) dan exp(-x) mempunyai nilai yang
sangat dekat ketika x mendekati nol. Ketika dua bilangan
yang hampir sama tersebut dikurangkan, sebagian ketelitian numerik dapat
hilang. Akibatnya, round-off error menjadi lebih terlihat.
Pada nilai x yang sangat kecil, g(x) lebih
stabil dibandingkan f(x). Reformulasi aljabar membantu
karena g(x) tidak melakukan pengurangan langsung terhadap
dua bilangan yang hampir sama. Oleh karena itu, kehilangan ketelitian
akibat pengurangan tersebut dapat dikurangi.
Secara matematis, deret harmonik
\[ \sum_{n=1}^{\infty}\frac{1}{n} \]
divergen, sehingga tidak benar-benar berhenti bertambah.
Kode berikut merupakan kode praktikum dan dipertahankan sesuai soal.
total <- 0; n <- 0
repeat {
n <- n + 1
term <- 1 / n
if (total + term == total) break # suku sudah 'hilang'
total <- total + term
}
cat('Berhenti pada n =', n, '\n')
cat('Total akhir =', total, '\n')
Catatan: blok di atas sengaja menggunakan
eval=FALSE agar proses Knit tidak terhenti sangat
lama. Kode R di dalamnya tidak diubah. Jika dijalankan langsung di R
Console/RStudio, program membutuhkan jumlah iterasi yang sangat besar
sebelum kondisi total + term == total tercapai.
Program tersebut tidak benar-benar menghitung sampai tak hingga.
Program berhenti ketika nilai 1/n sudah sangat kecil
sehingga ketika ditambahkan ke total, hasilnya tidak
mengalami perubahan lagi. Jadi, program “membeku” bukan karena deret
harmonik benar-benar berhenti, tetapi karena komputer mempunyai
ketelitian floating-point yang terbatas sehingga perubahan yang sangat
kecil tidak lagi dapat direpresentasikan pada nilai
total.
Konsep yang sama dengan Aktivitas 1 adalah keterbatasan representasi
bilangan floating-point dan round-off error. Pada Aktivitas 1,
bilangan yang sangat kecil dapat kehilangan pengaruh ketika dijumlahkan
dengan bilangan yang jauh lebih besar. Pada Aktivitas 3, suku
1/n yang semakin kecil akhirnya tidak memberikan perubahan
pada total. Keduanya menunjukkan bahwa hasil komputasi
komputer dapat berbeda dari perilaku matematika ideal karena ketelitian
mesin terbatas.
Pada aktivitas ini dibuat fungsi untuk menghitung absolute error dan relative error.
absolute_error <- function(p, p_star) abs(p - p_star)
relative_error <- function(p, p_star) abs(p - p_star) / abs(p)
pasangan <- list(
c(p = pi, p_star = 3.14),
c(p = exp(1), p_star = 2.71828),
c(p = 0.0005, p_star = 0.00045)
)
for (pr in pasangan) {
cat(sprintf('p=%.6f p*=%.6f | abs=%.3e rel=%.3e\n',
pr['p'], pr['p_star'],
absolute_error(pr['p'], pr['p_star']),
relative_error(pr['p'], pr['p_star'])))
}
## p=3.141593 p*=3.140000 | abs=1.593e-03 rel=5.070e-04
## p=2.718282 p*=2.718280 | abs=1.828e-06 rel=6.727e-07
## p=0.000500 p*=0.000450 | abs=5.000e-05 rel=1.000e-01
Dari tiga pasangan di atas, mana yang punya absolute error terkecil? Mana yang punya relative error terkecil? Apakah dua urutan itu sama?
Dari tiga pasangan tersebut, absolute error terkecil
dimiliki oleh pasangan p = exp(1) dan
p* = 2.71828, yaitu sekitar 1.828 × 10^-6.
Relative error terkecil juga dimiliki oleh pasangan
tersebut, yaitu sekitar 6.727 × 10^-7.
Jadi, pada tiga pasangan yang diberikan, urutan berdasarkan absolute error terkecil dan relative error terkecil adalah sama.
p, p*)
buatanmu sendiri di mana absolute error KECIL tapi relative error BESAR.
Jelaskan kenapa itu mungkin terjadi.
Contoh pasangan buatan sendiri:
p = 0.000001 dan p* = 0
Absolute error:
\[ |0.000001-0|=0.000001 \]
Relative error:
\[ \frac{|0.000001-0|}{|0.000001|}=1 \]
Absolute error terlihat kecil karena selisih kedua nilai hanya
0.000001. Namun relative error menjadi besar karena error
tersebut dibandingkan dengan nilai p yang juga sangat
kecil. Jadi, absolute error yang kecil tidak selalu berarti relative
error juga kecil.
Fungsi berikut digunakan untuk mencari t (angka
signifikan) terbesar yang memenuhi:
\[ \frac{|p-p^*|}{|p|}\leq5\times10^{-t} \]
sig_digits <- function(p, p_star, t_max = 15) {
rel <- abs(p - p_star) / abs(p)
t <- 0
for (k in 1:t_max) {
if (rel <= 5 * 10^(-k)) t <- k else break
}
t
}
sig_digits(pi, 3.14) # bandingkan dengan hitungan manualmu
## [1] 3
sig_digits(pi, 3.1416)
## [1] 6
sig_digits(1234.5, 1234.0)
## [1] 4
Hasil sig_digits(pi, 3.14) adalah 3. Jadi
berdasarkan fungsi yang diberikan pada praktikum, pasangan
pi dan 3.14 memenuhi syarat untuk
3 angka signifikan.
Untuk kasus batas 4.9 × 10^-4 dan
5.1 × 10^-4, nilai batasnya adalah
5 × 10^-4.
Karena:
\[ 4.9\times10^{-4}\leq5\times10^{-4} \]
maka 4.9 × 10^-4 masih memenuhi syarat.
Sedangkan:
\[ 5.1\times10^{-4}>5\times10^{-4} \]
maka 5.1 × 10^-4 tidak memenuhi syarat pada tingkat
tersebut.
Hal ini menunjukkan bahwa nilai yang berada di bawah atau tepat pada batas masih memenuhi kriteria, sedangkan nilai yang melewati batas tidak memenuhi kriteria.
Dari praktikum ini dapat dilihat bahwa hasil perhitungan komputer tidak selalu sama persis dengan hasil matematika ideal. Keterbatasan representasi floating-point dapat menyebabkan round-off error, terutama ketika bilangan yang sangat kecil berinteraksi dengan bilangan yang jauh lebih besar atau ketika dua bilangan yang hampir sama dikurangkan.
Selain itu, absolute error dan relative error memberikan cara yang berbeda untuk melihat besarnya kesalahan. Relative error menjadi penting ketika nilai sebenarnya sangat kecil karena error yang secara absolut kecil dapat menjadi relatif besar.
Praktikum ini juga menunjukkan pentingnya memilih bentuk perhitungan yang lebih stabil secara numerik, seperti pada reformulasi fungsi di Aktivitas 2.