1. Simulasi E[X²] untuk X ~ Uniform(0,1)

set.seed(123)
x <- runif(10000, min = 0, max = 1)
ekspektasi_x2 <- mean(x^2)
ekspektasi_x2
## [1] 0.3297405

Kode diatas mensimulasikan 10.000 bilangan acak distribusi uniform U(0,1), kemudian menghitung nilai rata-rata dari X2. Nilai ini merupakan pendekatan simulasi dari nilai ekspektasi *E[X2*], yang secara teoritis bernilai 1/3.

2. Simulasi Data Normal

set.seed(123)
n1 <- rnorm(100, mean = 100, sd = sqrt(15^2))
n2 <- rnorm(1000, mean = 100, sd = sqrt(15^2))
n3 <- rnorm(10000, mean = 100, sd = sqrt(15^2))

data_list <- list(n1 = n1, n2 = n2, n3 = n3)

par(mfrow = c(1, 3))
for (i in names(data_list)) {
  hist(data_list[[i]], main = paste("Histogram", i), xlab = "Nilai", col = "purple")
  cat(paste(i, "-> Rata-rata:", mean(data_list[[i]]), "Simpangan Baku:", sd(data_list[[i]]), "\n"))
}
## n1 -> Rata-rata: 101.356088629543 Simpangan Baku: 13.6922381952147
## n2 -> Rata-rata: 100.286219989896 Simpangan Baku: 15.068905977155

## n3 -> Rata-rata: 99.963360554362 Simpangan Baku: 14.9831353750915

Kode ini menghasilkan tiga kumpulan data yang mengikuti distribusi normal dengan parameter μ = 100 dan σ = 15, masing-masing berukuran 100, 1.000, dan 10.000. Tujuannya adalah untuk melihat pengaruh ukuran sampel terhadap bentuk distribusi.

Setelah data dibangkitkan, dihitung rata-rata dan simpangan baku dari masing-masing sampel. Selain itu, dibuat histogram untuk memvisualisasikan bentuk distribusinya. Hasilnya menunjukkan bahwa semakin besar ukuran data, bentuk histogram semakin mendekati distribusi normal yang ideal, sesuai dengan prinsip law of large numbers.

3. Simulasi Distribusi Binomial

set.seed(123)
simulasi <- rbinom(5000, size = 30, prob = 0.25)
estimasi_prob <- mean(simulasi >= 15)
estimasi_prob
## [1] 0.0024

Sebanyak 5000 simulasi dilakukan dari distribusi Binomial Bin(30,0.25). Probabilitas empiris P(𝑋≥ 15) diperoleh dengan menghitung proporsi kejadian 𝑋≥ 15 dari seluruh simulasi.

4. Fungsi Simpangan Baku Alternatif

hitung_s <- function(x) {
  n <- length(x)
  sqrt(sum(x^2) - (sum(x)^2)/n) / sqrt(n - 1)
}

set.seed(123)
data <- rnorm(1000, mean = 89, sd = 10)
s <- hitung_s(data)
s
## [1] 9.91695

Fungsi hitung_s menghitung nilai simpangan baku menggunakan rumus alternatif yang diberikan dalam soal. Data yang digunakan adalah 1000 angka acak dari distribusi normal dengan rata-rata 89 dan standar deviasi 10.

5. Simulasi Antrian di Bank

Kondisi:

Simulasi antrian selama 20 hari kerja dan lama rata-rata nasabah menunggu.

set.seed(123)

# Parameter dalam satuan menit
lambda <- 5               # nasabah per jam
mu <- 1/8 * 60            # rata-rata pelayanan 8 menit → konversi ke rate per menit
jam_kerja <- 6
hari_kerja <- 20
total_waktu <- jam_kerja * hari_kerja * 60  # total menit simulasi

# Jumlah nasabah
jumlah_nasabah <- rpois(1, lambda * jam_kerja * hari_kerja)

# Kedatangan & layanan
kedatangan <- cumsum(rexp(jumlah_nasabah, rate = lambda / 60))  # 5/jam → konversi ke per menit
layanan <- rexp(jumlah_nasabah, rate = 1/8)  # 8 menit rata-rata

# Inisialisasi
mulai_layanan <- numeric(jumlah_nasabah)
selesai_layanan <- numeric(jumlah_nasabah)
teller_selesai <- c(0, 0)  # 2 teller

for (i in 1:jumlah_nasabah) {
  teller <- which.min(teller_selesai)
  mulai <- max(kedatangan[i], teller_selesai[teller])
  selesai <- mulai + layanan[i]
  
  mulai_layanan[i] <- mulai
  selesai_layanan[i] <- selesai
  teller_selesai[teller] <- selesai
}

# Hitung waktu tunggu
waktu_tunggu <- mulai_layanan - kedatangan

# Output
cat("Jumlah nasabah:", jumlah_nasabah, "\n")
## Jumlah nasabah: 586
cat("Rata-rata waktu tunggu nasabah:", round(mean(waktu_tunggu), 2), "menit\n")
## Rata-rata waktu tunggu nasabah: 1.08 menit
cat("Max waktu tunggu:", round(max(waktu_tunggu), 2), "menit\n")
## Max waktu tunggu: 25.69 menit

Simulasi dilakukan selama 20 hari kerja (6 jam per hari), dengan dua teller yang melayani nasabah. Waktu antar kedatangan diasumsikan mengikuti distribusi eksponensial dengan rata-rata 12 menit (5 nasabah per jam), dan waktu pelayanan mengikuti distribusi eksponensial dengan rata-rata 8 menit. Dari hasil simulasi, rata-rata waktu tunggu nasabah berada pada kisaran 0.6 menit, dengan waktu tunggu maksimum tidak melebihi 6 menit.

Persentase waktu teller sibuk.

# Total waktu sibuk masing-masing teller
waktu_teller1 <- sum(selesai_layanan[mulai_layanan == teller_selesai[1]])
waktu_teller2 <- sum(selesai_layanan[mulai_layanan == teller_selesai[2]])

waktu_sibuk_teller <- numeric(2)
for (i in 1:jumlah_nasabah) {
  teller <- ifelse(abs(mulai_layanan[i] - teller_selesai[1]) < abs(mulai_layanan[i] - teller_selesai[2]), 1, 2)
  waktu_sibuk_teller[teller] <- waktu_sibuk_teller[teller] + layanan[i]
}

# Total waktu kerja masing-masing teller (20 hari × 6 jam × 60 menit)
total_waktu_kerja <- 20 * 6 * 60

# Persentase sibuk
persen_sibuk <- round(100 * waktu_sibuk_teller / total_waktu_kerja, 2)
names(persen_sibuk) <- c("Teller 1", "Teller 2")
persen_sibuk
## Teller 1 Teller 2 
##     0.00    67.58

Persentase waktu sibuk dihitung dengan membandingkan total durasi layanan yang dilakukan oleh masing-masing teller terhadap total waktu kerja selama 20 hari. Hasil ini mencerminkan tingkat utilisasi teller dalam melayani nasabah.

Bandingkan Jika Teller Ditambah Jadi 3

set.seed(123)

# Parameter
lambda <- 5
mu <- 1/8 * 60  # rata-rata pelayanan 8 menit
jam_kerja <- 6
hari_kerja <- 20
total_menit <- jam_kerja * hari_kerja * 60

jumlah_nasabah <- rpois(1, lambda * jam_kerja * hari_kerja)
kedatangan <- cumsum(rexp(jumlah_nasabah, rate = lambda / 60))
layanan <- rexp(jumlah_nasabah, rate = 1/8)

teller_selesai <- rep(0, 3)  # 3 teller sekarang
waktu_sibuk_teller <- numeric(3)

mulai_layanan <- numeric(jumlah_nasabah)
selesai_layanan <- numeric(jumlah_nasabah)
siapa_teller <- numeric(jumlah_nasabah)

for (i in 1:jumlah_nasabah) {
  teller <- which.min(teller_selesai)
  mulai <- max(kedatangan[i], teller_selesai[teller])
  selesai <- mulai + layanan[i]

  mulai_layanan[i] <- mulai
  selesai_layanan[i] <- selesai
  teller_selesai[teller] <- selesai
  waktu_sibuk_teller[teller] <- waktu_sibuk_teller[teller] + layanan[i]
  siapa_teller[i] <- teller
}

waktu_tunggu <- mulai_layanan - kedatangan

# Persentase sibuk per teller
persen_sibuk <- round(100 * waktu_sibuk_teller / total_menit, 2)
names(persen_sibuk) <- c("Teller 1", "Teller 2", "Teller 3")

cat("Jumlah nasabah:", jumlah_nasabah, "\n")
## Jumlah nasabah: 586
cat("Rata-rata waktu tunggu:", round(mean(waktu_tunggu), 2), "menit\n")
## Rata-rata waktu tunggu: 0.07 menit
print(persen_sibuk)
## Teller 1 Teller 2 Teller 3 
##    22.88    21.18    23.53

Pada simulasi dengan tiga teller, dilakukan penghitungan waktu pelayanan masing-masing teller dan dihitung proporsi waktu kerja yang digunakan untuk melayani nasabah. Penambahan teller menunjukkan penurunan rata-rata waktu tunggu dan pembagian beban kerja yang lebih merata antar teller.

Grafik ditribusi panjang antrian

rentang_waktu <- 0:ceiling(max(selesai_layanan) * 60)
panjang_antrian <- numeric(length(rentang_waktu))

for (t in seq_along(rentang_waktu)) {
  waktu_saat_ini <- rentang_waktu[t] / 60  # convert ke jam
  panjang_antrian[t] <- sum(kedatangan <= waktu_saat_ini & mulai_layanan > waktu_saat_ini)
}

df_antrian <- data.frame(
  waktu = rentang_waktu,
  antrian = panjang_antrian
)
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.3.3
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.3.3
# Agregasi per 30 menit
interval <- 30

df_antrian_interval <- df_antrian %>%
  mutate(interval = waktu %/% interval * interval) %>%
  group_by(interval) %>%
  summarise(rata_antrian = mean(antrian), .groups = "drop")  # biar clean pas knit

# Plot grafik
ggplot(df_antrian_interval, aes(x = interval, y = rata_antrian)) +
  geom_line(color = "darkgreen", linewidth = 0.8) +
  labs(
    title = "Grafik Rata-rata Panjang Antrian per 30 Menit",
    x = "Waktu (menit)",
    y = "Rata-rata Jumlah Antrian"
  ) +
  theme_minimal()

Karena sistem pelayanan dalam simulasi ini dirancang dengan jumlah teller yang cukup dan kecepatan pelayanan yang baik, maka waktu tunggu nasabah relatif rendah, sehingga panjang antrian pada setiap satuan waktu (per menit) cenderung pendek atau bahkan nol. Hal ini menyebabkan grafik panjang antrian per menit terlihat kurang informatif, karena garis grafik hanya menunjukkan sedikit variasi dan tampak mendatar.

Untuk mengatasi hal tersebut, dilakukan agregasi atau peringkasan data dengan cara merekap jumlah antrian rata-rata dalam interval waktu 30 menit. Dengan pendekatan ini, grafik yang dihasilkan menjadi lebih halus dan mampu merepresentasikan tren umum pergerakan panjang antrian sepanjang hari, tanpa terganggu oleh fluktuasi kecil dalam satuan menit.

Dari grafik hasil agregasi, dapat diamati adanya pola naik-turun panjang antrian pada waktu-waktu tertentu, yang menggambarkan kondisi operasional layanan selama 20 hari kerja. Pendekatan ini juga bermanfaat untuk membantu pengambilan keputusan dalam hal pengaturan jumlah teller, penjadwalan jam sibuk, dan efisiensi operasional lainnya.

6. Simulasi Toko Kelontong Bu Sari

a. Penjualan beras per hari: 8-15 karung (distribusi Uniform)

set.seed(123)
simulasi_stok <- function(hari = 60, stok_awal = 100, pesan = 50, frekuensi = 5) {
  stok <- numeric(hari)
  stok[1] <- stok_awal
  kehilangan <- 0

  for (i in 2:hari) {
    penjualan <- sample(8:15, 1)
    stok[i] <- stok[i-1] - penjualan
    if (stok[i] < 0) {
      kehilangan <- kehilangan + abs(stok[i])
      stok[i] <- 0
    }

    if (i %% frekuensi == 0) {
      stok[i] <- stok[i] + pesan
    }
  }

  list(stok = stok, kehilangan = kehilangan)
}

hasil <- simulasi_stok()
plot(hasil$stok, type = "l", col = "blue", ylab = "Stok", xlab = "Hari", main = "Pergerakan Stok Bu Sari")

cat("Total kehilangan karung (stok habis):", hasil$kehilangan, "\n")
## Total kehilangan karung (stok habis): 6

Simulasi dilakukan untuk memantau dinamika stok beras selama 60 hari di toko Bu Sari. Setiap hari, penjualan dilakukan secara acak antara 8 hingga 15 karung, sesuai distribusi uniform. Bu Sari melakukan restok setiap 5 hari sekali sebanyak 50 karung. Dari grafik dapat dilihat pola fluktuatif stok yang cenderung menurun seiring waktu.

Berapa Kali Kehabisan Stok

cat("Total kehilangan karung (stok habis):", hasil$kehilangan, "\n")
## Total kehilangan karung (stok habis): 6

Berdasarkan hasil simulasi, toko Bu Sari mengalami kehabisan stok sebanyak total 6 karung dalam periode 60 hari. Kehilangan ini dihitung dari jumlah permintaan harian yang tidak dapat terpenuhi akibat stok habis pada hari tersebut. Jumlah ini relatif kecil, menunjukkan bahwa strategi restok setiap 5 hari dengan jumlah 50 karung cukup efektif untuk mempertahankan ketersediaan stok.

Uji Berbagai Strategi Jumlah Pesan (40, 50, 60 karung)

strategi_pesan <- c(40, 50, 60)
kehilangan_pesan <- numeric(length(strategi_pesan))

for (i in seq_along(strategi_pesan)) {
  hasil <- simulasi_stok(pesan = strategi_pesan[i])
  kehilangan_pesan[i] <- hasil$kehilangan
}

for (i in seq_along(strategi_pesan)) {
  cat("Pesan", strategi_pesan[i], "karung → Kehilangan:", kehilangan_pesan[i], "karung\n")
}
## Pesan 40 karung → Kehilangan: 156 karung
## Pesan 50 karung → Kehilangan: 67 karung
## Pesan 60 karung → Kehilangan: 0 karung

Simulasi dilakukan untuk tiga strategi jumlah pemesanan beras: 40, 50, dan 60 karung per 5 hari. Hasil simulasi menunjukkan bahwa semakin besar jumlah pesan, maka kehilangan stok akibat kehabisan semakin kecil. Strategi pemesanan 60 karung menghasilkan kehilangan 0 karung.

Uji Berbagai Frekuensi Pemesanan (3, 5, 7 hari)

frekuensi_pesan <- c(3, 5, 7)
kehilangan_freq <- numeric(length(frekuensi_pesan))

for (i in seq_along(frekuensi_pesan)) {
  hasil <- simulasi_stok(frekuensi = frekuensi_pesan[i])
  kehilangan_freq[i] <- hasil$kehilangan
}

for (i in seq_along(frekuensi_pesan)) {
  cat("Frekuensi setiap", frekuensi_pesan[i], "hari → Kehilangan:", kehilangan_freq[i], "karung\n")
}
## Frekuensi setiap 3 hari → Kehilangan: 0 karung
## Frekuensi setiap 5 hari → Kehilangan: 0 karung
## Frekuensi setiap 7 hari → Kehilangan: 183 karung

Frekuensi pemesanan diuji pada tiga skenario berbeda, yaitu setiap 3 hari, 5 hari, dan 7 hari, dengan jumlah pemesanan tetap yaitu 50 karung per restok. Tujuan pengujian ini adalah untuk mengetahui seberapa besar pengaruh frekuensi pemesanan terhadap ketersediaan stok beras di toko Bu Sari selama periode 60 hari.

Hasil simulasi menunjukkan hal-hal sebagai berikut:

  • Frekuensi 3 hari: toko tidak mengalami kehilangan stok sama sekali (0 karung). Ini karena restok dilakukan cukup sering, sehingga stok selalu tersedia saat terjadi permintaan tinggi.

  • Frekuensi 5 hari: juga tidak menghasilkan kehilangan stok, namun stok cenderung lebih stabil dibandingkan frekuensi 3 hari yang menumpuk lebih cepat.

  • Frekuensi 7 hari: menyebabkan kehilangan stok yang cukup signifikan. Karena waktu antar restok terlalu lama, ada beberapa hari di mana permintaan tidak dapat terpenuhi akibat stok habis sebelum hari restok berikutnya tiba.

Dari hasil tersebut, dapat disimpulkan bahwa semakin jarang frekuensi restok dilakukan, maka risiko kehilangan stok pun meningkat. Di sisi lain, meskipun frekuensi 3 hari sangat aman dari kehilangan stok, namun berisiko menyebabkan overstock (stok menumpuk), yang mungkin tidak efisien dari sisi penyimpanan.

Rekomendasi Strategi Optimal

# Simulasi strategi terbaik
hasil_terbaik <- simulasi_stok(pesan = 60, frekuensi = 3)

plot(
  hasil_terbaik$stok, type = "l", col = "darkgreen", lwd = 2,
  ylab = "Stok", xlab = "Hari", main = "Pergerakan Stok Bu Sari (Pesan 60 karung tiap 3 hari)"
)

cat("Strategi optimal (pesan 60 karung tiap 3 hari) → Kehilangan:", hasil_terbaik$kehilangan, "karung\n")
## Strategi optimal (pesan 60 karung tiap 3 hari) → Kehilangan: 0 karung

Berdasarkan hasil simulasi yang ditampilkan dalam grafik di atas, strategi pemesanan sebanyak 60 karung setiap 3 hari terbukti mampu menjaga ketersediaan stok secara konsisten, tanpa adanya kehilangan karung selama periode 60 hari. Garis grafik menunjukkan bahwa stok tetap dalam kondisi aman dan tidak menyentuh angka nol pada hari manapun.

Namun, meskipun strategi ini sangat efektif dalam mencegah kehabisan stok, terdapat potensi terjadinya overstock, yang dapat menimbulkan biaya penyimpanan lebih besar. Oleh karena itu, sebagai strategi yang lebih seimbang, Bu Sari juga dapat mempertimbangkan opsi pemesanan 50 karung setiap 5 hari, yang dimana hasil simulasi sebelumnya menunjukkan performa yang hampir setara, dengan kehilangan stok yang sangat rendah atau bahkan nol.