1. Gunakan simulasi untuk menghitung nilai pendekatan dari

\[ E[X ^ 2] \] untuk variabel random berdistribusi Uniform jika X sim U(0, 1) Gunakan 10.000 angka random.

# Set seed 
set.seed(123)

# Jumlah simulasi
n <- 10000

# Generate 10.000 bilangan acak dari distribusi Uniform(0,1)
x <- runif(n, min = 0, max = 1)

# Hitung nilai E[X^2] dengan pendekatan simulasi
EX2 <- mean(x^2)

# Hasil
cat("Pendekatan nilai E[X^2] dari simulasi adalah:", EX2, "\n")
## Pendekatan nilai E[X^2] dari simulasi adalah: 0.3297405

2. Buat masing-masing untuk n_{1} = 100 n_{2} = 1000 dan n_{3} = 10000 angka random dari N(100, 15 ^ 2) lalu hitung rata-rata, simpangan baku, dan plot histogram. Berikan pen-jelasan hasil yang Saudara peroleh.

# Set seed 
set.seed(123)

# Parameter distribusi normal
mean_true <- 100
sd_true <- 15

# Ukuran sampel
n1 <- 100
n2 <- 1000
n3 <- 10000

# Generate data
data1 <- rnorm(n1, mean = mean_true, sd = sd_true)
data2 <- rnorm(n2, mean = mean_true, sd = sd_true)
data3 <- rnorm(n3, mean = mean_true, sd = sd_true)
mean1 <- mean(data1)
mean2 <- mean(data2)
mean3 <- mean(data3)

cat("Rata-rata:\n")
## Rata-rata:
cat("n = 100   :", round(mean1, 2), "\n")
## n = 100   : 101.36
cat("n = 1000  :", round(mean2, 2), "\n")
## n = 1000  : 100.29
cat("n = 10000 :", round(mean3, 2), "\n")
## n = 10000 : 99.96
sd1 <- sd(data1)
sd2 <- sd(data2)
sd3 <- sd(data3)

cat("\nSimpangan Baku:\n")
## 
## Simpangan Baku:
cat("n = 100   :", round(sd1, 2), "\n")
## n = 100   : 13.69
cat("n = 1000  :", round(sd2, 2), "\n")
## n = 1000  : 15.07
cat("n = 10000 :", round(sd3, 2), "\n")
## n = 10000 : 14.98
# Histogram untuk n = 100
hist(data1, 
     main = "Histogram N = 100", 
     xlab = "Nilai", 
     col = "skyblue", 
     border = "white", 
     breaks = 20)

# Histogram untuk n = 1000
hist(data2, 
     main = "Histogram N = 1000", 
     xlab = "Nilai", 
     col = "lightgreen", 
     border = "white", 
     breaks = 30)

# Histogram untuk n = 10000
hist(data3, 
     main = "Histogram N = 10000", 
     xlab = "Nilai", 
     col = "pink", 
     border = "white", 
     breaks = 40)

Hasil simulasi menunjukkan bahwa semakin besar ukuran sampel, nilai rata-rata dan simpangan baku semakin mendekati nilai teoritis, yaitu 100 dan 15. Pada sampel kecil (n = 100), hasil masih cukup bervariasi, namun mulai stabil pada n = 1000 dan n = 10000. Bentuk histogram juga semakin menyerupai kurva normal seiring bertambahnya jumlah data, membuktikan bahwa distribusi data akan semakin mendekati distribusi normal jika sampel cukup besar.

3. Simulasikan distribusi binomial Bin (30, 0.25) sebanyak 5000 kali. Tentukan nilai esti-masikan P(X >= 15).

# Set seed 
set.seed(123)

# Parameter distribusi binomial
# jumlah percobaan
n <- 30      
# peluang sukses
p <- 0.25 
# jumlah simulasi
sim <- 5000   

# Simulasi data binomial
data_binom <- rbinom(sim, size = n, prob = p)

# Estimasi peluang P(X >= 15)
p_estimate <- mean(data_binom >= 15)

# Hasil
cat("Estimasi P(X >= 15) dari simulasi:", p_estimate, "\n")
## Estimasi P(X >= 15) dari simulasi: 0.0024

4.Buatlah function untuk rumus berikut

\[ s = \sqrt{\frac{\sum x^2 - \frac{(\sum x)^2}{n}}{n - 1}} \] Hitung nilai s dengan membangkitkan data berdistribusi normal sebanyak data 1000, rata-rata 89, dan standar deviasi 10.

# menghitung simpangan baku manual
s_manual <- function(x) {
  n <- length(x)
  sum_x2 <- sum(x^2)
  sum_x <- sum(x)
  s <- sqrt((sum_x2 - (sum_x^2 / n)) / (n - 1))
  return(s)
}

# Set seed 
set.seed(123)

# Membangkitkan 1000 data dari distribusi normal (mean = 89, sd = 10)
data <- rnorm(1000, mean = 89, sd = 10)

# Hitung simpangan baku
s_hasil <- s_manual(data)

# Hasil
cat("Hasil perhitungan simpangan baku (s):", s_hasil, "\n")
## Hasil perhitungan simpangan baku (s): 9.91695
# Bandingkan dengan fungsi sd bawaan R
cat("Simpangan baku dengan fungsi sd():", sd(data), "\n")
## Simpangan baku dengan fungsi sd(): 9.91695

5. (Simulasi Antrian di Bank) Sebuah bank memiliki 2 teller dengan kondisi berikut.

  1. Buatlah simulasi antrian selama 20 hari kerja
  2. Hitung berapa lama rata-rata nasabah menunggu
  3. Hitung berapa persen waktu teller sibuk bekerja
  4. Bandingkan jika bank menambah 1 teller lagi apakah lebih efisien?
  5. Buat grafik yang menunjukkan panjang antrian sepanjang hari

5. a

library(simmer)
library(simmer.plot)
## Loading required package: ggplot2
## 
## Attaching package: 'simmer.plot'
## The following objects are masked from 'package:simmer':
## 
##     get_mon_arrivals, get_mon_attributes, get_mon_resources
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:simmer':
## 
##     select
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
# Parameter dasar
set.seed(123)
lambda <- 5     # kedatangan per jam
mu <- 1/8       # layanan per menit
jam_buka <- 6   # jam
waktu_simulasi <- jam_buka * 60  # dalam menit
jumlah_hari <- 20
kedatangan_per_menit <- lambda / 60

# Fungsi simulasi antrian
simulasi_bank <- function(jumlah_teller) {
  env <- simmer("Bank")
  
  teller <- trajectory("traj_nasabah") %>%
    seize("teller", 1) %>%
    timeout(function() rexp(1, rate = mu)) %>%
    release("teller", 1)
  
  env %>%
    add_resource("teller", jumlah_teller) %>%
    add_generator("nasabah", teller, function() rexp(1, rate = kedatangan_per_menit)) %>%
    run(until = waktu_simulasi * jumlah_hari)
  
  return(env)
}

# Jalankan simulasi untuk 2 teller
env2 <- simulasi_bank(2)
library(simmer)
library(simmer.plot)
library(dplyr)

set.seed(123)  

# Parameter
lambda_per_hour <- 5
lambda_per_minute <- lambda_per_hour / 60  
mean_service_time <- 8  # menit
bank_open_minutes <- 360  # 6 jam
n_days <- 20
total_time <- bank_open_minutes * n_days  # waktu simulasi dalam menit

# Buat trajectory untuk nasabah
traj_nasabah <- trajectory("traj_nasabah") %>%
  seize("teller", 1) %>%
  timeout(function() rexp(1, rate = 1/mean_service_time)) %>%
  release("teller", 1)

# Buat environment simulasi
bank <- simmer("Bank Simulasi") %>%
  add_resource("teller", 2) %>%
  add_generator("nasabah", traj_nasabah, function() rexp(1, rate = lambda_per_minute)) %>%
  run(until = total_time)

# Ambil hasil kedatangan
hasil_arrival <- get_mon_arrivals(bank)

# Hitung waktu tunggu per nasabah
hasil_arrival <- hasil_arrival %>%
  mutate(waktu_tunggu = end_time - start_time - activity_time)

# Tampilkan ringkasan hasil
cat("Jumlah nasabah selama 20 hari:", nrow(hasil_arrival), "\n")
## Jumlah nasabah selama 20 hari: 573
cat("Rata-rata waktu tunggu nasabah:", round(mean(hasil_arrival$waktu_tunggu), 2), "menit\n")
## Rata-rata waktu tunggu nasabah: 0.94 menit
cat("Maksimum waktu tunggu:", round(max(hasil_arrival$waktu_tunggu), 2), "menit\n")
## Maksimum waktu tunggu: 25.19 menit
cat("Proporsi nasabah yang menunggu lebih dari 5 menit:",
    round(mean(hasil_arrival$waktu_tunggu > 5) * 100, 2), "%\n")
## Proporsi nasabah yang menunggu lebih dari 5 menit: 7.16 %

5. b

waktu_tunggu <- get_mon_arrivals(env2) %>%
  mutate(waktu_tunggu = end_time - start_time - activity_time)

mean_waktu_tunggu <- mean(waktu_tunggu$waktu_tunggu)
cat("Rata-rata waktu tunggu nasabah (2 teller):", round(mean_waktu_tunggu, 2), "menit\n")
## Rata-rata waktu tunggu nasabah (2 teller): 0.94 menit

5. c

res <- get_mon_resources(env2)
utilisasi_teller <- res %>%
  filter(resource == "teller") %>%
  summarise(utilisasi = sum(server) / (max(time) * 2))  # 2 teller

cat("Persentase waktu teller sibuk (2 teller):", round(utilisasi_teller$utilisasi * 100, 2), "%\n")
## Persentase waktu teller sibuk (2 teller): 8.32 %

5. d

env3 <- simulasi_bank(3)

# Waktu tunggu
waktu_tunggu_3 <- get_mon_arrivals(env3) %>%
  mutate(waktu_tunggu = end_time - start_time - activity_time)

mean_waktu_tunggu_3 <- mean(waktu_tunggu_3$waktu_tunggu)
cat("Rata-rata waktu tunggu nasabah (3 teller):", round(mean_waktu_tunggu_3, 2), "menit\n")
## Rata-rata waktu tunggu nasabah (3 teller): 0.16 menit
# Utilisasi teller
res3 <- get_mon_resources(env3)
utilisasi_teller_3 <- res3 %>%
  filter(resource == "teller") %>%
  summarise(utilisasi = sum(server) / (max(time) * 3))  # 3 teller

cat("Persentase waktu teller sibuk (3 teller):", round(utilisasi_teller_3$utilisasi * 100, 2), "%\n")
## Persentase waktu teller sibuk (3 teller): 6.56 %

Menambah 1 teller memang membuat waktu tunggu nasabah sangat singkat, hanya 0,16 menit, namun tingkat kesibukan teller turun drastis menjadi 6,56%. Ini menunjukkan bahwa teller tambahan jarang digunakan sehingga tidak efisien secara operasional. Dengan kata lain, meskipun pelayanan jadi lebih cepat, penambahan teller tidak diperlukan karena dua teller sudah cukup untuk menangani jumlah nasabah yang ada. ### 5.e

library(simmer)
library(dplyr)
library(ggplot2)

# Simulasi ulang jika belum ada data
set.seed(123)

lambda_per_hour <- 5
lambda_per_minute <- lambda_per_hour / 60
mean_service_time <- 8
menit_per_hari <- 360
n_days <- 20
total_time <- menit_per_hari * n_days

# Trajectory dan simulasi
traj <- trajectory() %>%
  seize("teller", 1) %>%
  timeout(function() rexp(1, 1 / mean_service_time)) %>%
  release("teller", 1)

env <- simmer("Bank") %>%
  add_resource("teller", 2) %>%
  add_generator("nasabah", traj, function() rexp(1, lambda_per_minute)) %>%
  run(until = total_time)

# Data panjang antrian
queue_data <- get_mon_resources(env) %>%
  filter(resource == "teller") %>%
  mutate(hari = floor(time / menit_per_hari) + 1,
         menit_dalam_hari = floor(time %% menit_per_hari)) %>%
  group_by(hari, menit_dalam_hari) %>%
  summarise(queue = mean(queue), .groups = "drop")

# Rata-rata dan rentang per menit
summary_queue <- queue_data %>%
  group_by(minut = menit_dalam_hari) %>%
  summarise(
    avg_queue = mean(queue),
    min_queue = min(queue),
    max_queue = max(queue)
  )

# Plot final
ggplot(summary_queue, aes(x = minut)) +
  geom_ribbon(aes(ymin = min_queue, ymax = max_queue), fill = "lightblue", alpha = 0.4) +
  geom_line(aes(y = avg_queue), color = "darkblue", size = 1) +
  labs(
    title = "Rata-rata Panjang Antrian Teller Sepanjang Hari",
    subtitle = "Selama 20 Hari Simulasi, Bank Buka 6 Jam per Hari",
    x = "Menit ke- (dalam 1 hari kerja)", y = "Jumlah Antrian"
  ) +
  theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

6 (Simulasi Toko Kelontong) Bu Sari memiliki toko kelontong yang menjual beras dengan kondisi sebagai berikut.

  1. Simulasikan stok beras selama 60 hari
  2. Hitung berapa kali toko kehabisan stok
  1. Coba berbagai strategi: pesan 40, 50, atau 60 karung. Manakah yang terbaik? (
  1. Coba ubah frekuensi pesan: setiap 3, 5, atau 7 hari. Manakah yang optimal?
  2. Buat grafik pergerakan stok dan rekomendasi untuk Bu Sari.
library(data.table)
## 
## Attaching package: 'data.table'
## The following objects are masked from 'package:dplyr':
## 
##     between, first, last
library(ggplot2)

set.seed(123)

# Fungsi simulasi stok beras
simulasi_toko <- function(hari = 60, 
                          stok_awal = 100, 
                          frekuensi_pesan = 5, 
                          jumlah_pesan = 50) {
  
  dt <- data.table(
    hari = 1:hari,
    penjualan = sample(8:15, size = hari, replace = TRUE),
    stok = 0,
    kehabisan = 0,
    pesanan = 0
  )
  
  stok_sekarang <- stok_awal
  rugi_total <- 0
  
  for (i in 1:hari) {
    # Penjualan hari ini (maks stok tersedia)
    jual <- min(stok_sekarang, dt$penjualan[i])
    
    # Update stok setelah penjualan
    stok_sekarang <- stok_sekarang - jual
    
    # Catat jika stok habis (kehilangan untung)
    if (dt$penjualan[i] > jual) {
      dt$kehabisan[i] <- dt$penjualan[i] - jual
      rugi_total <- rugi_total + dt$kehabisan[i] * 50000
    }
    
    # Cek pemesanan ulang setiap frekuensi_pesan hari
    if (i %% frekuensi_pesan == 0) {
      stok_sekarang <- stok_sekarang + jumlah_pesan
      dt$pesanan[i] <- jumlah_pesan
    }
    
    # Simpan stok akhir hari ini
    dt$stok[i] <- stok_sekarang
  }
  
  list(data = dt, rugi = rugi_total)
}

6. a dan b

result_default <- simulasi_toko()
dt_default <- result_default$data

print(paste("Kehabisan stok terjadi sebanyak:", sum(dt_default$kehabisan > 0), "hari"))
## [1] "Kehabisan stok terjadi sebanyak: 3 hari"
print(paste("Total rugi karena kehabisan stok: Rp", format(result_default$rugi, big.mark = ",")))
## [1] "Total rugi karena kehabisan stok: Rp 9e+05"

6. c

strategi_pesan <- data.table(jumlah_pesan = c(40, 50, 60))

strategi_pesan[, `:=`(
  kehabisan_hari = 0,
  total_rugi = 0
)]

for (i in 1:nrow(strategi_pesan)) {
  res <- simulasi_toko(jumlah_pesan = strategi_pesan$jumlah_pesan[i])
  strategi_pesan$kehabisan_hari[i] <- sum(res$data$kehabisan > 0)
  strategi_pesan$total_rugi[i] <- res$rugi
}

print(strategi_pesan)
##    jumlah_pesan kehabisan_hari total_rugi
##           <num>          <num>      <num>
## 1:           40             17    8350000
## 2:           50              9    3950000
## 3:           60              0          0

Dari hasil simulasi terlihat bahwa memesan 60 karung setiap kali pesanan benar-benar membuat stok tetap aman tanpa pernah kehabisan, sehingga kerugian akibat kehilangan penjualan juga nol. Namun, memesan 60 karung berarti menyiapkan stok lebih banyak yang mungkin berisiko kelebihan stok atau biaya penyimpanan. Memesan 50 karung sudah cukup baik karena hanya terjadi 9 hari kehabisan stok dengan kerugian yang jauh lebih kecil dibandingkan memesan 40 karung, yang kehabisan stok sampai 17 hari dan rugi hampir dua kali lipat. Jadi, dari sisi menghindari rugi dan menjaga stok, memesan 50 atau 60 karung adalah pilihan yang lebih efisien, dengan 60 karung sebagai pilihan paling aman jika tidak masalah dengan jumlah stok besar.

6.d

strategi_frekuensi <- data.table(frekuensi_pesan = c(3, 5, 7))

strategi_frekuensi[, `:=`(
  kehabisan_hari = 0,
  total_rugi = 0
)]

for (i in 1:nrow(strategi_frekuensi)) {
  res <- simulasi_toko(frekuensi_pesan = strategi_frekuensi$frekuensi_pesan[i])
  strategi_frekuensi$kehabisan_hari[i] <- sum(res$data$kehabisan > 0)
  strategi_frekuensi$total_rugi[i] <- res$rugi
}

print(strategi_frekuensi)
##    frekuensi_pesan kehabisan_hari total_rugi
##              <num>          <num>      <num>
## 1:               3              0          0
## 2:               5              2     700000
## 3:               7             17    8700000

Dari hasil simulasi terlihat bahwa frekuensi pesan setiap 3 hari adalah yang paling optimal karena tidak terjadi kehabisan stok sama sekali dan kerugiannya nol. Jika frekuensi pesan setiap 5 hari, masih ada sedikit hari kehabisan stok dengan kerugian yang relatif kecil, sementara frekuensi setiap 7 hari menyebabkan banyak hari kehabisan stok dan kerugian yang besar. Jadi, memesan lebih sering dengan interval 3 hari jelas lebih efektif untuk menjaga stok dan meminimalkan kerugian.

6. e

ggplot(dt_default, aes(x = hari, y = stok)) +
  geom_line(color = "pink", size = 1) +
  geom_point(data = dt_default[pesanan > 0], aes(x = hari, y = stok), color = "red", size = 3) +
  labs(title = "Pergerakan Stok Beras Selama 60 Hari",
       subtitle = "Tanda merah = hari pemesanan stok",
       x = "Hari", y = "Jumlah Stok Karung") +
  theme_minimal()

Bu Sari sebaiknya mempertahankan strategi pemesanan stok secara rutin dengan interval waktu yang tetap, seperti yang terlihat pada grafik, yaitu setiap 5 hari sekali memesan 50 karung. Cara ini mampu menjaga stok tetap tersedia tanpa kehabisan, sekaligus memudahkan pengelolaan stok agar tidak menumpuk terlalu banyak. Pemesanan berkala ini juga menghindarkan dari kerugian akibat kehabisan stok, sehingga bisnis tetap berjalan lancar dan efisien. Jika ingin lebih optimal, Bu Sari bisa mempertimbangkan untuk mengevaluasi jumlah dan frekuensi pesanan berdasarkan pola penjualan yang ada agar stok lebih stabil dan risiko kerugian semakin kecil.