Simulasi Monte Carlo merupakan teknik numerik yang memanfaatkan pengambilan sampel acak berulang untuk mendekati nilai parameter statistik — khususnya nilai ekspektasi — ketika pendekatan analitik sulit atau tidak mungkin dilakukan.
Dalam Tugas ini, kita memodifikasi kode simulasi permintaan dari materi kuliah dengan mengubah jumlah simulasi menjadi:
Kemudian membandingkan hasil setiap simulasi terhadap nilai ekspektasi teoritis \(E[X] = 70\), dan menganalisis pengaruh peningkatan \(N\) terhadap akurasi dan stabilitas estimasi.
Bloi ini memuat semua paket yang diperlukan dan mendefinisikan
parameter global. ggplot2 untuk visualisasi,
dplyr & tidyr untuk manipulasi data,
scales untuk format persentase, patchwork
untuk multi-panel, dan knitr untuk tabel rapi.
Sebelum menjalankan simulasi, kita membangun tabel distribusi permintaan yang sama persis dengan materi kuliah. Data ini mencerminkan frekuensi historis permintaan dalam 100 hari. Probabilitas dihitung dari frekuensi relatif, dan ekspektasi teoritis dihitung langsung dari distribusi tersebut.
tabel_permintaan <- data.frame(
permintaan = c(50, 60, 70, 80, 90),
frekuensi = c(10, 20, 40, 20, 10)
)
total_frekuensi <- sum(tabel_permintaan$frekuensi)
tabel_permintaan <- tabel_permintaan %>%
mutate(
probabilitas = frekuensi / total_frekuensi,
prob_kumulatif = cumsum(probabilitas),
batas_bawah = c(1, head(prob_kumulatif, -1) * 100 + 1),
batas_atass = prob_kumulatif * 100
)
tabel_permintaan$batas_atas <- tabel_permintaan$batas_atass
kable(tabel_permintaan[, c("permintaan", "frekuensi", "probabilitas", "prob_kumulatif", "batas_bawah", "batas_atas")],
digits = 4, align = "c",
col.names = c("Permintaan", "Frekuensi", "Probabilitas", "Prob. Kumulatif", "Batas Bawah", "Batas Atas"),
caption = "Tabel Distribusi Permintaan — Dasar Simulasi Monte Carlo")
| Permintaan | Frekuensi | Probabilitas | Prob. Kumulatif | Batas Bawah | Batas Atas |
|---|---|---|---|---|---|
| 50 | 10 | 0.1 | 0.1 | 1 | 10 |
| 60 | 20 | 0.2 | 0.3 | 11 | 30 |
| 70 | 40 | 0.4 | 0.7 | 31 | 70 |
| 80 | 20 | 0.2 | 0.9 | 71 | 90 |
| 90 | 10 | 0.1 | 1.0 | 91 | 100 |
Ekspektasi teoritis dihitung langsung dari tabel distribusi menggunakan rumus \(E[X] = \sum x_i \cdot P(x_i)\). Nilai ini menjadi acuan (benchmark) untuk mengevaluasi akurasi setiap simulasi.
nilai_ekspektasi <- sum(tabel_permintaan$permintaan * tabel_permintaan$probabilitas)
cat(sprintf(" E[X] = Σ xi · P(xi) \n"))
## E[X] = Σ xi · P(xi)
cat(sprintf(" = 50x0.1 + 60x0.2 + 70x0.4 + 80x0.2 + 90x0.1\n"))
## = 50x0.1 + 60x0.2 + 70x0.4 + 80x0.2 + 90x0.1
cat(sprintf(" = %.0f\n", nilai_ekspektasi))
## = 70
Visualisasi berikut menampilkan distribusi probabilitas permintaan dalam bentuk diagram batang dengan penanda nilai ekspektasi teoritis \(E[X] = 70\).
ggplot(tabel_permintaan, aes(x = faktor <- factor(permintaan), y = probabilitas)) +
geom_col(fill = "#5c6bc0", color = "white", alpha = 0.85, width = 0.6) +
geom_text(aes(label = paste0(probabilitas * 100, "%")), vjust = -0.5, fontface = "bold", size = 4.5, color = "#1a237e") +
geom_hline(yintercept = nilai_ekspektasi / sum(tabel_permintaan$permintaan), linetype = "dashed", color = "#c62828", size = 0.7, alpha = 0.6) +
annotate("text", x = 5.4, y = 0.41, label = paste("E[X] =", nilai_ekspektasi), color = "#c62828", fontface = "bold", size = 4, hjust = 1) +
labs(
title = "Distribusi Probabilitas Permintaan",
subtitle = "Data historis 100 hari | Distribusi simetris dengan modus 70",
x = "Nilai Permintaan", y = "Probabilitas"
) +
scale_y_continuous(labels = percent_format(), limits = c(0, 0.48)) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1a237e", size = 14),
plot.subtitle = element_text(color = "#5c6bc0", size = 9.5)
)
Fungsi simulasi_monte_carlo() dibangun secara modular
menggunakan metode findInterval(). Pembangkitan menggunakan
runif(n, 0, 100) yang menjamin angka acak kontinu super
presisi dan bebas dari error panjang kosong (length zero).
simulasi_monte_carlo <- function(n, tabel, seed = NULL) {
if (!is.null(seed)) set.seed(seed)
bilangan_acak <- runif(n, min = 0, max = 100)
batas_potong <- c(0, tabel$batas_atas)
idx <- findInterval(bilangan_acak, batas_potong, rightmost.closed = TRUE)
permintaan_sim <- tabel$permintaan[idx]
return(data.frame(
iter = 1:n,
bilangan_acak = bilangan_acak,
permintaan = permintaan_sim
))
}
Tiga kondisi simulasi dijalankan dengan seed yang berbeda untuk memastikan reproduktifitas penuh namun tetap representatif.
sim_1000 <- simulasi_monte_carlo(n = 1000, tabel = tabel_permintaan, seed = 42)
sim_5000 <- simulasi_monte_carlo(n = 5000, tabel = tabel_permintaan, seed = 99)
sim_20000 <- simulasi_monte_carlo(n = 20000, tabel = tabel_permintaan, seed = 2024)
cat("Jumlah simulasi | Rata-rata | Selisih vs E[X]=70\n")
## Jumlah simulasi | Rata-rata | Selisih vs E[X]=70
for (sim_info in list(
list(label = "N = 1.000", data = sim_1000),
list(label = "N = 5.000", data = sim_5000),
list(label = "N = 20.000", data = sim_20000)
)) {
mu <- mean(sim_info$data$permintaan)
sel <- abs(mu - nilai_ekspektasi)
cat(sprintf("%s | %7.4f | %6.4f\n", sim_info$label, mu, sel))
}
## N = 1.000 | 69.8500 | 0.1500
## N = 5.000 | 70.0140 | 0.0140
## N = 20.000 | 69.9895 | 0.0105
Tabel di bawah menjadi inti jawaban tugas — memuat ekspektasi teoritis dan ketiga kondisi simulasi dalam satu tampilan terstruktur, dilengkapi selisih absolut, persentase error, dan Standard Error (SE) analitik.
sigma_pop <- sqrt(sum(tabel_permintaan$probabilitas * (tabel_permintaan$permintaan - nilai_ekspektasi)^2))
n_vals <- c(1000, 5000, 20000)
se_analis <- sigma_pop / sqrt(n_vals)
df_perbandingan <- data.frame(
Metode = c("Ekspektasi Teoritis", "Simulasi N = 1.000", "Simulasi N = 5.000", "Simulasi N = 20.000"),
Nilai_Ekspektasi = c(nilai_ekspektasi, mean(sim_1000$permintaan), mean(sim_5000$permintaan), mean(sim_20000$permintaan))
) %>%
mutate(
Selisih_vs_EX = abs(Nilai_Ekspektasi - nilai_ekspektasi),
Error_Persen = (Selisih_vs_EX / nilai_ekspektasi) * 100,
SE_Analitik = c(NA, se_analis)
)
kable(df_perbandingan, digits = 5, align = "c",
col.names = c("Metode", "Nilai Ekspektasi", "Selisih vs E[X]", "Error (%)", "SE Analitik"),
caption = "Tabel Utama — Perbandingan Ekspektasi Simulasi vs Teoritis")
| Metode | Nilai Ekspektasi | Selisih vs E[X] | Error (%) | SE Analitik |
|---|---|---|---|---|
| Ekspektasi Teoritis | 70.0000 | 0.0000 | 0.00000 | NA |
| Simulasi N = 1.000 | 69.8500 | 0.1500 | 0.21429 | 0.34641 |
| Simulasi N = 5.000 | 70.0140 | 0.0140 | 0.02000 | 0.15492 |
| Simulasi N = 20.000 | 69.9895 | 0.0105 | 0.01500 | 0.07746 |
Tiga histogram berikut menampilkan distribusi nilai permintaan yang dihasilkan masing-masing simulasi. Sumbu X dipertahankan sebagai numerik kontinu agar sinkron dengan fungsi grafik tingkat lanjut Anda.
buat_hist <- function(df, n_label, warna) {
# 1. Hitung rata-rata empiris simulasi
mu_sim <- mean(df$permintaan, na.rm = TRUE)
# 2. Ringkas data menjadi proporsi empiris
df_prop <- df %>%
group_by(permintaan) %>%
summarise(prop = n() / nrow(df), .groups = 'drop')
# 3. Bangun objek grafik ggplot
ggplot() +
geom_col(data = df_prop, aes(x = permintaan, y = prop),
fill = warna, color = "white", alpha = 0.82, width = 6) +
geom_hline(data = tabel_permintaan, aes(yintercept = probabilitas),
color = "#e65100", linetype = "dashed", linewidth = 0.4, alpha = 0.5) +
geom_point(data = tabel_permintaan, aes(x = permintaan, y = probabilitas),
color = "#e65100", size = 4, shape = 18) +
geom_vline(data = data.frame(x = mu_sim), aes(xintercept = x),
color = "#c62828", linewidth = 1.1, linetype = "solid", alpha = 0.8) +
labs(
title = paste("Simulasi N =", format(n_label, big.mark=".")),
subtitle = paste("Rata-rata =", round(mu_sim, 4)),
x = "Nilai Permintaan", y = "Probabilitas"
) +
scale_x_continuous(breaks = c(50, 60, 70, 80, 90)) +
scale_y_continuous(labels = percent_format(), limits = c(0, 0.48)) +
theme_minimal()
}
p1 <- buat_hist(sim_1000, 1000, "#5c6bc0")
p2 <- buat_hist(sim_5000, 5000, "#26a69a")
p3 <- buat_hist(sim_20000, 20000, "#ff7043")
panel_grafik <- p1 + p2 + p3 + plot_layout(ncol = 3)
panel_grafik
Grafik di bawah ini melacak pergerakan nilai rata-rata kumulatif (running mean) sepanjang iterasi berjalan. Garis hitam horizontal tipis merepresentasikan nilai target absolut \(E[X] = 70\).
hitung_running_mean <- function(df, label) {
df %>%
mutate(
running_mean = cumsum(permintaan) / iter,
Simulasi = label
)
}
rm_1000 <- hitung_running_mean(sim_1000, "N = 1.000")
rm_5000 <- hitung_running_mean(sim_5000, "N = 5.000")
rm_20000 <- hitung_running_mean(sim_20000, "N = 20.000")
df_running <- bind_rows(rm_1000, rm_5000, rm_20000)
ggplot(df_running, aes(x = iter, y = running_mean, color = Simulasi)) +
geom_line(linewidth = 0.8, alpha = 0.8) +
geom_hline(yintercept = 70, linetype = "dashed", color = "black", linewidth = 0.5) +
labs(
title = "Kurva Konvergensi Running Mean Menuju E[X] = 70",
subtitle = "Semakin besar N, fluktuasi awal teredam dan stabil menetap di garis parameter asli",
x = "Nomor Iterasi / Replikasi", y = "Nilai Rata-rata Kumulatif"
) +
scale_color_manual(values = c("N = 1.000" = "#5c6bc0", "N = 5.000" = "#26a69a", "N = 20.000" = "#ff7043")) +
theme_minimal()
Berikut adalah visualisasi tingkat kesalahan (error rate) yang menurun secara eksponensial seiring bertambahnya volume replikasi sampel acak.
df_error_plot <- df_perbandingan %>% filter(Metode != "Ekspektasi Teoritis")
ggplot(df_error_plot, aes(x = Metode, y = Error_Persen, fill = Metode)) +
geom_col(width = 0.4, alpha = 0.85, show.legend = FALSE) +
geom_text(aes(label = paste0(round(Error_Persen, 4), "%")), vjust = -0.5, fontface = "bold") +
labs(
title = "Penyusutan Persentase Error Estimasi",
subtitle = "Visualisasi penurunan galat relatif terhadap parameter E[X]",
x = "Ukuran Sampel Simulasi", y = "Galat Relatif (%)"
) +
scale_fill_manual(values = c("Simulasi N = 1.000" = "#5c6bc0", "Simulasi N = 5.000" = "#26a69a", "Simulasi N = 20.000" = "#ff7043")) +
theme_minimal()
Sebagai validasi statistika komputasi tambahan, grafik di bawah ini membandingkan Standard Error analitik terhadap nilai sebaran teoritisnya.
df_se_plot <- df_perbandingan %>%
filter(Metode != "Ekspektasi Teoritis") %>%
mutate(Sampel_N = factor(c("1.000", "5.000", "20.000"), levels = c("1.000", "5.000", "20.000")))
ggplot(df_se_plot, aes(x = Sampel_N, y = SE_Analitik, group = 1)) +
geom_line(color = "#b71c1c", linewidth = 1, linetype = "twodash") +
geom_point(color = "#b71c1c", size = 4) +
geom_text(aes(label = round(SE_Analitik, 5)), vjust = -0.8, fontface = "bold") +
labs(
title = "Penyusutan Nilai Standard Error (SE) Analitik",
subtitle = "Membuktikan berlakunya Teorema Limit Pusat secara matematis murni",
x = "Ukuran Replikasi (N)", y = "Standard Error"
) +
theme_minimal()
Berdasarkan eksperimen berbasis data komputasi, nilai rata-rata empiris yang dicapai adalah 69.85 untuk \(N=1.000\), kemudian bergerak ke angka 70.014 untuk \(N=5.000\), dan mencapai tingkat akurasi tertinggi sebesar 69.9895 pada \(N=20.000\).
Fenomena penyusutan fluktuasi acak yang terekam jelas pada Plot Konvergensi Running Mean (Bagian 7.2) memberikan bukti empiris berlakunya Hukum Bilangan Besar (Law of Large Numbers). Ketika ukuran sampel replikasi diperbesar mendekati tak hingga, nilai rata-rata sampel dipastikan akan mengonvergens secara presisi menuju nilai parameter aslinya.
Simulasi Monte Carlo terbukti menjadi instrumen estimasi statistik numerik yang sangat andal dan tangguh dalam memodelkan ketidakpastian dunia bisnis nyata. Peningkatan kapasitas replikasi berbanding lurus dengan minimalisasi persentase galat (error rate) serta penyusutan nilai Standard Error secara konsisten.