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")
# Membuat data waktu kedatangan (arrival) sebanyak 100 pelanggan
arrivals <- cumsum(rexp(100)) # waktu antar kedatangan eksponensial
head(arrivals)
## [1] 1.939816 3.329269 3.410338 3.551769 5.744942 6.833120
#Contoh Dasar: Simulasi Queue Sederhana
service <-rexp(100)
departures <- queue_step(arrivals, service = service, servers = 2)
departures
## $departures
## [1] 4.234026 3.803542 4.685955 4.709399 6.046283 10.735276
## [7] 8.937169 9.698598 9.758510 12.723962 17.206939 16.665389
## [13] 17.428498 19.587427 18.814935 20.855473 21.359282 21.244272
## [19] 21.321117 21.879114 21.489703 22.047452 24.018391 22.166933
## [25] 22.855299 25.871338 24.400491 28.760298 27.331333 27.633126
## [31] 28.415590 28.435419 28.606895 29.315124 29.991385 30.174557
## [37] 32.682886 31.770106 33.061685 33.353945 33.490430 37.827825
## [43] 36.262689 38.485274 37.970623 38.820077 40.061914 40.990822
## [49] 43.450010 41.346024 41.702979 42.468652 43.107421 44.090491
## [55] 45.750312 46.983952 48.552913 50.570923 51.167824 51.124480
## [61] 51.848192 52.520343 57.324796 56.536881 60.871165 59.847537
## [67] 60.526069 61.652309 69.205572 71.046431 69.339193 69.371069
## [73] 70.389037 72.612367 74.385872 76.867629 78.632418 80.035531
## [79] 79.996758 80.687793 83.973044 83.654381 84.900115 85.908104
## [85] 86.537048 86.705547 87.547104 90.076149 90.090749 92.054227
## [91] 90.451409 94.838885 96.944200 100.997834 101.467455 102.049077
## [97] 101.851202 103.010745 102.259315 103.398549
##
## $server
## [1] 1 2 2 1 2 1 2 2 2 2 1 2 2 1 2 2 1 2 2 2 1 1 2 1 1 1 2 2 1 1 1 1 1 1 2 1 2
## [38] 1 1 2 1 2 1 1 2 2 1 2 1 2 2 2 2 2 1 2 1 2 1 2 2 1 2 1 1 2 2 2 1 2 1 1 1 1
## [75] 2 1 2 1 2 2 1 2 2 1 2 1 2 1 2 1 2 2 1 2 1 2 1 1 2 2
##
## $departures_df
## # A tibble: 100 × 6
## arrivals service departures waiting system_time server
## <dbl> <dbl> <dbl> <dbl> <dbl> <int>
## 1 1.94 2.29 4.23 0 2.29 1
## 2 3.33 0.474 3.80 1.67e-16 0.474 2
## 3 3.41 0.882 4.69 3.93e- 1 1.28 2
## 4 3.55 0.475 4.71 6.82e- 1 1.16 1
## 5 5.74 0.301 6.05 0 0.301 2
## 6 6.83 3.90 10.7 0 3.90 1
## 7 7.02 1.92 8.94 0 1.92 2
## 8 7.34 0.761 9.70 1.60e+ 0 2.36 2
## 9 7.76 0.0599 9.76 1.94e+ 0 2.00 2
## 10 12.3 0.449 12.7 0 0.449 2
## # ℹ 90 more rows
##
## $queuelength_df
## times queuelength
## 1 0.000000 0
## 2 1.939816 1
## 3 1.939816 0
## 4 3.329269 1
## 5 3.329269 0
## 6 3.410338 1
## 7 3.551769 2
## 8 3.803542 1
## 9 4.234026 0
## 10 5.744942 1
## 11 5.744942 0
## 12 6.833120 1
## 13 6.833120 0
## 14 7.019520 1
## 15 7.019520 0
## 16 7.337512 1
## 17 7.757812 2
## 18 8.937169 1
## 19 9.698598 0
## 20 12.275006 1
## 21 12.275006 0
## 22 15.286441 1
## 23 15.286441 0
## 24 15.790852 1
## 25 15.790852 0
## 26 16.279805 1
## 27 16.446281 2
## 28 16.665389 1
## 29 16.845372 2
## 30 17.206939 1
## 31 17.428498 0
## 32 17.509171 1
## 33 17.734703 2
## 34 18.025341 3
## 35 18.213452 4
## 36 18.611648 5
## 37 18.685197 6
## 38 18.814935 5
## 39 19.263353 6
## 40 19.513265 7
## 41 19.587427 6
## 42 20.855473 5
## 43 21.244272 4
## 44 21.321117 3
## 45 21.359282 2
## 46 21.489703 1
## 47 21.543057 2
## 48 21.571897 3
## 49 21.879114 2
## 50 22.047452 1
## 51 22.166933 0
## 52 23.448385 1
## 53 23.448385 0
## 54 23.692617 1
## 55 24.018391 0
## 56 25.190822 1
## 57 25.190822 0
## 58 25.340545 1
## 59 25.536022 2
## 60 25.871338 1
## 61 26.065520 2
## 62 26.831113 3
## 63 27.061170 4
## 64 27.331333 3
## 65 27.633126 2
## 66 28.415590 1
## 67 28.435419 0
## 68 28.807416 1
## 69 28.807416 0
## 70 29.249020 1
## 71 29.249020 0
## 72 29.956167 1
## 73 29.956167 0
## 74 30.190540 1
## 75 30.190540 0
## 76 30.370870 1
## 77 30.370870 0
## 78 32.865171 1
## 79 32.865171 0
## 80 33.019653 1
## 81 33.019653 0
## 82 33.294503 1
## 83 33.294503 0
## 84 35.958092 1
## 85 35.958092 0
## 86 36.190063 1
## 87 36.190063 0
## 88 37.250795 1
## 89 37.250795 0
## 90 37.751649 1
## 91 37.827825 0
## 92 38.647400 1
## 93 38.647400 0
## 94 39.583848 1
## 95 39.583848 0
## 96 39.795739 1
## 97 39.795739 0
## 98 40.525734 1
## 99 40.525734 0
## 100 40.577817 1
## 101 40.990822 0
## 102 41.336541 1
## 103 41.346024 0
## 104 41.972494 1
## 105 41.972494 0
## 106 42.296708 1
## 107 42.468652 0
## 108 42.974228 1
## 109 43.107421 0
## 110 44.774941 1
## 111 44.774941 0
## 112 46.520197 1
## 113 46.520197 0
## 114 47.731222 1
## 115 47.731222 0
## 116 49.283231 1
## 117 49.283231 0
## 118 49.923808 1
## 119 49.923808 0
## 120 50.481586 1
## 121 50.570923 0
## 122 51.224949 1
## 123 51.224949 0
## 124 52.238063 1
## 125 52.238063 0
## 126 54.194700 1
## 127 54.194700 0
## 128 55.091958 1
## 129 55.091958 0
## 130 59.365842 1
## 131 59.365842 0
## 132 59.555924 1
## 133 59.555924 0
## 134 59.567034 1
## 135 59.847537 0
## 136 60.890439 1
## 137 60.890439 0
## 138 66.451773 1
## 139 66.451773 0
## 140 66.704726 1
## 141 66.704726 0
## 142 67.005259 1
## 143 68.477497 2
## 144 69.205572 1
## 145 69.339193 0
## 146 70.306576 1
## 147 70.306576 0
## 148 72.510249 1
## 149 72.510249 0
## 150 72.927377 1
## 151 72.927377 0
## 152 75.681242 1
## 153 75.681242 0
## 154 77.130847 1
## 155 77.130847 0
## 156 79.927714 1
## 157 79.927714 0
## 158 79.944131 1
## 159 79.944131 0
## 160 80.666220 1
## 161 80.666220 0
## 162 80.873426 1
## 163 80.873426 0
## 164 83.515027 1
## 165 83.515027 0
## 166 84.609394 1
## 167 84.609394 0
## 168 85.800510 1
## 169 85.800510 0
## 170 85.843323 1
## 171 85.843323 0
## 172 86.089132 1
## 173 86.089132 0
## 174 86.799425 1
## 175 86.799425 0
## 176 89.681339 1
## 177 89.681339 0
## 178 89.848693 1
## 179 89.848693 0
## 180 89.878493 1
## 181 89.906807 2
## 182 90.076149 1
## 183 90.090749 0
## 184 94.808806 1
## 185 94.808806 0
## 186 95.977347 1
## 187 95.977347 0
## 188 98.561529 1
## 189 98.561529 0
## 190 99.548938 1
## 191 99.548938 0
## 192 100.122008 1
## 193 100.387175 2
## 194 100.964702 3
## 195 100.997834 2
## 196 101.165768 3
## 197 101.467455 2
## 198 101.851202 1
## 199 102.049077 0
## 200 103.027239 1
## 201 103.027239 0
##
## $systemlength_df
## times queuelength
## 1 0.000000 0
## 2 1.939816 1
## 3 3.329269 2
## 4 3.410338 3
## 5 3.551769 4
## 6 3.803542 3
## 7 4.234026 2
## 8 4.685955 1
## 9 4.709399 0
## 10 5.744942 1
## 11 6.046283 0
## 12 6.833120 1
## 13 7.019520 2
## 14 7.337512 3
## 15 7.757812 4
## 16 8.937169 3
## 17 9.698598 2
## 18 9.758510 1
## 19 10.735276 0
## 20 12.275006 1
## 21 12.723962 0
## 22 15.286441 1
## 23 15.790852 2
## 24 16.279805 3
## 25 16.446281 4
## 26 16.665389 3
## 27 16.845372 4
## 28 17.206939 3
## 29 17.428498 2
## 30 17.509171 3
## 31 17.734703 4
## 32 18.025341 5
## 33 18.213452 6
## 34 18.611648 7
## 35 18.685197 8
## 36 18.814935 7
## 37 19.263353 8
## 38 19.513265 9
## 39 19.587427 8
## 40 20.855473 7
## 41 21.244272 6
## 42 21.321117 5
## 43 21.359282 4
## 44 21.489703 3
## 45 21.543057 4
## 46 21.571897 5
## 47 21.879114 4
## 48 22.047452 3
## 49 22.166933 2
## 50 22.855299 1
## 51 23.448385 2
## 52 23.692617 3
## 53 24.018391 2
## 54 24.400491 1
## 55 25.190822 2
## 56 25.340545 3
## 57 25.536022 4
## 58 25.871338 3
## 59 26.065520 4
## 60 26.831113 5
## 61 27.061170 6
## 62 27.331333 5
## 63 27.633126 4
## 64 28.415590 3
## 65 28.435419 2
## 66 28.606895 1
## 67 28.760298 0
## 68 28.807416 1
## 69 29.249020 2
## 70 29.315124 1
## 71 29.956167 2
## 72 29.991385 1
## 73 30.174557 0
## 74 30.190540 1
## 75 30.370870 2
## 76 31.770106 1
## 77 32.682886 0
## 78 32.865171 1
## 79 33.019653 2
## 80 33.061685 1
## 81 33.294503 2
## 82 33.353945 1
## 83 33.490430 0
## 84 35.958092 1
## 85 36.190063 2
## 86 36.262689 1
## 87 37.250795 2
## 88 37.751649 3
## 89 37.827825 2
## 90 37.970623 1
## 91 38.485274 0
## 92 38.647400 1
## 93 38.820077 0
## 94 39.583848 1
## 95 39.795739 2
## 96 40.061914 1
## 97 40.525734 2
## 98 40.577817 3
## 99 40.990822 2
## 100 41.336541 3
## 101 41.346024 2
## 102 41.702979 1
## 103 41.972494 2
## 104 42.296708 3
## 105 42.468652 2
## 106 42.974228 3
## 107 43.107421 2
## 108 43.450010 1
## 109 44.090491 0
## 110 44.774941 1
## 111 45.750312 0
## 112 46.520197 1
## 113 46.983952 0
## 114 47.731222 1
## 115 48.552913 0
## 116 49.283231 1
## 117 49.923808 2
## 118 50.481586 3
## 119 50.570923 2
## 120 51.124480 1
## 121 51.167824 0
## 122 51.224949 1
## 123 51.848192 0
## 124 52.238063 1
## 125 52.520343 0
## 126 54.194700 1
## 127 55.091958 2
## 128 56.536881 1
## 129 57.324796 0
## 130 59.365842 1
## 131 59.555924 2
## 132 59.567034 3
## 133 59.847537 2
## 134 60.526069 1
## 135 60.871165 0
## 136 60.890439 1
## 137 61.652309 0
## 138 66.451773 1
## 139 66.704726 2
## 140 67.005259 3
## 141 68.477497 4
## 142 69.205572 3
## 143 69.339193 2
## 144 69.371069 1
## 145 70.306576 2
## 146 70.389037 1
## 147 71.046431 0
## 148 72.510249 1
## 149 72.612367 0
## 150 72.927377 1
## 151 74.385872 0
## 152 75.681242 1
## 153 76.867629 0
## 154 77.130847 1
## 155 78.632418 0
## 156 79.927714 1
## 157 79.944131 2
## 158 79.996758 1
## 159 80.035531 0
## 160 80.666220 1
## 161 80.687793 0
## 162 80.873426 1
## 163 83.515027 2
## 164 83.654381 1
## 165 83.973044 0
## 166 84.609394 1
## 167 84.900115 0
## 168 85.800510 1
## 169 85.843323 2
## 170 85.908104 1
## 171 86.089132 2
## 172 86.537048 1
## 173 86.705547 0
## 174 86.799425 1
## 175 87.547104 0
## 176 89.681339 1
## 177 89.848693 2
## 178 89.878493 3
## 179 89.906807 4
## 180 90.076149 3
## 181 90.090749 2
## 182 90.451409 1
## 183 92.054227 0
## 184 94.808806 1
## 185 94.838885 0
## 186 95.977347 1
## 187 96.944200 0
## 188 98.561529 1
## 189 99.548938 2
## 190 100.122008 3
## 191 100.387175 4
## 192 100.964702 5
## 193 100.997834 4
## 194 101.165768 5
## 195 101.467455 4
## 196 101.851202 3
## 197 102.049077 2
## 198 102.259315 1
## 199 103.010745 0
## 200 103.027239 1
## 201 103.398549 0
##
## $servers_input
## [1] 2
##
## $state
## [1] 103.0107 103.3985
##
## 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.42
## Mean response time:
## 1.38
## Utilization factor:
## 0.466483447643133
## Mean queue length:
## 0.407
## Mean number of customers in system:
## 1.34
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)
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)
library(ggplot2)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following object is masked from 'package:simmer':
##
## select
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
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.