Penulis: Anthony Ebert, Paul Wu, Kerrie Mengersen, Fabrizio Ruggeri
Sumber: https://stat.paperswithcode.com/paper/computationally-efficient-simulation-of
Jaringan besar dari sistem antrian digunakan untuk memodelkan berbagai sistem dunia nyata yang penting seperti klaster MapReduce, server web, rumah sakit, pusat panggilan (call center), dan terminal penumpang bandara. Untuk memodelkan sistem-sistem ini secara akurat, kita perlu mengestimasi parameter antrian berdasarkan data. Sayangnya, untuk banyak jaringan antrian, belum ada cara yang jelas untuk melakukan inferensi parameter dari data. Approximate Bayesian Computation (Perhitungan Bayesian Mendekati) dapat menjadi pendekatan yang mudah untuk mengestimasi parameter jaringan semacam ini—jika saja kita dapat mensimulasikan data dengan cukup cepat.
Kami memperkenalkan metode simulasi yang efisien secara komputasi untuk berbagai jenis jaringan antrian umum dengan menggunakan paket R queuecomputer. Percepatan kinerja yang luar biasa, lebih dari dua orde magnitudo, diamati jika dibandingkan dengan paket discrete event simulation (DES) populer seperti simmer dan simpy. Kami mereplikasi keluaran dari paket-paket tersebut untuk memvalidasi queuecomputer.
Paket ini bersifat modular dan terintegrasi dengan baik bersama paket R populer dplyr. Jaringan antrian yang kompleks dengan topologi tandem, paralel, dan fork/join dapat dengan mudah dibangun dengan menggabungkan kedua paket tersebut. Kami menunjukkan penggunaan paket ini melalui dua contoh: pusat panggilan dan terminal bandara.
Kata kunci: antrian, teori antrian, simulasi kejadian diskrit, riset operasi, approximate Bayesian computation, R
Antrian yang kita temui dalam kehidupan sehari-hari—di mana pelanggan menunggu dalam antrean untuk dilayani oleh pelayan—merupakan analogi yang berguna untuk banyak proses lainnya. Disebut analogi karena istilah pelanggan dalam konteks ini bisa merujuk pada: pekerjaan MapReduce (Lin et al., 2013); pasien di rumah sakit (Takagi et al., 2016); barang dalam sistem manufaktur (Dallery & Gershwin, 1992); panggilan masuk ke pusat layanan (Gans et al., 2003); kontainer pengiriman di pelabuhan (Kozan, 1997); bahkan tugas kognitif (Cao, 2013). Demikian pula, istilah pelayan bisa merujuk pada: klaster komputasi, tenaga medis, mesin produksi, atau petugas layanan pelanggan.
Sistem antrian juga bisa dihubungkan satu sama lain membentuk jaringan antrian. Jaringan ini bisa digunakan untuk membangun model proses-proses seperti: penyediaan layanan internet (Sutton & Jordan, 2011), fasilitasi penumpang di bandara internasional (Wu & Mengersen, 2013), serta evakuasi darurat (Van Woensel & Vandaele, 2007). Jelas bahwa sistem dan jaringan antrian sangat berguna dalam memahami berbagai sistem nyata yang penting.
Untuk mengukur performa dari suatu sistem antrian, sering kali diperlukan simulasi. Biasanya, antrian disimulasikan menggunakan pendekatan Simulasi Kejadian Diskrit (Discrete Event Simulation/DES) (Rios Insua et al., 2012). Dalam DES, perubahan keadaan bersifat tidak kontinu dan dipicu oleh sejumlah peristiwa (events) pada waktu-waktu tertentu. Bila kejadian terjadi hanya bergantung pada waktu simulasi, maka peristiwa tersebut determined; jika tidak, disebut contingent (Nance, 1981).
Beberapa perangkat lunak populer untuk DES tersedia dalam berbagai bahasa pemrograman, termasuk: simmer untuk R (Ucar & Smeets, 2016), simpy untuk Python (Lunsdorf & Scherfke, 2013), JMT untuk Java (Bertoli et al., 2009).
Bahkan, beberapa paket DES begitu ekspresif sehingga dianggap sebagai bahasa pemrograman tersendiri; contohnya, bahasa Simula (Dahl & Nygaard, 1966) adalah contoh literal dari hal ini.
Paket queuecomputer (Ebert, 2016) mengimplementasikan suatu algoritma yang dapat diterapkan secara luas pada berbagai sistem dan jaringan antrian. Pendekatan ini jauh lebih efisien secara komputasi dibandingkan pendekatan DES konvensional. Algoritma ini disebut sebagai Queue Departure Computation (QDC). Efisiensi komputasi menjadi sangat penting, karena jika kita dapat menyimulasikan sistem antrian dengan cepat, maka simulasi tersebut dapat disisipkan dalam algoritma Approximate Bayesian Computation (ABC) (Sunnåker et al., 2013), sehingga memungkinkan estimasi parameter dari model antrian yang kompleks secara lebih mudah.
Struktur artikel ini adalah sebagai berikut:
Bagian 2: Tinjauan pustaka tentang teori antrian dan notasi yang digunakan;
Bagian 3: Presentasi algoritma QDC dan perbandingannya dengan DES;
Bagian 4: Demonstrasi penggunaan paket queuecomputer;
Bagian 5: Penjelasan detail implementasi dan penggunaannya;
Bagian 6: Validasi paket dengan mereplikasi hasil dari simpy dan simmer;
Bagian 7: Benchmark performa paket dibandingkan dengan dua paket lainnya;
Bagian 8: Studi kasus simulasi pusat panggilan (call centre) dan terminal bandara.
Teori antrian adalah studi tentang sistem antrian dan berasal dari karya Agner Krarup Erlang pada tahun 1909 untuk merencanakan kebutuhan infrastruktur sistem telepon di Denmark (Thomopoulos 2012, hlm. 2).
Sebuah sistem antrian didefinisikan sebagai berikut.
Setiap pelanggan \(i = 1, 2, \dots\)
memiliki waktu kedatangan \(a_i\) (atau
secara ekuivalen waktu antar-kedatangan \(\delta_i = a_i - a_{i-1}\), dengan \(a_0 = 0\)), serta sejumlah waktu yang
mereka butuhkan bersama pelayan, yang disebut waktu
pelayanan \(s_i\).
Secara umum, seorang pelayan hanya dapat melayani satu pelanggan
dalam satu waktu.
Pelayan yang sedang melayani pelanggan lain dikatakan tidak
tersedia, sedangkan pelayan yang tidak sedang melayani siapa
pun disebut tersedia.
Jika semua pelayan tidak tersedia saat seorang pelanggan datang, maka
pelanggan harus menunggu dalam antrian sampai pelayan tersedia.
Pengantar mendalam tentang sistem antrian dapat ditemukan dalam buku-buku standar seperti Bhat (2015).
Karakteristik dari suatu sistem antrian dinyatakan dengan notasi
Kendall (1953).
Notasi ini telah diperluas menjadi enam karakteristik:
Pilihan umum untuk distribusi antar-kedatangan dan pelayanan
adalah:
- “M” untuk distribusi eksponensial dan independen
- “GI” untuk distribusi umum dan independen
- “G” untuk distribusi umum tanpa asumsi independensi
Kapasitas sistem \(C\) mengacu pada
jumlah maksimum pelanggan dalam sistem pada suatu waktu.
Pelanggan berada di dalam sistem jika mereka sedang dilayani atau
menunggu dalam antrian.
Populasi pelanggan \(n\) adalah jumlah
total pelanggan, termasuk yang belum datang atau sudah pergi.
Disiplin pelayanan \(R\) mendefinisikan
bagaimana pelanggan dalam antrian dialokasikan ke pelayan yang
tersedia.
Disiplin pelayanan yang paling umum adalah first come first
serve (FCFS).
Untuk menentukan suatu sistem antrian, keenam karakteristik tersebut dituliskan secara berurutan dan dipisahkan dengan garis miring “/”.
Sistem antrian paling sederhana adalah sistem dengan distribusi eksponensial untuk waktu antar-kedatangan dan waktu pelayanan, yaitu:
dengan \(\lambda\) dan \(\mu\) sebagai parameter laju
eksponensial.
Selain itu, \(K\) diset menjadi 1,
\(C\) dan \(n\) tak hingga (\(\infty\)), dan \(R\) adalah FCFS.
Sistem ini dinyatakan dengan:
\[ \text{M}/\text{M}/1/\infty/\infty/\text{FCFS} \]
yang biasanya disingkat menjadi:
\[ \text{M}/\text{M}/1 \]
Inferensi parameter untuk sistem ini pertama kali dibahas oleh Clarke
(1957), di mana estimator diturunkan dari fungsi likelihood.
Likelihood ini kemudian digunakan oleh Muddapur (1972) untuk menurunkan
distribusi posterior bersama.
Inferensi Bayesian untuk sistem antrian dirangkum secara lengkap oleh
Rios Insua et al. (2012).
Manajer dan perencana biasanya kurang tertarik pada inferensi parameter, dan lebih tertarik pada ukuran performa seperti:
Jika \(\lambda < K\mu\), maka sistem antrian pada akhirnya akan mencapai keadaan keseimbangan dan distribusi dari ukuran performa menjadi tidak tergantung waktu.
Dalam kasus sistem \(\text{M}/\text{M}/K\), distribusi keseimbangan dari ukuran performa dapat diturunkan secara analitik, dan dapat ditemukan dalam buku teks teori antrian standar seperti Lipsky (2008) dan Thomopoulos (2012).
Sebagai contoh, probabilitas limit terdapatnya \(N\) pelanggan dalam sistem, \(P(N)\), adalah:
Probabilitas sistem kosong adalah:
\[ P(0) = \left[ \frac{(K\rho)^K}{K!(1 - \rho)} + 1 + \sum_{i = 1}^{K - 1} \frac{(K\rho)^i}{i!} \right]^{-1} \tag{1} \]
Probabilitas terdapat \(N\) pelanggan dalam sistem:
\[ P(N) = \begin{cases} P(0) \cdot \frac{(K\rho)^N}{N!}, & \text{jika } N \leq K \\ P(0) \cdot \frac{(K\rho)^N}{K! \cdot K^{N-K}}, & \text{jika tidak} \end{cases} \tag{2} \]
di mana \(\rho\), yaitu utilisasi sumber daya, didefinisikan sebagai:
\[ \rho = \frac{\lambda}{K\mu} \]
Untuk sistem \(\text{M}/\text{M}/K\), ini sama dengan jumlah harapan pelayan sibuk dibagi dengan jumlah total pelayan \(\frac{\mathbb{E}(B)}{K}\) (Cassandras dan Lafortune, 2009, hlm. 451). Jumlah harapan pelanggan dalam sistem menurut Bhat (2015) adalah:
\[ \mathbb{E}(N) = K\rho + \frac{\rho (K\rho)^K P(0)}{K!(1 - \rho)^2} \tag{3} \]
dan waktu tunggu harapan adalah:
\[ \mathbb{E}(w) = \frac{(K\rho)^K P(0)}{K!K\mu(1 - \rho)^2} \tag{4} \]
Jika parameter dari \(f_\delta\) dan \(f_s\) tidak pasti, maka kita harus menggunakan distribusi prediktif untuk memperkirakan ukuran performa. Distribusi prediktif dari ukuran performa menggunakan distribusi posterior Bayesian diturunkan oleh Armero (1994); Armero dan Bayarri (1999).
Jackson (1957) adalah salah satu yang pertama mempertimbangkan jaringan sistem antrian. Dalam jaringan Jackson terdapat himpunan sistem antrian sebanyak \(J\). Setelah pelanggan dilayani oleh sistem antrian ke-\(j\), mereka akan menuju sistem antrian lain dengan probabilitas tetap \(p_{j,k}\). Pelanggan akan meninggalkan sistem dengan probabilitas \(1 - \sum_{k=1}^{J} p_{j,k}\).
Contoh lain dari topologi jaringan antrian meliputi: - Tandem (Glynn dan Whitt, 1991) - Parallel (Hunt dan Foote, 1995) - Fork/Join (Kim dan Agrawala, 1989)
Dalam jaringan antrian tandem, pelanggan melewati serangkaian antrian secara berurutan sebelum keluar dari sistem. Contoh dunia nyata dari sistem ini termasuk terminal bandara, layanan internet, dan sistem manufaktur.
Dalam jaringan parallel, pelanggan dibagi ke dalam sistem antrian terpisah berdasarkan pasangan \((a, s)\).
Dalam jaringan fork/join, satu tugas (atau pelanggan) dibagi menjadi sejumlah sub-tugas yang harus diselesaikan oleh pelayan paralel yang berbeda. Perbedaan utama dari jaringan paralel biasa adalah bahwa tugas hanya dapat keluar dari sistem setelah semua sub-tugas mencapai titik join.
Sebagian besar model sistem antrian mengasumsikan bahwa proses antar-kedatangan dan pelayanan bersifat tidak berubah terhadap waktu (time-invariant). Namun dalam praktiknya, banyak sistem antrian di dunia nyata memiliki proses antar-kedatangan yang sangat tergantung waktu, contohnya:
Dalam kasus sistem \(\text{M}/\text{M}/1\), kita dapat menyesuaikan notasinya menjadi:
\[ \text{M}(t)/\text{M}(t)/1 \]
untuk merepresentasikan proses eksponensial di mana parameter \(\lambda(t)\) dan \(\mu(t)\) berubah terhadap waktu. Sistem antrian seperti ini disebut sebagai sistem antrian dinamis (dynamic queueing systems). Secara umum, solusi analitik tidak tersedia untuk sistem antrian dinamis (dynamic queueing systems) (Malone, 1995; Worthington, 2009).
Green, Kolesar, dan Svoronos (1991) menunjukkan bahwa
menggunakan model sistem antrian stasioner untuk
memodelkan sistem antrian dinamis akan menghasilkan kesalahan
serius, bahkan ketika penyimpangan dari kondisi stasioner
sangat kecil. Masalah ini menjadi semakin rumit ketika kita
mempertimbangkan jaringan antrian (queueing
networks).
Pemahaman tentang perilaku jangka panjang dan transien
dari sistem antrian semacam itu hanya dapat dicapai melalui metode
pendekatan (approximation methods) atau simulasi.
Pada bagian selanjutnya, kita akan menguraikan algoritma QDC (Queue Departure Computation), yaitu sebuah metode simulasi sistem antrian yang efisien secara komputasi.
QDC (Queue Departure Computation) dapat dianggap sebagai
ekstensi multiserver dari algoritma yang diperkenalkan oleh Lindley
(1952).
Untuk sistem antrian dengan satu pelayan, waktu keluarnya pelanggan
ke-\(i\) dirumuskan sebagai:
\[ d_i = \max(a_i, d_{i-1}) + s_i \]
karena pelanggan harus menunggu pelayan atau pelayan menunggu pelanggan.
Yang mengejutkan, algoritma ini (bukan makalahnya) tidak diperluas ke
sistem multi-pelayan hingga Krivulin (1994).
Namun, untuk setiap pelanggan baru \(i\), algoritma harus mencari dalam vektor
sepanjang \(i + 1\).
Akibatnya, algoritma ini tidak berskala baik, dengan
kompleksitas komputasi sebesar \(\mathcal{O}(n^2)\), di mana \(n\) adalah jumlah pelanggan.
Kin dan Chan (2010) mengadaptasi algoritma asli dari Kiefer dan Wolfowitz (1955) menjadi algoritma dengan kompleksitas \(\mathcal{O}(nK)\) untuk antrian tandem multi-pelayan dengan pemblokiran, yaitu sistem antrian G/G/K/C di mana \(C\) adalah kapasitas maksimum pelanggan dalam sistem antrian.
QDC juga dapat dilihat sebagai solusi komputasi yang efisien untuk
himpunan persamaan yang disajikan oleh Sutton dan Jordan (2011, hlm.
259) untuk sistem antrian FCFS.
Dalam sistem ini terdapat satu antrian yang dilayani oleh \(K\) pelayan tetap.
Pelanggan ke-\(i\) mengamati
serangkaian waktu:
\[ b_i = \{b_{ik} \mid k \in 1{:}K\} \]
yang merepresentasikan waktu berikutnya setiap pelayan akan
tersedia.
Pelanggan ke-\(i\) memilih pelayan
paling awal tersedia:
\[ p_i = \arg\min(b_i) \]
Maka waktu keluarnya pelanggan ke-\(i\) adalah:
\[ d_i = \max(a_i, b_{p_i}) + s_i \]
karena pelayan menunggu pelanggan atau pelanggan menunggu pelayan.
Algoritma QDC untuk jumlah pelayan tetap (Algorithm 1)
melakukan pra-urutkan (pre-sort) terhadap waktu
kedatangan.
Alih-alih menetapkan sebuah \(b_i\)
untuk setiap pelanggan \(i\) dan
membentuk matriks \(b \in \mathbb{M}^{n \times
K}\), QDC memperlakukan \(b\)
sebagai vektor berdimensi \(K\) yang diperbarui terus-menerus, mewakili
keadaan sistem.
Algoritma ini sederhana dan efisien secara komputasi.
Pada setiap iterasi, kita hanya perlu mencari nilai minimum dari
vektor \(b\) berdimensi \(K\) pada baris kode ke-8.
Dalam bahasa Discrete Event Simulation (DES), vektor
\(b\) mewakili state
dari sistem dan \(a\) (waktu
kedatangan) adalah daftar kejadian (event list) yang
sudah ditentukan.
Ini berbeda dari pendekatan DES konvensional untuk pemodelan sistem antrian, di mana panjang antrian dianggap sebagai state, dan baik \(a\) maupun \(d\) dianggap sebagai event list—di mana kejadian \(a\) bersifat deterministik dan kejadian \(d\) diperbarui terus-menerus (kontingen).
Algoritma 1 dapat mensimulasikan sistem antrian dengan bentuk:
\[ G(t)/G(t)/K/\infty/n/FCFS \]
di mana \(K\) dan \(n\) dapat dibuat sebesar apapun.
Selain itu, distribusi antar-kedatangan dan pelayanan dapat berbentuk
umum dan arbitrer, bahkan memiliki struktur
ketergantungan (dependency structure).
Karena waktu kedatangan dan pelayanan diberikan oleh
pengguna, bukan di-sampling langsung dalam algoritma,
maka QDC memisahkan proses pengambilan sampel statistik dari
proses simulasi antrian.
Dengan cara ini, pengguna bebas mensimulasikan sistem antrian dengan
bentuk distribusi \(f_{\delta}, f_{s}\)
yang sekompleks apapun, selama nilai \(K\) tetap.
Algoritma 1: QDC untuk \(K\) tetap
1: function QDC(a ∈ ℝⁿ₊, s ∈ ℝⁿ₊, K ∈ ℕ)
2: Urutkan (a, s) berdasarkan a (menaik)
3: Buat vektor p ∈ ℕⁿ
4: Buat vektor b ∈ ℝ₊^K
5: Buat vektor d ∈ ℝⁿ₊
6: Setiap elemen b_k ← 0 untuk semua k ∈ 1:K
7: for i ∈ 1:n do
8: pᵢ ← arg min(b) # indeks pelayan paling cepat tersedia
9: bₚᵢ ← max(aᵢ, bₚᵢ) + sᵢ
10: dᵢ ← bₚᵢ
11: end for
12: Kembalikan (a, d, p) ke urutan asli input a
13: return (d, p)
14: end function
Kasus Kondisional (Conditional Case)
Misalkan jumlah pelayan yang tersedia untuk digunakan oleh pelanggan
berubah sepanjang hari.
Hal ini mencerminkan situasi nyata, seperti ketika lebih banyak
pelayan dijadwalkan bertugas saat jam sibuk.
Kita nyatakan bahwa pada waktu \(t\)
tertentu, pelanggan memiliki pilihan sebanyak \(K(t)\) pelayan yang aktif dari total \(K\).
Artinya, terdapat \(K(t)\) pelayan yang
dijadwalkan aktif pada waktu \(t\).
Kita mendefinisikan istilah tertutup (closed) sebagai
kebalikan dari terbuka (open).
Kita representasikan jumlah pelayan terbuka sepanjang hari sebagai
sebuah fungsi langkah (step function).
Waktu diasumsikan sebagai bilangan real positif, dan dibagi oleh \(L\) simpul (knot) \(x = (x_1, \cdots, x_L) \in \mathbb{R}^L_+\)
ke dalam \(L + 1\) epoch:
\[(0, x_1], (x_1, x_2], \cdots, (x_L, \infty)\]
Jumlah pelayan terbuka di tiap epoch direpresentasikan oleh vektor berdimensi \(L + 1\):
\[ y = (y_1, \cdots, y_{L+1}) \in \mathbb{N}_0^{L+1} \]
Jika kita mengasumsikan bahwa tidak ada waktu pelayanan \(s_i\) yang melintasi lebih dari satu epoch, maka secara formal:
\[ \forall i, \quad s_i < \min(x_{l+1} - x_l \mid l \in 1{:}L) \tag{5} \]
maka kita hanya perlu mempertimbangkan perubahan status pada
maksimal satu simpul (knot).
Fungsi langkah ini ditentukan oleh pengguna sebagai
masukan (input) sebelum simulasi dilakukan, tidak dapat
berubah selama simulasi berlangsung, sama seperti waktu
kedatangan \(a\) dan waktu pelayanan
\(s\).
Untuk menutup pelayan ke-\(k\), kita set nilai \(b_k = \infty\) agar tidak ada pelanggan
yang dapat memilih pelayan tersebut.
Jika pelayan perlu dibuka kembali pada waktu \(t\), kita set nilai \(b_k = t\) agar pelayan tersebut dapat
digunakan.
Karena \(x\) sekarang merepresentasikan
perubahan pada vektor \(b\), maka simpul-simpul \(x\) merupakan bagian dari daftar
kejadian (event list) bersama dengan waktu kedatangan \(a\).
Seluruh daftar kejadian ini bersifat ditentukan
sebelumnya (pre-determined) dan tidak perlu diperbarui
selama simulasi berlangsung.
Algoritma ini dapat mensimulasikan sistem antrian dengan bentuk:
\[ G(t)/G(t)/K(t)/\infty/n/FCFS \]
di mana \(K(t)\) adalah jumlah pelayan terbuka yang berubah terhadap waktu.
Seperti telah disebutkan sebelumnya, algoritma ini bergantung pada Kondisi (5), namun kondisi ini tidak terlalu membatasi jika kita mempertimbangkan sistem nyata yang hanya mengalami sedikit perubahan pada \(K\) sepanjang waktu.
Perlu dicatat bahwa hasil alokasi pelayan yang direkam dalam vektor:
\[ p = (p_1, \cdots, p_n) \]
mungkin tidak mencerminkan sistem sebenarnya, karena Algoritma 2 tidak memungkinkan pengguna untuk menentukan pelayan mana yang digunakan secara spesifik.
Algoritma 2: QDC untuk \(K(t)\) (Kasus Kondisional)
1: function QDC_server_stepfun(a ∈ ℝⁿ₊, s ∈ ℝⁿ₊, x ∈ ℝ^L₊, y ∈ ℕ₀^{L+1})
2: Urutkan (a, s) berdasarkan a (menaik)
3: Tambahkan simpul terakhir: x_{L+1} ← ∞
4: Tambahkan y_{L+2} ← 1
5: K ← maksimum nilai dari vektor y
6: Buat vektor b ∈ ℝ₊^K
7: Setiap elemen b_k ← ∞ untuk semua k ∈ 1:K # semua pelayan awalnya ditutup
8: Setiap elemen b_k ← 0 untuk semua k ∈ 1:y₀ # buka y₀ pelayan pertama
9: Buat vektor p ∈ ℕⁿ # pelayan yang dipilih tiap pelanggan
10: Buat vektor d ∈ ℝⁿ₊ # waktu keluar tiap pelanggan
11: l ← 1 # indeks epoch
12: p₁ ← 1
13: for i ∈ 1:n do
14:
15: # Penyesuaian jika terjadi perubahan epoch
16: if ∀k ∈ 1:K [b_k ≥ x_{l+1}] OR a_i ≥ x_{l+1} then
17: if y_{l+1} − y_l > 0 then
18: for k ∈ (y_l + 1 : y_{l+1}) do
19: b_k ← x_{l+1} # buka pelayan di epoch baru
20: end for
21: end if
22: if y_{l+1} − y_l < 0 then
23: for k ∈ (y_{l+1} + 1 : y_l) do
24: b_k ← ∞ # tutup pelayan
25: end for
26: end if
27: l ← l + 1 # lanjut ke epoch berikutnya
28: end if
29:
30: # Pilih pelayan paling cepat tersedia
31: p_i ← arg min(b)
32: b_{p_i} ← max(a_i, b_{p_i}) + s_i
33: d_i ← b_{p_i}
34:
35: # Ulangi jika pada epoch saat ini tidak ada pelayan terbuka
36: if y_l = 0 then
37: i ← i − 1 # proses pelanggan di epoch selanjutnya
38: end if
39:
40: end for
41: Kembalikan (a, d, p) ke urutan asli input a
42: return (d, p)
43: end function
Kasus Tak Bersyarat (Unconditional Case)
Dalam pendekatan sebelumnya, hanya jumlah server yang terbuka atau tertutup dalam setiap epoch yang diketahui, tanpa menentukan server mana saja yang berada dalam kondisi tersebut. Jika keluaran seperti ini dibutuhkan, atau jika Kondisi 5 tidak terpenuhi, maka kita harus menggunakan algoritma yang lebih umum namun kurang efisien secara komputasi, yaitu algoritma tak bersyarat (unconditional algorithm), yang dijelaskan dalam Algoritma 4.
Jika Kondisi 5 tidak terpenuhi, atau jika kita ingin mengontrol secara tepat waktu kapan masing-masing server dibuka, maka kita perlu menggunakan algoritma yang kurang efisien ini.
Setiap server \(k\) memiliki pembagian sendiri dari \(L_k\) simpul waktu (knot locations), yang ditulis sebagai:
\[ x_k = (x_{k,1}, x_{k,2}, \ldots, x_{k,L_k}) \in \mathbb{R}_{+}^{L_k} \]
dan setiap simpul tersebut memiliki status terbuka atau tertutup yang dinyatakan dengan:
\[ y_k = (y_{k,1}, y_{k,2}, \ldots, y_{k,L_k+1}) \]
yaitu urutan nilai 0 dan 1 sepanjang \(L_k + 1\), yang menunjukkan apakah server dalam keadaan terbuka (1) atau tertutup (0) untuk setiap epoch terkait.
Algoritma 3: Fungsi next_fun
Misalkan:
\(y_k = (y_{k,1}, \dots,
y_{k,L_k+1})\) adalah urutan bolak-balik antara 0 dan
1 sepanjang \(L_k\),
yang menunjukkan apakah pelayan (server) dalam keadaan terbuka
(1) atau tertutup (0)
pada setiap epoch terkait.
Vektor \(c\) digunakan dengan
cara yang sedikit berbeda dibandingkan pada Sutton dan Jordan
(2011).
Di sini, \(c\) merepresentasikan
waktu berikutnya pelayan tersedia untuk pelanggan
ke-\(i\),
berdasarkan keadaan sistem saat ini (\(b\)).
Nilai \(c\) ini diperoleh dari
fungsi bantu next_fun.
Algorithm 3: Fungsi next_fun
function next_fun(t, x ∈ ℝ^L₊, y)
Temukan l sehingga x_l < t ≤ x_{l+1}
if y_{l+1} == 0 then
return x_{l+1} # jika pelayan ditutup, kembali ke waktu buka berikutnya
else
return t # jika pelayan tetap terbuka, kembali waktu saat ini
end if
end function
Algoritma 4: QDC untuk \(K(t)\) (Kasus Tak Bersyarat)
Fungsi ini digunakan jika kita ingin mengatur server mana
saja yang aktif atau tidak aktif secara eksplisit, tanpa
mengandalkan kondisi global.
Berikut ini adalah pseudokode dan penjelasan algoritma:
Algoritma 4: QDC untuk K(t) - Tanpa Syarat
function QDC_server.list(a ∈ ℝⁿ₊, s ∈ ℝⁿ₊,
x = (x₁, x₂, ..., x_K),
y = (y₁, y₂, ..., y_K))
Sort (a, s) berdasarkan a (naik)
# Tambahkan simpul tak hingga untuk akhir tiap server
∀k ∈ 1 : K → xₖ,Lₖ₊₁ ← ∞
∀k ∈ 1 : K → yₖ,Lₖ₊₂ ← 1
K ← panjang dari x (jumlah server)
Buat vektor c ∈ ℝ⁺ᴷ # waktu server aktif berikutnya
Buat vektor b ∈ ℝ⁺ᴷ # status server saat ini
Set bk ← 0 ∀k ∈ 1 : K
Buat vektor p ∈ ℕⁿ # alokasi server per pelanggan
Buat vektor d ∈ ℝ⁺ⁿ # waktu keluar pelanggan
for i ∈ 1 : n do
for k ∈ 1 : K do
cₖ ← next_fun(max(bₖ, aᵢ), xₖ, yₖ)
end for
pᵢ ← argmin(c) # pilih server dengan waktu tercepat
b_{pᵢ} ← c_{pᵢ} + sᵢ # perbarui waktu keluar server tersebut
dᵢ ← b_{pᵢ} # simpan waktu keluar pelanggan
end for
Kembalikan urutan (a, d, p) ke urutan awal input a
return (d, p)
end function
Algoritma ini dapat digunakan untuk mensimulasikan sistem antrian dengan bentuk \(G(t)/G(t)/K(t)/\infty/n/FCFS\), di mana \(K(t)\) merepresentasikan jumlah server yang aktif yang dapat berubah seiring waktu. Selain itu, kita juga dapat menentukan server tertentu yang aktif pada waktu tertentu, bukan hanya jumlahnya saja, dan kita tidak terikat oleh Kondisi 5.
Sekali lagi perlu dicatat bahwa vektor \(b\) dapat dianggap sebagai representasi dari kondisi sistem saat ini (system state), dan daftar kejadian (event list) dibentuk oleh waktu kedatangan (\(a\)) dan simpul waktu (\(x\)).
Fungsi ini bisa dijalankan dengan fungsi queue_step() dalam paket queuecomputer di R, dengan memberikan objek server.list ke argumen servers.
Dengan algoritma-algoritma yang telah disajikan sejauh ini, kita dapat melakukan simulasi dari sekumpulan sistem antrian yang sangat umum yaitu:
\[ G(t)/G(t)/K(t)/\infty/n/FCFS \]
dengan cara yang efisien secara komputasi (computationally efficient).
Berbeda dengan algoritma dari Kin dan Chan (2010), dalam QDC vektor
status \(b\) ditulisi ulang
pada setiap iterasi.
Oleh karena itu, penggunaan memori untuk QDC berskala sebagai \(\mathcal{O}(n)\) dibandingkan dengan \(\mathcal{O}(nK)\) pada algoritma
sebelumnya.
Jaringan antrian tandem (tandem queueing networks)
dapat disimulasikan dengan menggunakan keluaran dari satu sistem
antrian sebagai masukan bagi sistem antrian berikutnya.
Kami mendemonstrasikan pendekatan ini pada contoh simulasi
bandara (Airport Simulation) di Bagian
8.2.
Sementara itu, jaringan antrian fork/join akan dibahas pada
bagian selanjutnya, di mana kami menjelaskan rincian
implementasi dari paket queuecomputer sehubungan
dengan algoritma QDC.
4. Penggunaan Paket queuecomputer
Tujuan utama dari paket queuecomputer adalah untuk
menghitung output dari sistem antrian secara
deterministik, dengan memberikan waktu kedatangan dan
waktu pelayanan dari semua pelanggan.
Fungsi yang paling penting adalah queue_step(). Fungsi
ini memiliki tiga argumen utama:
arrivals)service)servers)Contoh Penggunaan
Kita akan mensimulasikan sistem antrian dengan 100 pelanggan dan 2 server. Berikut adalah langkah-langkahnya:
# Memuat paket queuecomputer
library("queuecomputer")
## Warning: package 'queuecomputer' was built under R version 4.4.3
# Membuat data waktu kedatangan (arrival) sebanyak 100 pelanggan
arrivals <- cumsum(rexp(100)) # waktu antar kedatangan eksponensial
head(arrivals)
## [1] 2.522998 3.035431 4.797518 5.324785 6.289665 6.881056
#Contoh Dasar: Simulasi Queue Sederhana
service <-rexp(100)
departures <- queue_step(arrivals, service = service, servers = 2)
departures
## $departures
## [1] 2.529778 3.075212 5.180949 5.409404 7.357425 6.955381 11.093935
## [8] 10.325109 11.574950 12.020816 15.217395 14.628883 17.598881 16.002528
## [15] 16.284894 17.289151 19.375627 19.710162 23.385990 22.008342 24.381217
## [22] 24.338942 27.179736 28.130153 28.996591 28.967055 30.875618 30.026664
## [29] 32.701825 32.569388 33.458095 34.253362 33.580310 35.082622 35.391316
## [36] 35.623523 35.564456 36.068947 38.951645 37.176156 38.558218 38.896179
## [43] 39.504773 39.807981 39.532430 39.876881 42.474564 43.582612 45.201768
## [50] 45.871622 48.477931 49.840460 48.865443 49.632814 49.963732 50.616176
## [57] 52.309281 52.098330 54.004737 54.570310 54.035019 55.569373 57.428414
## [64] 59.467121 59.027857 60.797905 61.079874 63.074184 62.976622 63.781433
## [71] 64.498779 65.200033 65.689244 65.373620 67.708659 66.582198 67.860469
## [78] 70.010512 69.635671 71.157219 70.274160 73.231272 73.554976 73.876075
## [85] 73.776751 74.632909 74.350107 77.575352 76.299591 76.324399 79.559158
## [92] 79.505343 81.083835 80.121614 80.806410 80.984857 84.271044 82.579447
## [99] 84.350381 85.850251
##
## $server
## [1] 1 2 1 2 1 2 2 1 1 2 1 2 2 1 1 1 1 2 1 2 2 1 1 2 1 2 2 1 1 2 2 1 2 2 1 2 1
## [38] 1 2 1 1 1 1 2 1 1 2 1 2 1 2 1 2 2 2 1 2 1 1 2 1 1 2 1 2 2 1 2 1 1 2 1 2 1
## [75] 1 2 2 1 2 2 1 1 2 1 2 2 1 1 2 2 2 1 1 2 2 2 2 1 1 2
##
## $departures_df
## # A tibble: 100 × 6
## arrivals service departures waiting system_time server
## <dbl> <dbl> <dbl> <dbl> <dbl> <int>
## 1 2.52 0.00678 2.53 0 0.00678 1
## 2 3.04 0.0398 3.08 0 0.0398 2
## 3 4.80 0.383 5.18 0 0.383 1
## 4 5.32 0.0846 5.41 0 0.0846 2
## 5 6.29 1.07 7.36 0 1.07 1
## 6 6.88 0.0743 6.96 0 0.0743 2
## 7 9.56 1.53 11.1 2.22e-16 1.53 2
## 8 9.91 0.413 10.3 6.11e-16 0.413 1
## 9 10.7 0.854 11.6 1.11e-16 0.854 1
## 10 10.8 0.927 12.0 3.28e- 1 1.25 2
## # ℹ 90 more rows
##
## $queuelength_df
## times queuelength
## 1 0.000000 0
## 2 2.522998 1
## 3 2.522998 0
## 4 3.035431 1
## 5 3.035431 0
## 6 4.797518 1
## 7 4.797518 0
## 8 5.324785 1
## 9 5.324785 0
## 10 6.289665 1
## 11 6.289665 0
## 12 6.881056 1
## 13 6.881056 0
## 14 9.564083 1
## 15 9.564083 0
## 16 9.911968 1
## 17 9.911968 0
## 18 10.721138 1
## 19 10.721138 0
## 20 10.766052 1
## 21 11.093935 0
## 22 14.061343 1
## 23 14.061343 0
## 24 14.528015 1
## 25 14.528015 0
## 26 14.749267 1
## 27 14.749267 0
## 28 14.755194 1
## 29 15.217395 0
## 30 16.142639 1
## 31 16.142639 0
## 32 17.114687 1
## 33 17.114687 0
## 34 17.454856 1
## 35 17.454856 0
## 36 18.582230 1
## 37 18.582230 0
## 38 19.187253 1
## 39 19.375627 0
## 40 21.381400 1
## 41 21.381400 0
## 42 22.074196 1
## 43 22.074196 0
## 44 22.900358 1
## 45 23.385990 0
## 46 26.540878 1
## 47 26.540878 0
## 48 28.081306 1
## 49 28.081306 0
## 50 28.611424 1
## 51 28.611424 0
## 52 28.706389 1
## 53 28.706389 0
## 54 29.643846 1
## 55 29.643846 0
## 56 29.716517 1
## 57 29.716517 0
## 58 31.110985 1
## 59 31.110985 0
## 60 31.224455 1
## 61 31.224455 0
## 62 31.875792 1
## 63 32.569388 0
## 64 32.591110 1
## 65 32.701825 0
## 66 33.308848 1
## 67 33.458095 0
## 68 33.896590 1
## 69 33.896590 0
## 70 34.128216 1
## 71 34.253362 0
## 72 34.706266 1
## 73 34.709448 2
## 74 35.082622 1
## 75 35.391316 0
## 76 35.571865 1
## 77 35.571865 0
## 78 35.675201 1
## 79 35.675201 0
## 80 36.896266 1
## 81 36.896266 0
## 82 38.514627 1
## 83 38.514627 0
## 84 38.736448 1
## 85 38.736448 0
## 86 38.879149 1
## 87 38.896179 0
## 88 39.010922 1
## 89 39.010922 0
## 90 39.014666 1
## 91 39.504773 0
## 92 39.535707 1
## 93 39.535707 0
## 94 41.542767 1
## 95 41.542767 0
## 96 42.977102 1
## 97 42.977102 0
## 98 44.237936 1
## 99 44.237936 0
## 100 45.583399 1
## 101 45.583399 0
## 102 47.827773 1
## 103 47.827773 0
## 104 48.152346 1
## 105 48.152346 0
## 106 48.325063 1
## 107 48.477931 0
## 108 49.216371 1
## 109 49.216371 0
## 110 49.878698 1
## 111 49.878698 0
## 112 50.261218 1
## 113 50.261218 0
## 114 50.434742 1
## 115 50.434742 0
## 116 50.763374 1
## 117 50.763374 0
## 118 50.802888 1
## 119 52.098330 0
## 120 52.684720 1
## 121 52.684720 0
## 122 53.232511 1
## 123 54.004737 0
## 124 55.313932 1
## 125 55.313932 0
## 126 57.062735 1
## 127 57.062735 0
## 128 57.979110 1
## 129 57.979110 0
## 130 58.979499 1
## 131 58.979499 0
## 132 60.418807 1
## 133 60.418807 0
## 134 61.016177 1
## 135 61.016177 0
## 136 61.789904 1
## 137 61.789904 0
## 138 61.817415 1
## 139 61.817415 0
## 140 62.411618 1
## 141 62.976622 0
## 142 64.365594 1
## 143 64.365594 0
## 144 65.002164 1
## 145 65.002164 0
## 146 65.002893 1
## 147 65.002893 0
## 148 65.337851 1
## 149 65.337851 0
## 150 66.089017 1
## 151 66.089017 0
## 152 66.448965 1
## 153 66.448965 0
## 154 67.162415 1
## 155 67.162415 0
## 156 68.454429 1
## 157 68.454429 0
## 158 69.137318 1
## 159 69.137318 0
## 160 69.721574 1
## 161 69.721574 0
## 162 70.019052 1
## 163 70.019052 0
## 164 71.178069 1
## 165 71.178069 0
## 166 71.223725 1
## 167 71.223725 0
## 168 71.534675 1
## 169 73.231272 0
## 170 73.456566 1
## 171 73.481587 2
## 172 73.554976 1
## 173 73.624744 2
## 174 73.776751 1
## 175 73.876075 0
## 176 74.942878 1
## 177 74.942878 0
## 178 75.111429 1
## 179 75.111429 0
## 180 75.503796 1
## 181 76.299591 0
## 182 76.418167 1
## 183 76.418167 0
## 184 77.903379 1
## 185 77.903379 0
## 186 78.948327 1
## 187 79.281152 2
## 188 79.505343 1
## 189 79.559158 0
## 190 79.751566 1
## 191 80.082807 2
## 192 80.121614 1
## 193 80.486019 2
## 194 80.806410 1
## 195 80.984857 0
## 196 81.177976 1
## 197 81.177976 0
## 198 82.792545 1
## 199 82.792545 0
## 200 84.182925 1
## 201 84.271044 0
##
## $systemlength_df
## times queuelength
## 1 0.000000 0
## 2 2.522998 1
## 3 2.529778 0
## 4 3.035431 1
## 5 3.075212 0
## 6 4.797518 1
## 7 5.180949 0
## 8 5.324785 1
## 9 5.409404 0
## 10 6.289665 1
## 11 6.881056 2
## 12 6.955381 1
## 13 7.357425 0
## 14 9.564083 1
## 15 9.911968 2
## 16 10.325109 1
## 17 10.721138 2
## 18 10.766052 3
## 19 11.093935 2
## 20 11.574950 1
## 21 12.020816 0
## 22 14.061343 1
## 23 14.528015 2
## 24 14.628883 1
## 25 14.749267 2
## 26 14.755194 3
## 27 15.217395 2
## 28 16.002528 1
## 29 16.142639 2
## 30 16.284894 1
## 31 17.114687 2
## 32 17.289151 1
## 33 17.454856 2
## 34 17.598881 1
## 35 18.582230 2
## 36 19.187253 3
## 37 19.375627 2
## 38 19.710162 1
## 39 21.381400 2
## 40 22.008342 1
## 41 22.074196 2
## 42 22.900358 3
## 43 23.385990 2
## 44 24.338942 1
## 45 24.381217 0
## 46 26.540878 1
## 47 27.179736 0
## 48 28.081306 1
## 49 28.130153 0
## 50 28.611424 1
## 51 28.706389 2
## 52 28.967055 1
## 53 28.996591 0
## 54 29.643846 1
## 55 29.716517 2
## 56 30.026664 1
## 57 30.875618 0
## 58 31.110985 1
## 59 31.224455 2
## 60 31.875792 3
## 61 32.569388 2
## 62 32.591110 3
## 63 32.701825 2
## 64 33.308848 3
## 65 33.458095 2
## 66 33.580310 1
## 67 33.896590 2
## 68 34.128216 3
## 69 34.253362 2
## 70 34.706266 3
## 71 34.709448 4
## 72 35.082622 3
## 73 35.391316 2
## 74 35.564456 1
## 75 35.571865 2
## 76 35.623523 1
## 77 35.675201 2
## 78 36.068947 1
## 79 36.896266 2
## 80 37.176156 1
## 81 38.514627 2
## 82 38.558218 1
## 83 38.736448 2
## 84 38.879149 3
## 85 38.896179 2
## 86 38.951645 1
## 87 39.010922 2
## 88 39.014666 3
## 89 39.504773 2
## 90 39.532430 1
## 91 39.535707 2
## 92 39.807981 1
## 93 39.876881 0
## 94 41.542767 1
## 95 42.474564 0
## 96 42.977102 1
## 97 43.582612 0
## 98 44.237936 1
## 99 45.201768 0
## 100 45.583399 1
## 101 45.871622 0
## 102 47.827773 1
## 103 48.152346 2
## 104 48.325063 3
## 105 48.477931 2
## 106 48.865443 1
## 107 49.216371 2
## 108 49.632814 1
## 109 49.840460 0
## 110 49.878698 1
## 111 49.963732 0
## 112 50.261218 1
## 113 50.434742 2
## 114 50.616176 1
## 115 50.763374 2
## 116 50.802888 3
## 117 52.098330 2
## 118 52.309281 1
## 119 52.684720 2
## 120 53.232511 3
## 121 54.004737 2
## 122 54.035019 1
## 123 54.570310 0
## 124 55.313932 1
## 125 55.569373 0
## 126 57.062735 1
## 127 57.428414 0
## 128 57.979110 1
## 129 58.979499 2
## 130 59.027857 1
## 131 59.467121 0
## 132 60.418807 1
## 133 60.797905 0
## 134 61.016177 1
## 135 61.079874 0
## 136 61.789904 1
## 137 61.817415 2
## 138 62.411618 3
## 139 62.976622 2
## 140 63.074184 1
## 141 63.781433 0
## 142 64.365594 1
## 143 64.498779 0
## 144 65.002164 1
## 145 65.002893 2
## 146 65.200033 1
## 147 65.337851 2
## 148 65.373620 1
## 149 65.689244 0
## 150 66.089017 1
## 151 66.448965 2
## 152 66.582198 1
## 153 67.162415 2
## 154 67.708659 1
## 155 67.860469 0
## 156 68.454429 1
## 157 69.137318 2
## 158 69.635671 1
## 159 69.721574 2
## 160 70.010512 1
## 161 70.019052 2
## 162 70.274160 1
## 163 71.157219 0
## 164 71.178069 1
## 165 71.223725 2
## 166 71.534675 3
## 167 73.231272 2
## 168 73.456566 3
## 169 73.481587 4
## 170 73.554976 3
## 171 73.624744 4
## 172 73.776751 3
## 173 73.876075 2
## 174 74.350107 1
## 175 74.632909 0
## 176 74.942878 1
## 177 75.111429 2
## 178 75.503796 3
## 179 76.299591 2
## 180 76.324399 1
## 181 76.418167 2
## 182 77.575352 1
## 183 77.903379 2
## 184 78.948327 3
## 185 79.281152 4
## 186 79.505343 3
## 187 79.559158 2
## 188 79.751566 3
## 189 80.082807 4
## 190 80.121614 3
## 191 80.486019 4
## 192 80.806410 3
## 193 80.984857 2
## 194 81.083835 1
## 195 81.177976 2
## 196 82.579447 1
## 197 82.792545 2
## 198 84.182925 3
## 199 84.271044 2
## 200 84.350381 1
## 201 85.850251 0
##
## $servers_input
## [1] 2
##
## $state
## [1] 84.35038 85.85025
##
## attr(,"class")
## [1] "queue_list" "list"
Hasil Output dari queue_step()
Fungsi queue_step() menghasilkan objek bertipe
queue_list, yaitu objek yang berisi hasil simulasi
sistem antrian seperti waktu kedatangan, pelayanan, waktu tunggu, waktu
keluar dari sistem, dan alokasi server.
Untuk memudahkan analisis hasil, penulis paket juga membuat sebuah
fungsi summary() khusus yang dapat
digunakan pada objek hasil queue_step. Fungsi ini akan
menampilkan rangkuman statistik dari proses antrian
yang disimulasikan.
Contoh penggunaan:
summary(departures)
## Total customers:
## 100
## Missed customers:
## 0
## Mean waiting time:
## 0.125
## Mean response time:
## 1.04
## Utilization factor:
## 0.532773248951013
## Mean queue length:
## 0.149
## Mean number of customers in system:
## 1.21
Missed Customers:
1. Jika pada akhir vektor \(y\), nilai
nya nol, artinya server dalam keadaan tertutup, sehingga ada pelanggan
yang tidak pernah mendapatkan layanan. Hasil ini akan dilaporkan sebagai
“Missed customers”.
Perulangan (loop) utama dalam Algoritma 1 dan 2 ditulis menggunakan bahasa C++ dengan bantuan pustaka Armadillo untuk aljabar linier (Sanderson dan Curtin, 2016). Perulangan C++ ini kemudian dipanggil dari R menggunakan paket Rcpp (Eddelbuettel dkk., 2011) dan RcppArmadillo (Eddelbuettel dan Sanderson, 2014).
Fungsi utama dalam paket ini adalah queue_step, yang
bertindak sebagai wrapper untuk fungsi queue.
Fungsi queue ini merupakan metode S3 di R yang akan memilih
dan menjalankan algoritma sesuai dengan kelas (class)
dari objek yang diberikan pada argumen servers:
server bertipe numeric, maka
dijalankan Algoritma 1 (jumlah server tetap).server bertipe server.stepfun,
maka dijalankan Algoritma 2 (jumlah server berubah
dalam bentuk fungsi langkah).server bertipe server.list, maka
dijalankan Algoritma 4 (pengaturan server yang lebih
kompleks dan fleksibel).Fungsi queue akan menghitung waktu keluar (departure
time) d dan alokasi server p. Sementara itu,
fungsi queue_step menambahkan beberapa output tambahan
seperti:
Semua informasi ini digunakan dalam fungsi-fungsi
summary dan plot untuk menganalisis kinerja
sistem antrian yang disimulasikan.
Untuk mensimulasikan jaringan antrian bertipe
fork/join, paket queuecomputer menyediakan
fungsi wait_step. Fungsi ini adalah pembungkus (wrapper)
dari fungsi dasar pmax.int, yang digunakan untuk mengambil
nilai maksimum dari beberapa waktu keberangkatan subpekerjaan
(subtasks). Artinya, sebuah tugas (job) dianggap selesai ketika seluruh
subtugas-nya telah selesai.
Berbeda dengan paket simmer dan simpy yang
meminta pengguna memasukkan parameter distribusi (seperti laju
kedatangan dan layanan), dalam queuecomputer, pengguna
langsung memasukkan waktu kedatangan dan waktu layanan yang
sudah disampling sebelumnya.
Keuntungan pendekatan ini adalah:
Dengan demikian, queuecomputer memisahkan antara proses
sampling statistik dan perhitungan sistem
antrian, yang memberi fleksibilitas dan efisiensi tinggi.
Berikut adalah terjemahan isi Bab 6: Validation dari buku “Computationally Efficient Simulation of Queues: The R Package queuecomputer”:
Untuk menunjukkan validitas algoritma QDC (Queue Departure Computation), penulis melakukan simulasi sistem antrian M/M/2/∞/1000/FCFS. Jika QDC valid untuk sistem antrian jenis ini, maka diasumsikan valid pula untuk sistem G(t)/G(t)/K/∞/n/FCFS, karena data input berupa pasangan \((a, s)\) (waktu kedatangan dan layanan) bisa saja berasal dari distribusi eksponensial meskipun peluang pengamatannya kecil.
Langkah-langkah validasi:
Generate waktu kedatangan dan waktu layanan
set.seed(1)
n_customers <- 10^4
lambda_a <- 1/1
lambda_s <- 1/0.9
interarrivals <- rexp(n_customers, lambda_a)
arrivals <- cumsum(interarrivals)
service <- rexp(n_customers, lambda_s)
Jalankan kedua paket:
queuecomputer langsung menerima input waktu kedatangan
dan layanan.simmer perlu dimodifikasi agar bisa menerima input
tersebut (karena default-nya menerima parameter distribusi, bukan nilai
acak mentah).## Menggunakan queuecomputer
library(queuecomputer)
queuecomputer_output <- queue_step(arrivals = arrivals, service = service, servers = 2)
head(sort(depart(queuecomputer_output)))
## [1] 1.340151 2.288112 2.639976 2.796572 3.249794 5.714967
## Menggunakan Simmmer
library(simmer)
## Warning: package 'simmer' was built under R version 4.4.3
simmer_step <- function(arrivals, service, servers = 1) {
n <- length(arrivals)
env <- simmer("DES Simulation")
# Gunakan indeks global untuk mengakses service[i]
idx <- 0
# trajectory menetapkan waktu layanan berdasarkan urutan pelanggan
traj <- trajectory("customer path") %>%
set_attribute("service_time", function() {
idx <<- idx + 1
service[idx]
}) %>%
seize("server", 1) %>%
timeout_from_attribute("service_time") %>%
release("server", 1)
# Tambah server
env %>% add_resource("server", capacity = servers)
# Tambahkan seluruh arrival
env %>% add_generator(
name_prefix = "cust",
trajectory = traj,
distribution = at(arrivals),
mon = 2
)
env %>% run()
return(get_mon_arrivals(env)$end_time)
}
simmer_output <- simmer_step(arrivals = arrivals, service = service, servers = 2)
head(simmer_output)
## [1] 1.340151 2.288112 2.639976 2.796572 3.249794 5.714967
Hasil:
queuecomputer memberikan hasil
yang setara secara fungsional dengan simulasi berbasis
Discrete Event Simulation (DES) seperti
simmer dan simpy.Simulasi juga dilakukan untuk sistem
M/M/3/∞/5.000.000/FCFS menggunakan
queuecomputer, dengan tujuan membandingkan metrik performa
hasil simulasi dengan rumus analitik teori antrian:
Parameter:
Hasil teoretis:
Hasil simulasi:
set.seed(1)
n_customers <- 5e6
lambda <- 2
mu <- 1
interarrivals <- rexp(n_customers, lambda)
arrivals <- cumsum(interarrivals)
service <- rexp(n_customers, mu)
K = 3
MM3 <- queue_step(arrivals = arrivals, service = service, servers = K)
summary(MM3)
## Total customers:
## 5000000
## Missed customers:
## 0
## Mean waiting time:
## 0.445
## Mean response time:
## 1.44
## Utilization factor:
## 0.666140156160826
## Mean queue length:
## 0.889
## Mean number of customers in system:
## 2.89
Untuk mengukur efisiensi komputasi dari berbagai paket simulasi antrian, penulis melakukan eksperimen sebagai berikut:
Sistem antrian yang disimulasikan: M/M/2/∞/n/FCFS (dengan dua server dan kapasitas tak terbatas).
Parameter yang digunakan:
Tujuan: menghitung waktu keberangkatan pelanggan dengan jumlah pelanggan \(n\) yang bervariasi:
queuecomputer bahkan dicoba sampai \(n = 10^7\)Setiap percobaan diulang 100 kali untuk mendapatkan waktu komputasi rata-rata.
Alat ukur waktu:
microbenchmark dari paket R
microbenchmark digunakan untuk queuecomputer
dan simmer.Lingkungan uji:
CPU: Intel Core i7-6700 @ 3.40GHz
OS: Debian GNU/Linux
Versi perangkat lunak:
simmer: 3.6.1simpy: 3.6.1queuecomputer: 0.8.1Hasil benchmark menunjukkan bahwa:
queuecomputer jauh lebih cepat
dibandingkan dua paket lainnya.
Keuntungan kecepatan:
Dibandingkan simpy:
Dibandingkan simmer:
Bahkan untuk 10 juta pelanggan,
queuecomputer hanya membutuhkan kurang dari 1
detik untuk menyelesaikan simulasi.
Efisiensi tinggi ini didapatkan karena:
Grafik (Gambar 2) dalam buku menunjukkan waktu eksekusi dalam
milidetik terhadap jumlah pelanggan pada skala log. Kurva
queuecomputer secara konsisten berada jauh di bawah
simmer dan simpy.
QDC dan implementasinya dalam queuecomputer merupakan
cara yang sangat efisien secara komputasi untuk
mensimulasikan sistem antrian tipe G(t)/G(t)/K/∞/n/FCFS
dibandingkan metode simulasi DES konvensional yang digunakan di
simpy dan simmer.
Bab ini memperlihatkan beragam contoh simulasi sistem antrian
menggunakan paket queuecomputer di R, mulai dari simulasi
dasar, validasi model M/M/K, hingga aplikasi pada skenario bandara dan
visualisasi hasilnya.
library(queuecomputer)
library(randomNames)
## Warning: package 'randomNames' was built under R version 4.4.3
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.4.3
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.4.3
##
## 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
Pertama, kita memuat pustaka yang diperlukan:
queuecomputer untuk simulasi antrian,
randomNames untuk menghasilkan nama acak,
ggplot2 untuk visualisasi, dan dplyr untuk
manipulasi data.
arrivals <- cumsum(rexp(100))
head(arrivals)
## [1] 0.03191802 1.56707421 1.63300775 2.03311017 2.25897130 3.40425011
Waktu kedatangan pelanggan dihasilkan dari distribusi eksponensial, dan dikonversi menjadi waktu kedatangan kumulatif.
service <-rexp(100)
departures <- queue_step(arrivals, service = service, servers = 2)
head(departures)
## $departures
## [1] 7.020874 2.874659 4.994353 5.046308 5.821050 8.491635 8.001266
## [8] 8.198585 9.881190 12.793183 11.506491 12.739232 16.346705 17.820301
## [15] 18.896116 19.881708 21.169453 25.923729 25.616642 26.035712 26.798894
## [22] 26.225767 28.063023 27.974980 31.326364 29.158032 29.797688 30.449111
## [29] 31.198705 31.935463 31.399457 31.807203 33.594346 34.743897 36.495513
## [36] 36.627119 38.667802 38.766714 40.051650 40.142113 41.390960 41.628946
## [43] 41.636018 41.994260 45.977332 48.337258 48.071360 49.313280 48.825331
## [50] 49.051880 49.098984 50.607190 51.076834 53.563876 51.374360 54.479363
## [57] 54.737201 55.405674 56.328476 58.052968 56.479337 59.791775 58.588751
## [64] 59.262005 59.749795 60.760553 66.109363 66.109778 66.633358 68.903158
## [71] 70.299787 69.060521 71.736641 71.222161 71.591935 72.182959 72.249669
## [78] 76.230362 74.708306 76.612613 81.997902 81.781715 81.828348 82.369901
## [85] 84.582392 85.205845 86.562365 86.451264 88.681585 91.058305 91.814324
## [92] 91.746218 91.865389 92.487097 95.307024 93.072876 96.648826 96.369517
## [99] 96.771675 98.199363
##
## $server
## [1] 1 2 2 2 2 2 1 1 1 2 1 1 1 2 1 2 1 2 1 1 2 1 1 2 2 1 1 1 1 1 2 2 2 1 2 1 2
## [38] 1 2 1 2 1 2 1 2 1 2 2 1 1 1 1 2 1 2 2 1 2 1 2 1 1 2 2 2 2 1 2 1 2 1 2 2 1
## [75] 1 1 2 1 2 2 1 2 2 2 1 2 1 2 2 1 2 1 1 2 1 2 2 1 1 2
##
## $departures_df
## # A tibble: 100 × 6
## arrivals service departures waiting system_time server
## <dbl> <dbl> <dbl> <dbl> <dbl> <int>
## 1 0.0319 6.99 7.02 0 6.99 1
## 2 1.57 1.31 2.87 0 1.31 2
## 3 1.63 2.12 4.99 1.24e+ 0 3.36 2
## 4 2.03 0.0520 5.05 2.96e+ 0 3.01 2
## 5 2.26 0.775 5.82 2.79e+ 0 3.56 2
## 6 3.40 2.67 8.49 2.42e+ 0 5.09 2
## 7 6.12 0.980 8.00 9.05e- 1 1.89 1
## 8 6.75 0.197 8.20 1.25e+ 0 1.45 1
## 9 9.30 0.580 9.88 0 0.580 1
## 10 10.2 2.59 12.8 4.44e-16 2.59 2
## # ℹ 90 more rows
##
## $queuelength_df
## times queuelength
## 1 0.00000000 0
## 2 0.03191802 1
## 3 0.03191802 0
## 4 1.56707421 1
## 5 1.56707421 0
## 6 1.63300775 1
## 7 2.03311017 2
## 8 2.25897130 3
## 9 2.87465898 2
## 10 3.40425011 3
## 11 4.99435298 2
## 12 5.04630759 1
## 13 5.82104977 0
## 14 6.11599085 1
## 15 6.74926606 2
## 16 7.02087397 1
## 17 8.00126648 0
## 18 9.30164777 1
## 19 9.30164777 0
## 20 10.20339610 1
## 21 10.20339610 0
## 22 10.51514738 1
## 23 10.51514738 0
## 24 11.92875114 1
## 25 11.92875114 0
## 26 16.26120012 1
## 27 16.26120012 0
## 28 16.49568059 1
## 29 16.49568059 0
## 30 18.60862166 1
## 31 18.60862166 0
## 32 19.51715468 1
## 33 19.51715468 0
## 34 20.95733092 1
## 35 20.95733092 0
## 36 21.17445657 1
## 37 21.17445657 0
## 38 21.31161680 1
## 39 21.31161680 0
## 40 23.25486967 1
## 41 23.84703013 2
## 42 25.61664225 1
## 43 25.71095748 2
## 44 25.92372947 1
## 45 26.03158955 2
## 46 26.03571188 1
## 47 26.22576731 0
## 48 26.92867876 1
## 49 26.92867876 0
## 50 27.18630888 1
## 51 27.97342778 2
## 52 27.97498019 1
## 53 28.06302313 0
## 54 28.57387587 1
## 55 28.67073020 2
## 56 29.15803176 1
## 57 29.56657528 2
## 58 29.72866244 3
## 59 29.79768793 2
## 60 30.34845110 3
## 61 30.39509502 4
## 62 30.44911106 3
## 63 31.19870549 2
## 64 31.32636412 1
## 65 31.39945725 0
## 66 33.35901534 1
## 67 33.35901534 0
## 68 33.85187805 1
## 69 33.85187805 0
## 70 35.92813719 1
## 71 35.92813719 0
## 72 36.59218135 1
## 73 36.59218135 0
## 74 37.17496841 1
## 75 37.17496841 0
## 76 38.46633771 1
## 77 38.46633771 0
## 78 39.35264010 1
## 79 39.35264010 0
## 80 39.44679710 1
## 81 39.44679710 0
## 82 39.76660212 1
## 83 40.05164985 0
## 84 40.54621139 1
## 85 40.54621139 0
## 86 41.06844940 1
## 87 41.39096018 0
## 88 41.83742713 1
## 89 41.83742713 0
## 90 44.58272071 1
## 91 44.58272071 0
## 92 45.15391394 1
## 93 45.15391394 0
## 94 46.07286168 1
## 95 46.07286168 0
## 96 46.35868255 1
## 97 47.02470897 2
## 98 47.35608785 3
## 99 47.38685874 4
## 100 48.07136046 3
## 101 48.33725810 2
## 102 48.41204417 3
## 103 48.82533145 2
## 104 49.05187987 1
## 105 49.09898371 0
## 106 49.84859646 1
## 107 49.84859646 0
## 108 50.06382036 1
## 109 50.60718962 0
## 110 51.02429570 1
## 111 51.07683402 0
## 112 54.38609740 1
## 113 54.38609740 0
## 114 54.51871687 1
## 115 54.51871687 0
## 116 55.15587873 1
## 117 55.15587873 0
## 118 55.32030863 1
## 119 55.32030864 0
## 120 55.75872310 1
## 121 55.75872310 0
## 122 55.81811854 1
## 123 56.32847554 0
## 124 57.15750551 1
## 125 57.15750551 0
## 126 57.60731874 1
## 127 58.05296767 0
## 128 58.21422119 1
## 129 58.58875148 0
## 130 59.35269424 1
## 131 59.35269425 0
## 132 59.92461086 1
## 133 59.92461086 0
## 134 64.87574572 1
## 135 64.87574572 0
## 136 66.09106412 1
## 137 66.09106412 0
## 138 66.20605680 1
## 139 66.20605680 0
## 140 67.64218892 1
## 141 67.64218892 0
## 142 67.65909717 1
## 143 67.65909717 0
## 144 68.50909833 1
## 145 68.90315792 0
## 146 70.59011750 1
## 147 70.59011750 0
## 148 70.83973399 1
## 149 70.83973399 0
## 150 71.42987913 1
## 151 71.42987913 0
## 152 71.43758517 1
## 153 71.47975462 2
## 154 71.59193492 1
## 155 71.73664150 0
## 156 73.62660416 1
## 157 73.62660416 0
## 158 73.69621302 1
## 159 73.69621302 0
## 160 76.15160999 1
## 161 76.15160999 0
## 162 81.35209627 1
## 163 81.35209627 0
## 164 81.54448970 1
## 165 81.54448970 0
## 166 81.59896172 1
## 167 81.78171489 0
## 168 82.02927648 1
## 169 82.02927648 0
## 170 83.46305169 1
## 171 83.46305169 0
## 172 84.79021620 1
## 173 84.79021620 0
## 174 84.97319933 1
## 175 84.97319933 0
## 176 85.44385001 1
## 177 85.44385001 0
## 178 88.37676210 1
## 179 88.37676210 0
## 180 90.41871042 1
## 181 90.41871042 0
## 182 91.17416971 1
## 183 91.17416971 0
## 184 91.37165292 1
## 185 91.37165292 0
## 186 91.84942917 1
## 187 91.84942917 0
## 188 92.30096161 1
## 189 92.30096161 0
## 190 92.30223706 1
## 191 92.30223706 0
## 192 92.38702003 1
## 193 92.48709653 0
## 194 93.21611884 1
## 195 93.21611884 0
## 196 96.13026393 1
## 197 96.13026393 0
## 198 96.49223633 1
## 199 96.49223633 0
## 200 98.10466732 1
## 201 98.10466732 0
##
## $systemlength_df
## times queuelength
## 1 0.00000000 0
## 2 0.03191802 1
## 3 1.56707421 2
## 4 1.63300775 3
## 5 2.03311017 4
## 6 2.25897130 5
## 7 2.87465898 4
## 8 3.40425011 5
## 9 4.99435298 4
## 10 5.04630759 3
## 11 5.82104977 2
## 12 6.11599085 3
## 13 6.74926606 4
## 14 7.02087397 3
## 15 8.00126648 2
## 16 8.19858505 1
## 17 8.49163452 0
## 18 9.30164777 1
## 19 9.88119024 0
## 20 10.20339610 1
## 21 10.51514738 2
## 22 11.50649097 1
## 23 11.92875114 2
## 24 12.73923157 1
## 25 12.79318275 0
## 26 16.26120012 1
## 27 16.34670494 0
## 28 16.49568059 1
## 29 17.82030064 0
## 30 18.60862166 1
## 31 18.89611597 0
## 32 19.51715468 1
## 33 19.88170787 0
## 34 20.95733092 1
## 35 21.16945349 0
## 36 21.17445657 1
## 37 21.31161680 2
## 38 23.25486967 3
## 39 23.84703013 4
## 40 25.61664225 3
## 41 25.71095748 4
## 42 25.92372947 3
## 43 26.03158955 4
## 44 26.03571188 3
## 45 26.22576731 2
## 46 26.79889395 1
## 47 26.92867876 2
## 48 27.18630888 3
## 49 27.97342778 4
## 50 27.97498019 3
## 51 28.06302313 2
## 52 28.57387587 3
## 53 28.67073020 4
## 54 29.15803176 3
## 55 29.56657528 4
## 56 29.72866244 5
## 57 29.79768793 4
## 58 30.34845110 5
## 59 30.39509502 6
## 60 30.44911106 5
## 61 31.19870549 4
## 62 31.32636412 3
## 63 31.39945725 2
## 64 31.80720295 1
## 65 31.93546312 0
## 66 33.35901534 1
## 67 33.59434588 0
## 68 33.85187805 1
## 69 34.74389727 0
## 70 35.92813719 1
## 71 36.49551251 0
## 72 36.59218135 1
## 73 36.62711940 0
## 74 37.17496841 1
## 75 38.46633771 2
## 76 38.66780170 1
## 77 38.76671363 0
## 78 39.35264010 1
## 79 39.44679710 2
## 80 39.76660212 3
## 81 40.05164985 2
## 82 40.14211257 1
## 83 40.54621139 2
## 84 41.06844940 3
## 85 41.39096018 2
## 86 41.62894585 1
## 87 41.63601778 0
## 88 41.83742713 1
## 89 41.99426029 0
## 90 44.58272071 1
## 91 45.15391394 2
## 92 45.97733188 1
## 93 46.07286168 2
## 94 46.35868255 3
## 95 47.02470897 4
## 96 47.35608785 5
## 97 47.38685874 6
## 98 48.07136046 5
## 99 48.33725810 4
## 100 48.41204417 5
## 101 48.82533145 4
## 102 49.05187987 3
## 103 49.09898371 2
## 104 49.31327995 1
## 105 49.84859646 2
## 106 50.06382036 3
## 107 50.60718962 2
## 108 51.02429570 3
## 109 51.07683402 2
## 110 51.37436030 1
## 111 53.56387650 0
## 112 54.38609740 1
## 113 54.47936342 0
## 114 54.51871687 1
## 115 54.73720149 0
## 116 55.15587873 1
## 117 55.32030863 2
## 118 55.40567448 1
## 119 55.75872310 2
## 120 55.81811854 3
## 121 56.32847554 2
## 122 56.47933663 1
## 123 57.15750551 2
## 124 57.60731874 3
## 125 58.05296767 2
## 126 58.21422119 3
## 127 58.58875148 2
## 128 59.26200492 1
## 129 59.35269424 2
## 130 59.74979469 1
## 131 59.79177524 0
## 132 59.92461086 1
## 133 60.76055278 0
## 134 64.87574572 1
## 135 66.09106412 2
## 136 66.10936265 1
## 137 66.10977834 0
## 138 66.20605680 1
## 139 66.63335838 0
## 140 67.64218892 1
## 141 67.65909717 2
## 142 68.50909833 3
## 143 68.90315792 2
## 144 69.06052146 1
## 145 70.29978742 0
## 146 70.59011750 1
## 147 70.83973399 2
## 148 71.22216134 1
## 149 71.42987913 2
## 150 71.43758517 3
## 151 71.47975462 4
## 152 71.59193492 3
## 153 71.73664150 2
## 154 72.18295910 1
## 155 72.24966940 0
## 156 73.62660416 1
## 157 73.69621302 2
## 158 74.70830630 1
## 159 76.15160999 2
## 160 76.23036160 1
## 161 76.61261297 0
## 162 81.35209627 1
## 163 81.54448970 2
## 164 81.59896172 3
## 165 81.78171489 2
## 166 81.82834796 1
## 167 81.99790155 0
## 168 82.02927648 1
## 169 82.36990105 0
## 170 83.46305169 1
## 171 84.58239168 0
## 172 84.79021620 1
## 173 84.97319933 2
## 174 85.20584517 1
## 175 85.44385001 2
## 176 86.45126390 1
## 177 86.56236478 0
## 178 88.37676210 1
## 179 88.68158536 0
## 180 90.41871042 1
## 181 91.05830452 0
## 182 91.17416971 1
## 183 91.37165292 2
## 184 91.74621791 1
## 185 91.81432420 0
## 186 91.84942917 1
## 187 91.86538885 0
## 188 92.30096161 1
## 189 92.30223706 2
## 190 92.38702003 3
## 191 92.48709653 2
## 192 93.07287600 1
## 193 93.21611884 2
## 194 95.30702356 1
## 195 96.13026393 2
## 196 96.36951695 1
## 197 96.49223633 2
## 198 96.64882595 1
## 199 96.77167502 0
## 200 98.10466732 1
## 201 98.19936282 0
##
## $servers_input
## [1] 2
Simulasi dilakukan dengan 2 server. Fungsi queue_step()
akan menghitung waktu keluar berdasarkan urutan kedatangan dan durasi
layanan.
Validasi Model M/M/2
#Validasi dengan M/M/2 Queue (λ = 1, μ = 0.9)
set.seed(1)
n_customers <-10^4
lambda_a <- 1/1
lambda_s <- 1/0.9
interarrivals <- rexp(n_customers, lambda_a)
arrivals <- cumsum(interarrivals)
service <- rexp(n_customers, lambda_s)
queue_output <- queue_step(arrivals = arrivals, service = service, servers = 2)
head(sort(depart(queue_output)))
## [1] 1.340151 2.288112 2.639976 2.796572 3.249794 5.714967
Simulasi sistem antrian M/M/2 (dua server,
kedatangan dan layanan eksponensial) dilakukan dengan 10.000 pelanggan.
Ini digunakan untuk memverifikasi apakah queuecomputer
menghasilkan output yang sesuai dengan teori.
Replikasi Hasil Teori untuk M/M/3
#Replikasi Teori M/M/3 Queue (λ = 2, μ = 1)
set.seed(1)
n_customers <- 5e6
lambda <- 2
mu <- 1
interarrivals <- rexp(n_customers, lambda)
arrivals <- cumsum(interarrivals)
service <- rexp(n_customers, mu)
K = 3
MM3 <- queue_step(arrivals = arrivals, service = service, servers = K)
summary(MM3)
## Total customers:
## 5000000
## Missed customers:
## 0
## Mean waiting time:
## 0.445
## Mean response time:
## 1.44
## Utilization factor:
## 0.666140156160826
## Mean queue length:
## 0.889
## Mean number of customers in system:
## 2.89
Dalam contoh ini, kita meniru sistem M/M/3 dengan 5
juta pelanggan. Ringkasan output (summary) menunjukkan
waktu tunggu rata-rata, utilisasi server, dan panjang antrian rata-rata,
yang mendekati hasil teori untuk sistem M/M/3.
Simulasi Antrian dengan Nama Pelanggan
set.seed(1)
interarrivals <- rexp(20, 1)
arrivals <- cumsum(interarrivals)
customers <- randomNames(20, name.order = "first.last")
service <- rexp(20, 0.5)
head(service)
## [1] 0.8979956 0.7591994 2.4975352 5.3868231 0.3926436 4.5996743
Kita membuat daftar nama pelanggan secara acak. Lalu kita simulasikan antrian untuk 20 pelanggan:
queue_obj <-queue_step(arrivals, service, servers = 2, labels = customers)
head(queue_obj$departures_df)
## # A tibble: 6 × 7
## labels arrivals service departures waiting system_time server
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <int>
## 1 Saadiq, el-Molla 0.755 0.898 1.65 1.11e-16 0.898 1
## 2 Jelicha, Kim 1.94 0.759 2.70 0 0.759 2
## 3 Briana, Lesley 2.08 2.50 4.58 0 2.50 1
## 4 Daeun, Her 2.22 5.39 8.08 4.74e- 1 5.86 2
## 5 Ruwaida, al-Bahri 2.66 0.393 4.97 1.92e+ 0 2.31 1
## 6 Fawzi, al-Jamail 5.55 4.60 10.2 0 4.60 1
Nama pelanggan ditambahkan sebagai label, dan hasil
queue_obj dapat diperiksa untuk melihat siapa dilayani oleh
server mana dan kapan mereka selesai.
firstcustomers <- arrivals[1:2] + service [1:2]
firstcustomers[2] + service[3]
## [1] 5.193559
depart(queue_obj)[1:3] - arrivals[1:3] - service[1:3]
## [1] 1.110223e-16 -1.110223e-16 0.000000e+00
Baris di atas menghitung waktu tunggu untuk tiga pelanggan pertama.
Analisis Statistik dan Visualisasi
summary(queue_obj)
## Total customers:
## 20
## Missed customers:
## 0
## Mean waiting time:
## 0.893
## Mean response time:
## 2.52
## Utilization factor:
## 0.700476008415974
## Mean queue length:
## 0.82
## Mean number of customers in system:
## 2.17
plot(queue_obj, which = c(2, 4, 5, 6))
## [[1]]
##
## [[2]]
##
## [[3]]
##
## [[4]]
Fungsi summary() memberikan ringkasan performa sistem,
dan plot() memvisualisasikan panjang antrian, waktu
layanan, dan distribusi kedatangan serta keberangkatan.
Kita bangun data simulasi 20 penumpang dengan rute imigrasi acak dan waktu layanan yang berbeda:
# Simulasi data 20 penumpang dengan rute imigrasi acak
set.seed(123)
n <- 20
Passenger_df <- tibble(
ID = paste0("P", 1:n),
route_imm = sample(c("smart gate", "manual"), n, replace = TRUE),
arrive_imm = sort(runif(n, 500, 800)), # waktu tiba di imigrasi
service_imm = rexp(n, rate = 1/3), # waktu layanan imigrasi
bag_time = runif(n, 550, 900) # waktu bagasi tiba
)
Definisi server per rute:
# Definisikan jumlah server per rute
server_df <- data.frame(route_imm = c("smart gate", "manual"))
server_df$servers <- list(
5, # smart gate: 5 server tetap
as.server.stepfun(x = c(600, 780), y = c(10, 12, 8)) # manual: server dinamis
)
Kita gabungkan konfigurasi server ke dalam data penumpang:
# Gabungkan informasi server ke data penumpang
Passenger_df <- left_join(Passenger_df, server_df, by = "route_imm")
Kemudian kita jalankan simulasi per grup (berdasarkan rute), dan hitung waktu keluarnya dari imigrasi dan dari area bagasi (fork/join):
Passenger_df <- Passenger_df %>%
group_by(route_imm) %>%
mutate(departures_imm = queue(arrive_imm, service_imm, servers = servers[[1]])) %>%
ungroup() %>%
mutate(departures_bc = pmax.int(departures_imm, bag_time)) # proses fork-join
Ringkasan waktu tunggu rata-rata:
Passenger_df %>%
group_by(route_imm) %>%
summarise(
waiting_imm = mean(departures_imm - service_imm - arrive_imm),
waiting_bc = mean(departures_bc - departures_imm)
)
## # A tibble: 2 × 3
## route_imm waiting_imm waiting_bc
## <chr> <dbl> <dbl>
## 1 manual 0 49.3
## 2 smart gate 0 76.3
Visualisasi Sistem Bandara
# Simulasi ulang untuk mendapatkan queue_list object
queue_obj <- queue_step(
arrivals = Passenger_df$arrive_imm,
service = Passenger_df$service_imm,
servers = 5
)
# Plot queue length dan lainnya
plot(queue_obj, which = c(2, 4, 5, 6))
## [[1]]
##
## [[2]]
##
## [[3]]
##
## [[4]]
Grafik yang dihasilkan menampilkan:
Visualisasi Tambahan: Boxplot Waktu Tunggu
dummy_data <- data.frame(
route_imm = rep(c("A", "B", "C"), each = 30),
waiting_imm = c(rnorm(30, 15, 5), rnorm(30, 30, 10), rnorm(30, 5, 2))
)
ggplot(dummy_data, aes(x = route_imm, y = waiting_imm, fill = route_imm)) +
geom_boxplot() +
labs(title = "Boxplot Waktu Tunggu (Contoh Data Dummy)")
Visualisasi ini memperlihatkan perbandingan waktu tunggu antar rute imigrasi menggunakan data dummy — menggambarkan bagaimana distribusi waktu tunggu bisa sangat berbeda tergantung beban layanan dan jumlah server.
Kesimpulan
Contoh-contoh ini menunjukkan bahwa:
queuecomputer mudah digunakan bahkan
untuk simulasi kompleks.dplyr dan ggplot2
memungkinkan analisis dan visualisasi yang interaktif dan
informatif.Jika Anda ingin, saya dapat menyusun ini menjadi file
.Rmd atau .pdf sebagai bagian dari laporan
tugas atau publikasi.
Paket R queuecomputer mengimplementasikan algoritma
Queue Departure Computation (QDC), yang dapat digunakan
untuk mensimulasikan sistem antrian atau
jaringan antrian berantai (tandem) dengan bentuk
umum:
\[ G(t)/G(t)/K(t)/\infty/n/\text{FCFS} \]
Beberapa algoritma cepat untuk sistem antrian multiserver sebenarnya telah diusulkan sebelumnya (misalnya oleh Krivulin, 1994; Sutton dan Jordan, 2010; Kin dan Chan, 2010), tetapi kurang mendapat perhatian, bahkan ketika efisiensinya telah dibuktikan (seperti pada Kin dan Chan, 2010).
Algoritma QDC telah divalidasi dengan dua pendekatan:
simmer dan
simpy.Hasilnya menunjukkan bahwa QDC mampu menghasilkan output yang sama akuratnya, tetapi dengan kecepatan komputasi yang jauh lebih tinggi mencapai percepatan hingga 1000 kali lipat.
dplyr untuk manipulasi data dan
ggplot2 untuk visualisasi.Kecepatan queuecomputer memungkinkan simulasi antrian
untuk disisipkan dalam algoritma Approximate Bayesian
Computation (ABC), yang akan dibahas lebih lanjut dalam
penelitian selanjutnya.
# Panggil library
library(queuecomputer)
library(ggplot2)
library(dplyr)
Studi Kasus: Antrian Pasien di Rumah Sakit Dalam studi kasus ini, dilakukan simulasi antrian pasien di sebuah rumah sakit dengan jumlah pasien sebanyak 100 orang. Waktu antar kedatangan pasien mengikuti distribusi eksponensial dengan rata-rata 1 pasien per menit, sedangkan waktu pelayanan setiap pasien rata-rata adalah 3 menit per pasien, juga mengikuti distribusi eksponensial.
Konfigurasi jumlah petugas (server) disesuaikan dengan kondisi waktu operasional, yaitu:
Pada 0–30 menit (jam sepi), tersedia 1 petugas,
Pada 30–60 menit (jam ramai), tersedia 2 petugas,
Setelah 60 menit (jam sepi kembali), jumlah petugas dikurangi menjadi 1 petugas.
Simulasi ini bertujuan untuk mengevaluasi performa sistem antrian, termasuk analisis waktu tunggu pasien, waktu pelayanan, jumlah pasien dalam sistem dari waktu ke waktu, serta visualisasi antrian secara menyeluruh.
#Langkah pertama Simulasi Data Pasien
# Jumlah pasien
set.seed(123)
n <- 100
# Waktu antar kedatangan ~ Exponential(1) → rata-rata 1 menit
arrivals <- cumsum(rexp(n, rate = 1))
# Waktu pelayanan ~ Exponential(1/3) → rata-rata 3 menit
service <- rexp(n, rate = 1/3)
# Tambahkan label pasien
labels_pasien <- paste0("Pasien-", 1:n)
Langkah Pertama: Simulasi Data Pasien Pada langkah ini, dilakukan proses simulasi data untuk 100 pasien yang akan dilayani oleh rumah sakit. Langkah-langkahnya dijelaskan sebagai berikut: set.seed(123) Digunakan untuk menetapkan seed angka acak agar hasil simulasi dapat direproduksi. Artinya, setiap kali kode dijalankan, hasil acaknya akan tetap sama. n <- 100 Menyatakan bahwa jumlah pasien yang akan disimulasikan sebanyak 100 orang. arrivals <- cumsum(rexp(n, rate = 1)) Menghasilkan waktu kedatangan pasien secara berurutan. Setiap pasien datang secara acak mengikuti distribusi eksponensial dengan rata-rata 1 menit. Fungsi cumsum() digunakan untuk mengakumulasi waktu antar kedatangan, sehingga menghasilkan waktu kedatangan kumulatif untuk setiap pasien. service <- rexp(n, rate = 1/3) Menghasilkan waktu pelayanan bagi masing-masing pasien yang juga bersifat acak dan mengikuti distribusi eksponensial, dengan rata-rata waktu pelayanan 3 menit per pasien. labels_pasien <- paste0(“Pasien-”, 1:n) Memberikan label atau nama identitas unik untuk masing-masing pasien, dari “Pasien-1” hingga “Pasien-100”. Label ini akan digunakan dalam visualisasi untuk mempermudah identifikasi setiap pasien dalam antrian.
# Langkah kedua mengonfigurasi Server yang Berubah
# Jadwal server:
# 0–30 menit: 1 dokter (sepi)
# 30–60 menit: 2 dokter (ramai)
# >60 menit: kembali 1 dokter
server_shift <- as.server.stepfun(
x = c(30, 60),
y = c(1, 2, 1)
)
Langkah Kedua: Mengonfigurasi Server yang Berubah Pada bagian ini, dilakukan konfigurasi terhadap jumlah petugas (dalam hal ini dokter) yang melayani pasien di rumah sakit, dengan memperhatikan perubahan jumlah server berdasarkan rentang waktu tertentu. server_shift <- as.server.stepfun(…) Mendefinisikan fungsi tangga (step function) untuk menggambarkan perubahan jumlah server seiring waktu. Fungsi ini menghasilkan objek server_shift yang dapat digunakan dalam simulasi antrian dinamis menggunakan queuecomputer. x = c(30, 60) Titik waktu di mana perubahan jumlah server terjadi. Artinya: Perubahan pertama terjadi pada menit ke-30 Perubahan kedua terjadi pada menit ke-60 y = c(1, 2, 1) Jumlah server (dokter) yang aktif pada setiap interval waktu: Dari waktu 0 hingga <30 menit, tersedia 1 dokter (kondisi sepi) Dari waktu 30 hingga <60 menit, tersedia 2 dokter (kondisi ramai) Dari waktu 60 menit ke atas, kembali tersedia 1 dokter (kondisi sepi) Konfigurasi ini memungkinkan simulasi untuk menangani skenario realistik di mana jumlah tenaga medis dapat disesuaikan berdasarkan kondisi waktu operasional, seperti jam ramai dan jam sepi.
#langkah ke tiga Simulasi Antrian dengan queue_step()
queue_obj <- queue_step(
arrivals = arrivals,
service = service,
servers = server_shift,
labels = labels_pasien
)
Langkah Ketiga: Simulasi Antrian Pasien Langkah ini merupakan inti dari proses simulasi, yaitu menjalankan sistem antrian berdasarkan data kedatangan, waktu pelayanan, jumlah server, dan identitas pasien.
#Ringkasan Performa Antrian
summary(queue_obj)
## Total customers:
## 100
## Missed customers:
## 0
## Mean waiting time:
## 59.8
## Mean response time:
## 62.7
## Utilization factor:
## 1.00480969299063
## Mean queue length:
## 23.3
## Mean number of customers in system:
## 24.2
Simulasi menunjukkan bahwa rumah sakit mengalami beban antrian yang tinggi, dengan waktu tunggu rata-rata hampir 1 jam dan utilisasi melebihi kapasitas normal. Hal ini mengindikasikan bahwa konfigurasi jumlah server (dokter) yang tersedia masih belum optimal dalam menangani jumlah kedatangan pasien yang tinggi, terutama di jam-jam sibuk.
#Visualisasi Queue Length dan Sistem
plot(queue_obj, which = c(2, 4, 5, 6)) # default visualisasi bawaan
## [[1]]
##
## [[2]]
##
## [[3]]
##
## [[4]]
#Data Frame Untuk Analisis
queue_df <- queue_obj$departures_df %>%
mutate(
waiting_time = departures - arrivals - service,
start_time = departures - service,
label = factor(labels, levels = labels) # agar urutan benar
)
#Boxplot Waktu Tunggu Pasien
ggplot(queue_df, aes(y = waiting_time)) +
geom_boxplot(fill = "steelblue") +
labs(
title = "Boxplot Waktu Tunggu Pasien",
y = "Waktu Tunggu (menit)",
x = NULL
) +
theme_minimal()
#Gantt Chart Pelayanan per Pasien
ggplot(queue_df, aes(y = label)) +
geom_segment(
aes(x = start_time, xend = departures, yend = label),
linewidth = 4, color = "darkgreen"
) +
labs(
title = "Gantt Chart Pelayanan Pasien",
x = "Waktu (menit)",
y = "Pasien"
) +
theme_minimal()
# 1. Install dan panggil library
library(queuecomputer)
library(ggplot2)
library(dplyr)
Baris diatas digunakan untuk memasang dan memanggil library yang dibutuhkan. queuecomputer → untuk simulasi antrian. ggplot2 dan dplyr → untuk visualisasi dan manipulasi data.
# 2. Simulasi Data Drive-Thru
set.seed(2025)
n_pelanggan <- 120
kedatangan <- cumsum(rexp(n_pelanggan, rate = 1/3)) # ~1 mobil tiap 3 menit
pelayanan <- rexp(n_pelanggan, rate = 1/5) # ~5 menit waktu pelayanan
labels <- paste("Mobil", 1:n_pelanggan)
set.seed() menjamin hasil simulasi konsisten (reproducible). Simulasi diatas membuat 120 pelanggan (mobil) yang datang dalam satu hari. kedatangan dihitung dengan distribusi eksponensial → merepresentasikan kedatangan acak setiap ±3 menit. pelayanan juga eksponensial → waktu cuci mobil bervariasi, rata-rata 5 menit. labels digunakan untuk memberi nama identitas tiap mobil (Mobil 1, Mobil 2, dst)
# 3. Simulasi Antrian dengan 3 Jalur Drive-Thru
dt_queue <- queue_step(arrivals = kedatangan,
service = pelayanan,
servers = 3,
labels = labels)
Fungsi queue_step() menjalankan simulasi antrian drive-thru. Parameter: arrivals: waktu kedatangan tiap mobil, service: waktu pelayanan tiap mobil, servers = 3: sistem memiliki 3 jalur pelayanan paralel, labels: nama mobil (untuk pelacakan). Hasilnya adalah objek simulasi yang menyimpan waktu tunggu, waktu selesai pelayanan, dll.
# 4. Ringkasan Statistik Sistem Antrian
summary(dt_queue)
## Total customers:
## 120
## Missed customers:
## 0
## Mean waiting time:
## 0.688
## Mean response time:
## 5.77
## Utilization factor:
## 0.583258277850833
## Mean queue length:
## 0.242
## Mean number of customers in system:
## 1.99
Menampilkan statistik kinerja sistem antrian, seperti: Mean waiting time → rata-rata waktu pelanggan menunggu giliran, Mean response time → total waktu dalam sistem (tunggu + pelayanan), Utilization factor → seberapa sibuk server (jalur drive-thru), Missed customers → pelanggan yang tidak dilayani (dalam simulasi ini = 0).
# 5. Dataframe hasil simulasi
df_dt <- dt_queue$departures_df
Mengambil hasil simulasi dalam bentuk dataframe, berisi: Nama mobil,Waktu kedatangan,Waktu mulai dan selesai pelayanan,Server yang melayani.
# 6. Tambahkan kolom waktu_tunggu dan jam kedatangan
df_dt <- df_dt %>%
mutate(waktu_tunggu = departures - arrivals - service,
jam = floor(arrivals / 60)) # jam sejak simulasi dimulai
Menambahkan 2 kolom baru: waktu_tunggu: total waktu mobil menunggu sebelum dilayani. jam: untuk melihat jam ke berapa pelanggan datang (dibagi per 60 menit).
# 7. Plot 1: Histogram Waktu Tunggu
ggplot(df_dt, aes(x = waktu_tunggu)) +
geom_histogram(binwidth = 0.5, fill = "steelblue", color = "red") +
labs(title = "Distribusi Waktu Tunggu Pelanggan Drive-Thru",
x = "Waktu Tunggu (menit)", y = "Jumlah Pelanggan")
Menampilkan distribusi jumlah pelanggan berdasarkan waktu tunggu.Digunakan untuk mengetahui apakah mayoritas pelanggan harus menunggu lama atau tidak.
# 8. Plot 2: Boxplot Waktu Tunggu
ggplot(df_dt, aes(y = waktu_tunggu)) +
geom_boxplot(fill = "tomato", alpha = 0.7) +
labs(title = "Boxplot Waktu Tunggu Pelanggan", y = "Waktu Tunggu (menit)")
Memberikan gambaran statistik waktu tunggu:Median, kuartil, dan outlier.Sangat berguna untuk melihat sebaran dan ekstrem dari waktu tunggu pelanggan.
# 9.Plot 3: Rata-rata Waktu Tunggu Tiap Jam
df_dt %>%
group_by(jam) %>%
summarise(rata_waktu_tunggu = mean(waktu_tunggu)) %>%
ggplot(aes(x = jam, y = rata_waktu_tunggu)) +
geom_line(color = "darkgreen", linewidth = 1.2) +
geom_point(size = 2) +
labs(title = "Rata-rata Waktu Tunggu Pelanggan per Jam",
x = "Jam ke-", y = "Rata-rata Waktu Tunggu (menit)")
Mengelompokkan pelanggan berdasarkan jam kedatangan.Menunjukkan jam-jam sibuk dengan waktu tunggu paling tinggi.
# 10. 📊 Plot 4: Timeline Waktu Tiba vs Waktu Selesai
ggplot(df_dt, aes(x = arrivals, xend = departures, y = labels, yend = labels)) +
geom_segment(color = "purple") +
labs(title = "Timeline Kedatangan dan Selesai Pelayanan per Mobil",
x = "Waktu (menit)", y = "Mobil") +
theme(axis.text.y = element_blank())
Visualisasi timeline setiap mobil: kapan mereka datang dan kapan selesai dilayani.Sangat informatif untuk melihat tumpang tindih layanan dan durasi pelayanan tiap mobil.
Kedua studi kasus ini mengembangkan dan memperkuat klaim dari paper bahwa:
queuecomputer efisien untuk simulasi antrian dalam skenario realistis — dari yang paling sederhana (drive-thru) hingga kompleks (rumah sakit).
Pendekatan non-event-driven memudahkan implementasi tanpa overhead besar.
Sangat cocok untuk eksperimen kebijakan operasional, baik dalam pengaturan waktu shift, penambahan kapasitas, maupun analisis bottleneck.