# Load library yang diperlukan
library(ggplot2)
library(dplyr)
##
## 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(knitr)
# Set seed untuk reproducibility
set.seed(123)
# Parameter simulasi
n_sim <- 10000
# Generate 10.000 angka random dari distribusi Uniform(0,1)
x_uniform <- runif(n_sim, min = 0, max = 1)
# Hitung X²
x_squared <- x_uniform^2
# Hitung E[X²] dari simulasi
e_x_squared_sim <- mean(x_squared)
# Nilai teoritis E[X²] untuk U(0,1)
# E[X²] = ∫₀¹ x² dx = [x³/3]₀¹ = 1/3
e_x_squared_theory <- 1/3
# Tampilkan hasil
cat("HASIL SIMULASI E[X²]:\n")
## HASIL SIMULASI E[X²]:
cat("- Nilai simulasi :", round(e_x_squared_sim, 6), "\n")
## - Nilai simulasi : 0.32974
cat("- Nilai teoritis :", round(e_x_squared_theory, 6), "\n")
## - Nilai teoritis : 0.333333
cat("- Selisih :", round(abs(e_x_squared_sim - e_x_squared_theory), 6), "\n")
## - Selisih : 0.003593
cat("- Akurasi (%) :", round((1 - abs(e_x_squared_sim - e_x_squared_theory)/e_x_squared_theory) * 100, 2), "%\n")
## - Akurasi (%) : 98.92 %
# Plot histogram X²
hist(x_squared, breaks = 50,
main = "Histogram X² untuk X ~ U(0,1)",
xlab = "X²", ylab = "Frekuensi",
col = "cyan3", border = "black")
abline(v = e_x_squared_sim, col = "red", lwd = 2, lty = 2)
legend("topright",
legend = paste("E[X²] =", round(e_x_squared_sim, 4)),
col = "red", lty = 2, lwd = 2)
# Parameter distribusi
mu <- 100
sigma <- 15
sample_sizes <- c(100, 1000, 10000)
# Inisialisasi tabel hasil
results_normal <- data.frame(
n = sample_sizes,
mean_sample = numeric(3),
sd_sample = numeric(3),
se_mean = numeric(3),
error_mean = numeric(3),
error_sd = numeric(3)
)
# Set layout untuk plot
par(mfrow = c(2, 2))
for(i in 1:length(sample_sizes)) {
n <- sample_sizes[i]
# Generate sampel
sample_data <- rnorm(n, mean = mu, sd = sigma)
# Hitung statistik
sample_mean <- mean(sample_data)
sample_sd <- sd(sample_data)
se_mean <- sample_sd / sqrt(n)
# Hitung error dari nilai teoritis
error_mean <- abs(sample_mean - mu)
error_sd <- abs(sample_sd - sigma)
# Simpan hasil
results_normal[i, "mean_sample"] <- sample_mean
results_normal[i, "sd_sample"] <- sample_sd
results_normal[i, "se_mean"] <- se_mean
results_normal[i, "error_mean"] <- error_mean
results_normal[i, "error_sd"] <- error_sd
# Plot histogram
hist(sample_data, breaks = 30,
main = paste("N(100,15²) dengan n =", n),
xlab = "Nilai", ylab = "Frekuensi",
col = "darkturquoise", border = "black")
abline(v = sample_mean, col = "red", lwd = 2)
abline(v = mu, col = "blue", lwd = 2, lty = 2)
legend("topright",
legend = c(paste("x̄ =", round(sample_mean, 2)),
paste("μ =", mu)),
col = c("red", "blue"), lty = c(1, 2), lwd = 2)
}
par(mfrow = c(1, 1))
# Tampilkan tabel hasil
cat("RINGKASAN HASIL SIMULASI NORMAL:\n")
## RINGKASAN HASIL SIMULASI NORMAL:
print(kable(results_normal, digits = 4,
col.names = c("n", "x̄", "s", "SE(x̄)", "Error x̄", "Error s")))
##
##
## | n| x̄| s| SE(x̄)| Error x̄| Error s|
## |-----:|--------:|-------:|------:|-------:|-------:|
## | 100| 101.0205| 13.3411| 1.3341| 1.0205| 1.6589|
## | 1000| 100.4924| 14.9238| 0.4719| 0.4924| 0.0762|
## | 10000| 99.9738| 15.0465| 0.1505| 0.0262| 0.0465|
cat("\nANALISIS LAW OF LARGE NUMBERS:\n")
##
## ANALISIS LAW OF LARGE NUMBERS:
for(i in 1:3) {
cat("- n =", sample_sizes[i],
": Error rata-rata =", round(results_normal[i, "error_mean"], 4),
", Error SD =", round(results_normal[i, "error_sd"], 4), "\n")
}
## - n = 100 : Error rata-rata = 1.0205 , Error SD = 1.6589
## - n = 1000 : Error rata-rata = 0.4924 , Error SD = 0.0762
## - n = 10000 : Error rata-rata = 0.0262 , Error SD = 0.0465
# Parameter distribusi binomial
n_trials <- 30
prob_success <- 0.25
n_simulations <- 5000
# Generate sampel dari distribusi binomial
binomial_samples <- rbinom(n_simulations, size = n_trials, prob = prob_success)
# Simulasi distribusi Binomial Bin(30, 0.25)
n_trials <- 30
prob_success <- 0.25
n_simulations <- 5000
# Generate sampel dari distribusi binomial
binomial_samples <- rbinom(n_simulations, size = n_trials, prob = prob_success)
# Hitung P(X ≥ 15) dari simulasi
prob_x_geq_15_sim <- mean(binomial_samples >= 15)
# Hitung nilai teoritis P(X ≥ 15)
prob_x_geq_15_theory <- 1 - pbinom(14, size = n_trials, prob = prob_success)
# Statistik deskriptif
mean_binomial <- mean(binomial_samples)
var_binomial <- var(binomial_samples)
sd_binomial <- sd(binomial_samples)
# Nilai teoritis
mean_theory <- n_trials * prob_success
var_theory <- n_trials * prob_success * (1 - prob_success)
cat("Hasil Simulasi Distribusi Binomial Bin(30, 0.25):\n")
## Hasil Simulasi Distribusi Binomial Bin(30, 0.25):
cat("Jumlah simulasi:", n_simulations, "\n\n")
## Jumlah simulasi: 5000
cat("Statistik Sampel:\n")
## Statistik Sampel:
cat("Rata-rata:", round(mean_binomial, 4), "(Teoritis:", mean_theory, ")\n")
## Rata-rata: 7.492 (Teoritis: 7.5 )
cat("Varians:", round(var_binomial, 4), "(Teoritis:", round(var_theory, 4), ")\n")
## Varians: 5.6635 (Teoritis: 5.625 )
cat("Simpangan baku:", round(sd_binomial, 4), "(Teoritis:", round(sqrt(var_theory), 4), ")\n\n")
## Simpangan baku: 2.3798 (Teoritis: 2.3717 )
cat("Estimasi Probabilitas:\n")
## Estimasi Probabilitas:
cat("P(X ≥ 15) simulasi:", round(prob_x_geq_15_sim, 6), "\n")
## P(X ≥ 15) simulasi: 0.0046
cat("P(X ≥ 15) teoritis:", round(prob_x_geq_15_theory, 6), "\n")
## P(X ≥ 15) teoritis: 0.00275
cat("Selisih:", round(abs(prob_x_geq_15_sim - prob_x_geq_15_theory), 6), "\n")
## Selisih: 0.00185
# Plot histogram
hist(binomial_samples, breaks = 0:30,
main = "Histogram Distribusi Binomial Bin(30, 0.25)",
xlab = "Nilai X", ylab = "Frekuensi",
col = "darkmagenta", border = "black")
abline(v = 15, col = "red", lwd = 2, lty = 2)
abline(v = mean_binomial, col = "blue", lwd = 2)
legend("topright",
legend = c("X = 15", paste("x̄ =", round(mean_binomial, 2))),
col = c("red", "blue"), lty = c(2, 1), lwd = 2)
# Tabel probabilitas untuk beberapa nilai
prob_table <- data.frame(
X = 10:20,
P_X_eq = dbinom(10:20, size = n_trials, prob = prob_success),
P_X_leq = pbinom(10:20, size = n_trials, prob = prob_success),
P_X_geq = 1 - pbinom(9:19, size = n_trials, prob = prob_success)
)
cat("\nTabel Probabilitas Teoritis untuk beberapa nilai:\n")
##
## Tabel Probabilitas Teoritis untuk beberapa nilai:
print(kable(prob_table, digits = 6,
col.names = c("X", "P(X=x)", "P(X≤x)", "P(X≥x)")))
##
##
## | X| P(X=x)| P(X≤x)| P(X≥x)|
## |--:|--------:|--------:|--------:|
## | 10| 0.090865| 0.894272| 0.196593|
## | 11| 0.055070| 0.949342| 0.105728|
## | 12| 0.029065| 0.978406| 0.050658|
## | 13| 0.013414| 0.991821| 0.021594|
## | 14| 0.005430| 0.997250| 0.008179|
## | 15| 0.001931| 0.999181| 0.002750|
## | 16| 0.000603| 0.999784| 0.000819|
## | 17| 0.000166| 0.999950| 0.000216|
## | 18| 0.000040| 0.999990| 0.000050|
## | 19| 0.000008| 0.999998| 0.000010|
## | 20| 0.000002| 1.000000| 0.000002|
\[ 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.
# Function untuk menghitung simpangan baku
calculate_std_dev <- function(x) {
n <- length(x)
if(n <= 1) {
stop("Sampel harus memiliki lebih dari 1 observasi")
}
# Hitung rata-rata
x_bar <- mean(x)
# Hitung sum of squares
sum_squares <- sum((x - x_bar)^2)
# Hitung simpangan baku sampel
s <- sqrt(sum_squares / (n - 1))
return(s)
}
# Parameter data
n_data <- 1000
mean_given <- 89
sd_given <- 10
# Generate data normal
normal_data <- rnorm(n_data, mean = mean_given, sd = sd_given)
# Hitung simpangan baku
s_custom <- calculate_std_dev(normal_data)
s_builtin <- sd(normal_data)
# Statistik deskriptif
mean_data <- mean(normal_data)
median_data <- median(normal_data)
var_data <- var(normal_data)
cat("HASIL ANALISIS DATA NORMAL (n=1000, μ=89, σ=10):\n")
## HASIL ANALISIS DATA NORMAL (n=1000, μ=89, σ=10):
cat("Parameter teoritis: μ =", mean_given, ", σ =", sd_given, "\n\n")
## Parameter teoritis: μ = 89 , σ = 10
cat("STATISTIK SAMPEL:\n")
## STATISTIK SAMPEL:
cat("- Rata-rata : ", round(mean_data, 4), "\n")
## - Rata-rata : 88.7851
cat("- Median : ", round(median_data, 4), "\n")
## - Median : 88.7757
cat("- Varians : ", round(var_data, 4), "\n\n")
## - Varians : 99.6573
cat("PERBANDINGAN SIMPANGAN BAKU:\n")
## PERBANDINGAN SIMPANGAN BAKU:
cat("- Function custom : ", round(s_custom, 6), "\n")
## - Function custom : 9.982849
cat("- Function R built-in : ", round(s_builtin, 6), "\n")
## - Function R built-in : 9.982849
cat("- Selisih : ", round(abs(s_custom - s_builtin), 10), "\n")
## - Selisih : 0
cat("- Parameter teoritis : ", sd_given, "\n")
## - Parameter teoritis : 10
cat("- Validasi function : ", ifelse(abs(s_custom - s_builtin) < 1e-10, "✓ BENAR", "✗ ERROR"), "\n")
## - Validasi function : ✓ BENAR
HASIL ANALISIS DATA NORMAL (n=1000, μ=89, σ=10): Parameter teoritis: μ = 89 , σ = 10
STATISTIK SAMPEL: - Rata-rata : 89.1855 - Median : 89.1614 - Varians : 101.3548
PERBANDINGAN SIMPANGAN BAKU: - Function custom : 10.06751 - Function R built-in : 10.06751 - Selisih : 0 - Parameter teoritis : 10 - Validasi function : ✓ BENAR
# Setup plot
par(mfrow = c(2, 2))
# 1. Histogram
hist(normal_data, breaks = 40,
main = "Histogram Data Normal",
xlab = "Nilai", ylab = "Frekuensi",
col = "deepskyblue2", border = "black")
abline(v = mean_data, col = "red", lwd = 2)
abline(v = mean_given, col = "blue", lwd = 2, lty = 2)
legend("topright",
legend = c(paste("x̄ =", round(mean_data, 2)),
paste("μ =", mean_given)),
col = c("red", "blue"), lty = c(1, 2), lwd = 2)
# 2. Q-Q plot
qqnorm(normal_data, main = "Q-Q Plot vs Normal")
qqline(normal_data, col = "red")
# 3. Box plot
boxplot(normal_data, main = "Box Plot Data",
ylab = "Nilai", col = "aquamarine2")
# 4. Density plot
plot(density(normal_data), main = "Kurva Densitas",
xlab = "Nilai", ylab = "Densitas", lwd = 2)
curve(dnorm(x, mean = mean_given, sd = sd_given),
add = TRUE, col = "red", lwd = 2, lty = 2)
legend("topright",
legend = c("Densitas Sampel", "Densitas Teoritis"),
col = c("black", "red"), lty = c(1, 2), lwd = 2)
par(mfrow = c(1, 1))
# Test normalitas
shapiro_test <- shapiro.test(normal_data)
cat("\nTEST NORMALITAS (Shapiro-Wilk):\n")
##
## TEST NORMALITAS (Shapiro-Wilk):
cat("- W statistic : ", round(shapiro_test$statistic, 6), "\n")
## - W statistic : 0.99909
cat("- p-value : ", format(shapiro_test$p.value, scientific = TRUE), "\n")
## - p-value : 9.155289e-01
cat("- Kesimpulan : ", ifelse(shapiro_test$p.value > 0.05,
"Data mengikuti distribusi normal (α = 0.05)",
"Data tidak mengikuti distribusi normal (α = 0.05)"), "\n")
## - Kesimpulan : Data mengikuti distribusi normal (α = 0.05)
Kesimpulan : Data mengikuti distribusi normal (α = 0.05)
Sebuah bank memiliki 2 teller dengan kondisi sebagai berikut:
(a) Buatlah simulasi antrian selama 20 hari kerja
(b) Hitung berapa lama rata-rata nasabah menunggu
(c) Hitung berapa persen waktu teller sibuk bekerja
(d) Bandingkan jika bank menambah 1 teller lagi — apakah lebih efisien?
(e) Buat grafik yang menunjukkan panjang antrian sepanjang hari
# Simulasi Sistem Antrian Bank
library(ggplot2)
library(dplyr)
library(knitr)
# Set seed untuk reproducibility
set.seed(123)
# Parameter sistem antrian
lambda_arrival <- 5/60 # 5 orang per jam = 5/60 per menit
mu_service <- 1/8 # rata-rata 8 menit per pelayanan = 1/8 per menit
bank_hours <- 6 * 60 # 6 jam = 360 menit
num_days <- 20 # simulasi 20 hari kerja
# Function untuk simulasi antrian satu hari
simulate_bank_queue <- function(lambda, mu, total_time) {
# Generate waktu kedatangan (Poisson process)
arrival_times <- c()
current_time <- 0
while(current_time < total_time) {
# Waktu antar kedatangan (Exponential dengan rate lambda)
inter_arrival <- rexp(1, lambda)
current_time <- current_time + inter_arrival
if(current_time <= total_time) {
arrival_times <- c(arrival_times, current_time)
}
}
if(length(arrival_times) == 0) {
return(list(
num_customers = 0,
waiting_times = numeric(0),
service_times = numeric(0),
total_waiting_time = 0,
avg_waiting_time = 0,
max_queue_length = 0,
server_busy_time = 0,
utilization = 0
))
}
num_customers <- length(arrival_times)
# Generate waktu pelayanan (Exponential dengan rate mu)
service_times <- rexp(num_customers, mu)
# Simulasi proses antrian
departure_times <- numeric(num_customers)
waiting_times <- numeric(num_customers)
queue_lengths <- numeric(num_customers)
# Nasabah pertama
departure_times[1] <- arrival_times[1] + service_times[1]
waiting_times[1] <- 0
queue_lengths[1] <- 1
# Nasabah selanjutnya
for(i in 2:num_customers) {
# Waktu mulai dilayani = max(waktu datang, waktu selesai nasabah sebelumnya)
service_start <- max(arrival_times[i], departure_times[i-1])
waiting_times[i] <- max(0, departure_times[i-1] - arrival_times[i])
departure_times[i] <- service_start + service_times[i]
# Hitung panjang antrian saat nasabah i datang
queue_lengths[i] <- sum(departure_times[1:(i-1)] > arrival_times[i]) + 1
}
# Statistik hasil
total_waiting_time <- sum(waiting_times)
avg_waiting_time <- mean(waiting_times)
max_queue_length <- max(queue_lengths)
server_busy_time <- sum(service_times)
utilization <- server_busy_time / total_time * 100
return(list(
num_customers = num_customers,
arrival_times = arrival_times,
service_times = service_times,
waiting_times = waiting_times,
departure_times = departure_times,
queue_lengths = queue_lengths,
total_waiting_time = total_waiting_time,
avg_waiting_time = avg_waiting_time,
max_queue_length = max_queue_length,
server_busy_time = server_busy_time,
utilization = utilization
))
}
# (a) Simulasi antrian selama 20 hari kerja
cat("=== SIMULASI ANTRIAN BANK SELAMA 20 HARI ===\n\n")
## === SIMULASI ANTRIAN BANK SELAMA 20 HARI ===
# Inisialisasi hasil simulasi
daily_results <- list()
summary_stats <- data.frame(
Hari = 1:num_days,
Jumlah_Nasabah = numeric(num_days),
Rata_Rata_Menunggu = numeric(num_days),
Waktu_Menunggu_Total = numeric(num_days),
Panjang_Antrian_Max = numeric(num_days),
Utilisasi_Teller = numeric(num_days)
)
# Jalankan simulasi untuk setiap hari
for(day in 1:num_days) {
result <- simulate_bank_queue(lambda_arrival, mu_service, bank_hours)
daily_results[[day]] <- result
summary_stats[day, "Jumlah_Nasabah"] <- result$num_customers
summary_stats[day, "Rata_Rata_Menunggu"] <- result$avg_waiting_time
summary_stats[day, "Waktu_Menunggu_Total"] <- result$total_waiting_time
summary_stats[day, "Panjang_Antrian_Max"] <- result$max_queue_length
summary_stats[day, "Utilisasi_Teller"] <- result$utilization
}
# Tampilkan tabel hasil harian
cat("Tabel Hasil Simulasi Harian:\n")
## Tabel Hasil Simulasi Harian:
print(kable(summary_stats, digits = 2,
col.names = c("Hari", "Jml Nasabah", "Rata² Tunggu (mnt)",
"Total Tunggu (mnt)", "Max Antrian", "Utilisasi (%)")))
##
##
## | Hari| Jml Nasabah| Rata² Tunggu (mnt)| Total Tunggu (mnt)| Max Antrian| Utilisasi (%)|
## |----:|-----------:|------------------:|------------------:|-----------:|-------------:|
## | 1| 31| 27.04| 838.26| 9| 87.19|
## | 2| 31| 4.29| 133.08| 3| 57.74|
## | 3| 30| 14.65| 439.64| 5| 70.70|
## | 4| 28| 15.31| 428.82| 7| 66.64|
## | 5| 28| 4.80| 134.46| 3| 61.59|
## | 6| 37| 10.36| 383.35| 6| 81.48|
## | 7| 33| 18.57| 612.84| 7| 78.96|
## | 8| 29| 5.77| 167.21| 4| 80.89|
## | 9| 36| 20.33| 731.77| 8| 77.78|
## | 10| 31| 3.25| 100.62| 4| 69.28|
## | 11| 27| 4.80| 129.66| 4| 61.88|
## | 12| 27| 7.99| 215.73| 5| 75.45|
## | 13| 21| 3.18| 66.87| 4| 30.00|
## | 14| 30| 24.90| 747.05| 7| 84.44|
## | 15| 35| 17.39| 608.53| 6| 104.05|
## | 16| 26| 3.77| 98.15| 3| 59.48|
## | 17| 36| 7.22| 259.89| 4| 79.56|
## | 18| 33| 8.35| 275.53| 6| 59.65|
## | 19| 31| 32.59| 1010.26| 9| 79.30|
## | 20| 27| 11.75| 317.15| 7| 54.79|
# (b) Rata-rata lama nasabah menunggu
overall_waiting_times <- unlist(lapply(daily_results, function(x) x$waiting_times))
avg_waiting_overall <- mean(overall_waiting_times)
cat("\n=== ANALISIS WAKTU MENUNGGU ===\n")
##
## === ANALISIS WAKTU MENUNGGU ===
cat("Rata-rata lama nasabah menunggu:", round(avg_waiting_overall, 2), "menit\n")
## Rata-rata lama nasabah menunggu: 12.68 menit
cat("Median waktu menunggu:", round(median(overall_waiting_times), 2), "menit\n")
## Median waktu menunggu: 4.99 menit
cat("Std deviasi waktu menunggu:", round(sd(overall_waiting_times), 2), "menit\n")
## Std deviasi waktu menunggu: 16.63 menit
cat("Waktu menunggu maksimum:", round(max(overall_waiting_times), 2), "menit\n")
## Waktu menunggu maksimum: 80.83 menit
# (c) Persentase waktu teller sibuk bekerja
avg_utilization <- mean(summary_stats$Utilisasi_Teller)
cat("\n=== ANALISIS UTILISASI TELLER ===\n")
##
## === ANALISIS UTILISASI TELLER ===
cat("Rata-rata persentase waktu teller sibuk:", round(avg_utilization, 2), "%\n")
## Rata-rata persentase waktu teller sibuk: 71.04 %
cat("Utilisasi minimum:", round(min(summary_stats$Utilisasi_Teller), 2), "%\n")
## Utilisasi minimum: 30 %
cat("Utilisasi maksimum:", round(max(summary_stats$Utilisasi_Teller), 2), "%\n")
## Utilisasi maksimum: 104.05 %
# (d) Perbandingan efisiensi 1 vs 2 teller
cat("\n=== PERBANDINGAN 1 TELLER vs 2 TELLER ===\n")
##
## === PERBANDINGAN 1 TELLER vs 2 TELLER ===
# Simulasi dengan 2 teller (simplified model)
simulate_two_tellers <- function(lambda, mu, total_time) {
# Generate kedatangan
arrival_times <- c()
current_time <- 0
while(current_time < total_time) {
inter_arrival <- rexp(1, lambda)
current_time <- current_time + inter_arrival
if(current_time <= total_time) {
arrival_times <- c(arrival_times, current_time)
}
}
if(length(arrival_times) == 0) {
return(list(avg_waiting_time = 0, utilization = 0))
}
num_customers <- length(arrival_times)
service_times <- rexp(num_customers, mu)
# Simulasi 2 teller (pilih yang lebih cepat selesai)
teller1_free <- 0
teller2_free <- 0
waiting_times <- numeric(num_customers)
for(i in 1:num_customers) {
# Pilih teller yang lebih cepat tersedia
if(teller1_free <= teller2_free) {
waiting_times[i] <- max(0, teller1_free - arrival_times[i])
teller1_free <- max(arrival_times[i], teller1_free) + service_times[i]
} else {
waiting_times[i] <- max(0, teller2_free - arrival_times[i])
teller2_free <- max(arrival_times[i], teller2_free) + service_times[i]
}
}
total_service_time <- sum(service_times)
utilization <- (total_service_time / 2) / total_time * 100 # 2 teller
return(list(
avg_waiting_time = mean(waiting_times),
utilization = utilization
))
}
# Simulasi perbandingan
comparison_results <- data.frame(
Skenario = c("1 Teller", "2 Teller"),
Rata_Tunggu = numeric(2),
Utilisasi = numeric(2)
)
# 1 Teller (dari hasil sebelumnya)
comparison_results[1, "Rata_Tunggu"] <- avg_waiting_overall
comparison_results[1, "Utilisasi"] <- avg_utilization
# 2 Teller
two_teller_results <- replicate(num_days,
simulate_two_tellers(lambda_arrival, mu_service, bank_hours),
simplify = FALSE)
avg_wait_2teller <- mean(sapply(two_teller_results, function(x) x$avg_waiting_time))
avg_util_2teller <- mean(sapply(two_teller_results, function(x) x$utilization))
comparison_results[2, "Rata_Tunggu"] <- avg_wait_2teller
comparison_results[2, "Utilisasi"] <- avg_util_2teller
print(kable(comparison_results, digits = 2,
col.names = c("Skenario", "Rata² Tunggu (menit)", "Utilisasi (%)")))
##
##
## |Skenario | Rata² Tunggu (menit)| Utilisasi (%)|
## |:--------|--------------------:|-------------:|
## |1 Teller | 12.68| 71.04|
## |2 Teller | 0.87| 31.79|
# Analisis efisiensi
improvement_wait <- (avg_waiting_overall - avg_wait_2teller) / avg_waiting_overall * 100
cat("\nPerbaikan waktu tunggu dengan 2 teller:", round(improvement_wait, 1), "%\n")
##
## Perbaikan waktu tunggu dengan 2 teller: 93.2 %
if(improvement_wait > 30) {
cat("Rekomendasi: Menambah 1 teller akan SANGAT EFISIEN\n")
} else if(improvement_wait > 15) {
cat("Rekomendasi: Menambah 1 teller cukup efisien\n")
} else {
cat("Rekomendasi: Menambah teller mungkin tidak terlalu diperlukan\n")
}
## Rekomendasi: Menambah 1 teller akan SANGAT EFISIEN
# (e) Grafik panjang antrian sepanjang hari
cat("\n=== VISUALISASI HASIL SIMULASI ===\n")
##
## === VISUALISASI HASIL SIMULASI ===
# Pilih hari dengan aktivitas tinggi untuk visualisasi
busy_day <- which.max(summary_stats$Jumlah_Nasabah)
busy_day_data <- daily_results[[busy_day]]
# Plot 1: Histogram waktu menunggu
par(mfrow = c(2, 2))
hist(overall_waiting_times, breaks = 30,
main = "Distribusi Waktu Menunggu (20 hari)",
xlab = "Waktu Menunggu (menit)", ylab = "Frekuensi",
col = "violet", border = "black")
abline(v = avg_waiting_overall, col = "red", lwd = 2)
legend("topright", legend = paste("Rata-rata =", round(avg_waiting_overall, 2)),
col = "red", lwd = 2)
# Plot 2: Utilisasi teller per hari
plot(1:num_days, summary_stats$Utilisasi_Teller,
type = "b", pch = 19, col = "blue",
main = "Utilisasi Teller per Hari",
xlab = "Hari", ylab = "Utilisasi (%)",
ylim = c(0, 100))
abline(h = avg_utilization, col = "red", lty = 2)
legend("topright", legend = paste("Rata-rata =", round(avg_utilization, 1), "%"),
col = "red", lty = 2)
# Plot 3: Jumlah nasabah per hari
barplot(summary_stats$Jumlah_Nasabah,
names.arg = 1:num_days,
main = "Jumlah Nasabah per Hari",
xlab = "Hari", ylab = "Jumlah Nasabah",
col = "brown2", border = "black")
# Plot 4: Perbandingan 1 vs 2 teller
barplot(comparison_results$Rata_Tunggu,
names.arg = comparison_results$Skenario,
main = "Perbandingan Waktu Tunggu",
ylab = "Rata-rata Waktu Tunggu (menit)",
col = c("red", "darkkhaki"), border = "black")
par(mfrow = c(1, 1))
# Analisis teoritis vs simulasi
cat("\n=== PERBANDINGAN TEORITIS vs SIMULASI ===\n")
##
## === PERBANDINGAN TEORITIS vs SIMULASI ===
# Teori antrian M/M/1
rho_theory <- lambda_arrival / mu_service # Traffic intensity
avg_customers_theory <- rho_theory / (1 - rho_theory) # L
avg_wait_theory <- rho_theory / (mu_service * (1 - rho_theory)) # Wq
cat("Traffic Intensity (ρ):", round(rho_theory, 3), "\n")
## Traffic Intensity (ρ): 0.667
cat("Rata-rata jumlah nasabah dalam sistem (teoritis):", round(avg_customers_theory, 2), "\n")
## Rata-rata jumlah nasabah dalam sistem (teoritis): 2
cat("Rata-rata waktu menunggu (teoritis):", round(avg_wait_theory, 2), "menit\n")
## Rata-rata waktu menunggu (teoritis): 16 menit
cat("Rata-rata waktu menunggu (simulasi):", round(avg_waiting_overall, 2), "menit\n")
## Rata-rata waktu menunggu (simulasi): 12.68 menit
cat("Selisih teoritis vs simulasi:", round(abs(avg_wait_theory - avg_waiting_overall), 2), "menit\n")
## Selisih teoritis vs simulasi: 3.32 menit
if(rho_theory >= 0.9) {
cat("\nPERINGATAN: Sistem mendekati tidak stabil (ρ ≥ 0.9)\n")
} else if(rho_theory >= 0.7) {
cat("\nSistem cukup sibuk (ρ ≥ 0.7) - pertimbangkan menambah teller\n")
} else {
cat("\nSistem dalam kondisi normal (ρ < 0.7)\n")
}
##
## Sistem dalam kondisi normal (ρ < 0.7)
# Summary dan rekomendasi
cat("\n" %+% rep("=", 60) %+% "\n")
cat("RINGKASAN dan REKOMENDASI:\n")
## RINGKASAN dan REKOMENDASI:
cat(rep("=", 60) %+% "\n\n")
cat("1. KONDISI SAAT INI (1 Teller):\n")
## 1. KONDISI SAAT INI (1 Teller):
cat(" - Rata-rata", round(mean(summary_stats$Jumlah_Nasabah), 1), "nasabah per hari\n")
## - Rata-rata 30.4 nasabah per hari
cat(" - Waktu tunggu rata-rata:", round(avg_waiting_overall, 1), "menit\n")
## - Waktu tunggu rata-rata: 12.7 menit
cat(" - Utilisasi teller:", round(avg_utilization, 1), "%\n\n")
## - Utilisasi teller: 71 %
cat("2. DENGAN 2 TELLER:\n")
## 2. DENGAN 2 TELLER:
cat(" - Waktu tunggu akan berkurang", round(improvement_wait, 1), "%\n")
## - Waktu tunggu akan berkurang 93.2 %
cat(" - Utilisasi per teller akan lebih optimal\n\n")
## - Utilisasi per teller akan lebih optimal
cat("3. REKOMENDASI OPERASIONAL:\n")
## 3. REKOMENDASI OPERASIONAL:
if(avg_waiting_overall > 10) {
cat(" - Pertimbangkan menambah 1 teller (waktu tunggu > 10 menit)\n")
}
## - Pertimbangkan menambah 1 teller (waktu tunggu > 10 menit)
if(avg_utilization > 80) {
cat(" - Teller sangat sibuk, risiko kelelahan tinggi\n")
}
if(max(summary_stats$Panjang_Antrian_Max) > 5) {
cat(" - Antrian panjang dapat mengurangi kepuasan nasabah\n")
}
## - Antrian panjang dapat mengurangi kepuasan nasabah
cat(" - Monitor jam sibuk untuk penjadwalan staff yang optimal\n")
## - Monitor jam sibuk untuk penjadwalan staff yang optimal
=== SIMULASI ANTRIAN BANK SELAMA 20 HARI ===
Tabel Hasil Simulasi Harian:
=== ANALISIS WAKTU MENUNGGU === Rata-rata lama nasabah menunggu: 12.68 menit Median waktu menunggu: 4.99 menit Std deviasi waktu menunggu: 16.63 menit Waktu menunggu maksimum: 80.83 menit
=== ANALISIS UTILISASI TELLER === Rata-rata persentase waktu teller sibuk: 71.04 % Utilisasi minimum: 30 % Utilisasi maksimum: 104.05 %
=== PERBANDINGAN 1 TELLER vs 2 TELLER ===
Perbaikan waktu tunggu dengan 2 teller: 93.2 % Rekomendasi: Menambah 1 teller akan SANGAT EFISIEN
=== VISUALISASI HASIL SIMULASI ===
=== PERBANDINGAN TEORITIS vs SIMULASI === Traffic Intensity (ρ): 0.667 Rata-rata jumlah nasabah dalam sistem (teoritis): 2 Rata-rata waktu menunggu (teoritis): 16 menit Rata-rata waktu menunggu (simulasi): 12.68 menit Selisih teoritis vs simulasi: 3.32 menit
Bu Sari memiliki toko kelontong yang menjual beras dengan kondisi sebagai berikut:
# Load required libraries
library(ggplot2)
library(dplyr)
library(knitr)
library(gridExtra)
##
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
##
## combine
# Set seed untuk reproducibility
set.seed(123)
# Parameter simulasi
initial_stock <- 100
order_quantity <- 50
order_interval <- 5
loss_per_karung <- 50000
simulation_days <- 60
# Fungsi simulasi
simulate_store <- function(days = 60) {
# Inisialisasi
stock <- initial_stock
daily_demand <- runif(days, min = 8, max = 15)
daily_sales <- numeric(days)
daily_stock <- numeric(days)
daily_shortage <- numeric(days)
daily_loss <- numeric(days)
order_days <- seq(5, days, by = 5)
for (day in 1:days) {
# Cek apakah hari pemesanan
if (day %in% order_days) {
stock <- stock + order_quantity
}
# Permintaan hari ini
demand <- daily_demand[day]
# Penjualan aktual
if (stock >= demand) {
sales <- demand
shortage <- 0
} else {
sales <- stock
shortage <- demand - stock
}
# Update stok
stock <- stock - sales
# Simpan data
daily_sales[day] <- sales
daily_stock[day] <- stock
daily_shortage[day] <- shortage
daily_loss[day] <- shortage * loss_per_karung
}
return(data.frame(
day = 1:days,
demand = daily_demand,
sales = daily_sales,
stock = daily_stock,
shortage = daily_shortage,
loss = daily_loss
))
}
# Jalankan simulasi
simulation_result <- simulate_store(60)
# Debug: cek apakah data frame terbuat dengan benar
cat("Dimensi data frame:", dim(simulation_result), "\n")
## Dimensi data frame: 60 6
cat("Nama kolom:", names(simulation_result), "\n")
## Nama kolom: day demand sales stock shortage loss
cat("Beberapa baris pertama:\n")
## Beberapa baris pertama:
# Tampilkan 10 hari pertama dengan format yang lebih jelas
cat("=== HASIL SIMULASI 10 HARI PERTAMA ===\n")
## === HASIL SIMULASI 10 HARI PERTAMA ===
for(i in 1:min(10, nrow(simulation_result))) {
cat(sprintf("Hari %2d: Permintaan=%.1f, Penjualan=%.1f, Stok=%.1f, Kekurangan=%.1f, Kerugian=Rp%.0f\n",
simulation_result$day[i],
simulation_result$demand[i],
simulation_result$sales[i],
simulation_result$stock[i],
simulation_result$shortage[i],
simulation_result$loss[i]))
}
## Hari 1: Permintaan=10.0, Penjualan=10.0, Stok=90.0, Kekurangan=0.0, Kerugian=Rp0
## Hari 2: Permintaan=13.5, Penjualan=13.5, Stok=76.5, Kekurangan=0.0, Kerugian=Rp0
## Hari 3: Permintaan=10.9, Penjualan=10.9, Stok=65.6, Kekurangan=0.0, Kerugian=Rp0
## Hari 4: Permintaan=14.2, Penjualan=14.2, Stok=51.4, Kekurangan=0.0, Kerugian=Rp0
## Hari 5: Permintaan=14.6, Penjualan=14.6, Stok=86.8, Kekurangan=0.0, Kerugian=Rp0
## Hari 6: Permintaan=8.3, Penjualan=8.3, Stok=78.5, Kekurangan=0.0, Kerugian=Rp0
## Hari 7: Permintaan=11.7, Penjualan=11.7, Stok=66.8, Kekurangan=0.0, Kerugian=Rp0
## Hari 8: Permintaan=14.2, Penjualan=14.2, Stok=52.6, Kekurangan=0.0, Kerugian=Rp0
## Hari 9: Permintaan=11.9, Penjualan=11.9, Stok=40.7, Kekurangan=0.0, Kerugian=Rp0
## Hari 10: Permintaan=11.2, Penjualan=11.2, Stok=79.5, Kekurangan=0.0, Kerugian=Rp0
a. Simulasi Stok Beras Selama 60 Hari
# Grafik stok beras selama 60 hari
cat("\n=== MEMBUAT GRAFIK STOK ===\n")
##
## === MEMBUAT GRAFIK STOK ===
p1 <- ggplot(simulation_result, aes(x = day, y = stock)) +
geom_line(color = "darkseagreen", size = 1) +
geom_point(color = "red", size = 0.8) +
geom_vline(xintercept = seq(5, 60, by = 5), linetype = "dashed", alpha = 0.5, color = "cyan2") +
labs(title = "Simulasi Stok Beras Selama 60 Hari",
x = "Hari",
y = "Stok (karung)",
subtitle = "Garis hijau menunjukkan hari pemesanan") +
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.
print(p1)
# Statistik deskriptif stok
cat("\n=== STATISTIK STOK ===\n")
##
## === STATISTIK STOK ===
stock_stats <- summary(simulation_result$stock)
print(stock_stats)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00 11.70 31.67 33.46 46.78 89.99
cat("Stok minimum:", min(simulation_result$stock), "karung\n")
## Stok minimum: 0 karung
cat("Stok maksimum:", max(simulation_result$stock), "karung\n")
## Stok maksimum: 89.98696 karung
cat("Stok rata-rata:", round(mean(simulation_result$stock), 2), "karung\n")
## Stok rata-rata: 33.46 karung
=== MEMBUAT GRAFIK STOK ===
=== STATISTIK STOK ===
Min. 1st Qu. Median Mean 3rd Qu. Max. 0.00 16.45 33.45 34.85 51.79 90.96
b. Hitung Berapa Kali Toko Kehabisan Stok
# Hitung berapa kali kehabisan stok
shortage_days <- sum(simulation_result$shortage > 0)
shortage_events <- rle(simulation_result$shortage > 0)
shortage_periods <- sum(shortage_events$values)
cat("\n=== ANALISIS KEHABISAN STOK ===\n")
##
## === ANALISIS KEHABISAN STOK ===
cat("Jumlah hari kehabisan stok:", shortage_days, "hari\n")
## Jumlah hari kehabisan stok: 7 hari
cat("Jumlah periode kehabisan stok:", shortage_periods, "periode\n")
## Jumlah periode kehabisan stok: 6 periode
cat("Persentase hari kehabisan stok:", round(shortage_days/60*100, 1), "%\n")
## Persentase hari kehabisan stok: 11.7 %
# Detail hari-hari kehabisan stok
shortage_details <- simulation_result[simulation_result$shortage > 0, ]
if(nrow(shortage_details) > 0) {
cat("\n=== DETAIL HARI-HARI KEHABISAN STOK ===\n")
for(i in 1:nrow(shortage_details)) {
cat(sprintf("Hari %2d: Permintaan=%.1f, Kekurangan=%.1f, Kerugian=Rp%.0f\n",
shortage_details$day[i],
shortage_details$demand[i],
shortage_details$shortage[i],
shortage_details$loss[i]))
}
} else {
cat("Tidak ada hari kehabisan stok dalam simulasi ini.\n")
}
##
## === DETAIL HARI-HARI KEHABISAN STOK ===
## Hari 29: Permintaan=10.0, Kekurangan=1.2, Kerugian=Rp58712
## Hari 33: Permintaan=12.8, Kekurangan=0.9, Kerugian=Rp46100
## Hari 34: Permintaan=13.6, Kekurangan=13.6, Kerugian=Rp678414
## Hari 39: Permintaan=10.2, Kekurangan=2.6, Kerugian=Rp128410
## Hari 44: Permintaan=10.6, Kekurangan=1.0, Kerugian=Rp50040
## Hari 54: Permintaan=8.9, Kekurangan=4.7, Kerugian=Rp233016
## Hari 59: Permintaan=14.3, Kekurangan=7.8, Kerugian=Rp390177
c. Coba Berbagai Strategi: Pesan 40, 50, atau 60 Karung
# Fungsi untuk menguji strategi berbeda
test_strategy <- function(order_qty, days = 60) {
stock <- initial_stock
daily_demand <- runif(days, min = 8, max = 15)
total_shortage <- 0
total_loss <- 0
shortage_days <- 0
for (day in 1:days) {
if (day %% 5 == 0) {
stock <- stock + order_qty
}
demand <- daily_demand[day]
if (stock >= demand) {
stock <- stock - demand
} else {
shortage <- demand - stock
total_shortage <- total_shortage + shortage
total_loss <- total_loss + (shortage * loss_per_karung)
shortage_days <- shortage_days + 1
stock <- 0
}
}
return(list(
order_quantity = order_qty,
total_shortage = total_shortage,
total_loss = total_loss,
shortage_days = shortage_days,
avg_shortage_per_day = total_shortage / days
))
}
# Test berbagai strategi
strategies <- c(40, 50, 60)
strategy_results <- lapply(strategies, test_strategy)
# Buat dataframe untuk perbandingan
strategy_df <- data.frame(
Strategi = paste0("Pesan_", strategies, "_karung"),
Total_Kekurangan = sapply(strategy_results, function(x) round(x$total_shortage, 2)),
Hari_Kehabisan = sapply(strategy_results, function(x) x$shortage_days),
Total_Kerugian = sapply(strategy_results, function(x) x$total_loss),
Rata2_Kekurangan = sapply(strategy_results, function(x) round(x$avg_shortage_per_day, 2))
)
# Visualisasi perbandingan strategi
p2 <- ggplot(strategy_df, aes(x = Strategi, y = Total_Kerugian)) +
geom_col(fill = "darkslategray2", alpha = 0.7) +
geom_text(aes(label = paste0("Rp ", format(Total_Kerugian, big.mark = ","))),
vjust = -0.5) +
labs(title = "Perbandingan Total Kerugian per Strategi",
x = "Strategi Pemesanan",
y = "Total Kerugian (Rp)") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
print(p2)
=== PERBANDINGAN STRATEGI PEMESANAN ===
d. Coba Ubah Frekuensi Pesan: Setiap 3, 5, atau 7 Hari
# Fungsi untuk menguji frekuensi pemesanan berbeda
test_frequency <- function(order_interval, days = 60) {
stock <- initial_stock
daily_demand <- runif(days, min = 8, max = 15)
total_shortage <- 0
total_loss <- 0
shortage_days <- 0
total_orders <- 0
for (day in 1:days) {
if (day %% order_interval == 0) {
stock <- stock + order_quantity
total_orders <- total_orders + 1
}
demand <- daily_demand[day]
if (stock >= demand) {
stock <- stock - demand
} else {
shortage <- demand - stock
total_shortage <- total_shortage + shortage
total_loss <- total_loss + (shortage * loss_per_karung)
shortage_days <- shortage_days + 1
stock <- 0
}
}
return(list(
order_interval = order_interval,
total_orders = total_orders,
total_shortage = total_shortage,
total_loss = total_loss,
shortage_days = shortage_days
))
}
# Test berbagai frekuensi
frequencies <- c(3, 5, 7)
frequency_results <- lapply(frequencies, test_frequency)
# Buat dataframe untuk perbandingan
frequency_df <- data.frame(
Frekuensi = paste0("Setiap_", frequencies, "_hari"),
Total_Pemesanan = sapply(frequency_results, function(x) x$total_orders),
Hari_Kehabisan = sapply(frequency_results, function(x) x$shortage_days),
Total_Kerugian = sapply(frequency_results, function(x) x$total_loss),
Total_Kekurangan = sapply(frequency_results, function(x) round(x$total_shortage, 2))
)
# Visualisasi perbandingan frekuensi
p3 <- ggplot(frequency_df, aes(x = Frekuensi, y = Total_Kerugian)) +
geom_col(fill = "aquamarine2", alpha = 0.7) +
geom_text(aes(label = paste0("Rp ", format(Total_Kerugian, big.mark = ","))),
vjust = -0.5) +
labs(title = "Perbandingan Total Kerugian per Frekuensi Pemesanan",
x = "Frekuensi Pemesanan",
y = "Total Kerugian (Rp)") +
theme_minimal()
print(p3)
=== PERBANDINGAN FREKUENSI PEMESANAN ===
e. Grafik Pergerakan Stok dan Rekomendasi untuk Bu Sari
# Grafik komprehensif pergerakan stok
p4 <- ggplot(simulation_result, aes(x = day)) +
geom_line(aes(y = stock, color = "Stok"), size = 1) +
geom_line(aes(y = demand, color = "Permintaan"), size = 0.8) +
geom_point(data = simulation_result[simulation_result$shortage > 0, ],
aes(y = 0), color = "red", size = 2, shape = 17) +
geom_vline(xintercept = seq(5, 60, by = 5), linetype = "dashed", alpha = 0.3) +
scale_color_manual(values = c("Stok" = "blue", "Permintaan" = "orange")) +
labs(title = "Pergerakan Stok vs Permintaan Selama 60 Hari",
x = "Hari",
y = "Jumlah (karung)",
color = "Legenda",
subtitle = "Segitiga merah = hari kehabisan stok, garis vertikal = hari pemesanan") +
theme_minimal() +
theme(legend.position = "bottom")
print(p4)
# Summary statistics
total_loss_original <- sum(simulation_result$loss)
total_shortage_original <- sum(simulation_result$shortage)
=== RINGKASAN HASIL SIMULASI ===
Strategi saat ini (pesan 50 karung setiap 5 hari):
=== REKOMENDASI UNTUK BU SARI ===
Berdasarkan analisis simulasi:
STRATEGI JUMLAH PEMESANAN:
Strategi terbaik: Pesan 60 karung Alasan: Menghasilkan kerugian terendah
STRATEGI FREKUENSI PEMESANAN:
Frekuensi terbaik: Setiap 3 hari Alasan: Meminimalkan risiko kehabisan stok
KESIMPULAN: