\[ 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
# 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.
# 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
\[ 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
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 %
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
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 %
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.
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)
}
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"
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.
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.
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.