<?php /******************************/ /* 簡単な待ち行列問題(M/M/s)*/ /* coded by Y.Suganuma */ /******************************/ /************************/ /* クラスCustomerの定義 */ /************************/ class Customer { public $time; // 到着時刻 public $state; // 客の状態 // =-1 : 待ち行列に入っている // >=0 : サービスを受けている窓口番号 /*********************/ /* コンストラクタ */ /* s : 状態 */ /* t : 到着時刻 */ /*******************************/ function Customer($s, $t) { $this->time = $t; $this->state = $s; } } /**********************/ /* クラスQ_baseの定義 */ /**********************/ class Q_base { private $s; // 窓口の数 private $asb; // 全窓口の空き状況 // =0 : すべての窓口がふさがっている // =n : n個の窓口が空いている private $sb; // 各窓口の空き状況 // =0 : 窓口は空いている // >0 : サービスを受けている客番号 private $asb_t; // すべての窓口がふさがった時刻 private $c_asb; // すべての窓口がふさがっている時間の累計 private $c_sb; // 各窓口がふさがっている時間の累計 private $st_e; // 各窓口のサービス終了時刻 private $st_s; // 各窓口がふさがった時刻 private $m_lq; // 最大待ち行列長 private $c_lq_t; // 待ち行列長にその長さが継続した時間を乗じた値の累計 private $c_wt; // 待ち時間の累計 private $lq_t; // 現在の待ち行列長になった時刻 private $m_wt; // 最大待ち時間 private $c_sc_t; // 系内客数にその数が継続した時間を乗じた値の累計 private $c_sys; // 滞在時間の累計 private $m_sys; // 最大滞在時間 private $sc_t; // 現在の系内客数になった時刻 private $m_sc; // 最大系内客数 private $m_a; // 到着時間間隔の平均値 private $m_s; // サービス時間の平均値 private $at; // 次の客の到着時刻(負の時は,終了) private $p_time; // 現在時刻 private $end; // シミュレーション終了時刻 private $nc; // 到着客数カウンタ private $cus; // 系内にいる客のリスト private $que; // 待ち行列 /*****************************************/ /* コンストラクタ */ /* s_i : 窓口の数 */ /* m_a_i : 到着時間間隔の平均値 */ /* m_s_i : サービス時間の平均値 */ /* end_i : シミュレーション終了時刻 */ /*****************************************/ function Q_base ($s_i, $m_a_i, $m_s_i, $end_i) { /* 設定 */ $this->s = $s_i; $this->m_a = $m_a_i; $this->m_s = $m_s_i; $this->end = $end_i; /* 領域の確保 */ $this->sb = array(); $this->c_sb = array(); $this->st_e = array(); $this->st_s = array(); $this->cus = array(); $this->que = array(); for ($i1 = 0; $i1 < $this->s; $i1++) { $this->sb[$i1] = 0; $this->c_sb[$i1] = 0.0; } /* 初期設定 */ $this->p_time = 0.0; $this->nc = 0; $this->asb = $this->s; $this->m_lq = 0; $this->m_sc = 0; $this->c_asb = 0.0; $this->c_wt = 0.0; $this->m_wt = 0.0; $this->c_lq_t = 0.0; $this->lq_t = 0.0; $this->m_sys = 0.0; $this->c_sys = 0.0; $this->c_sc_t = 0.0; $this->sc_t = 0.0; /* 乱数の初期設定 */ mt_srand(); /* 最初の客の到着時刻の設定 */ $this->at = $this->p_time + $this->Next_at(); } /********************************/ /* 次の客の到着までの時間の発生 */ /********************************/ function Next_at() { return -$this->m_a * log(mt_rand() / mt_getrandmax()); } /************************/ /* サービス時間の発生 */ /************************/ function Next_sv() { return -$this->m_s * log(mt_rand() / mt_getrandmax()); } /**************/ /* 全体の制御 */ /**************/ function Control() { $sw = 0; while ($sw > -2) { $sw = $this->Next(); // 次の処理の選択 if ($sw == -1) $sw = $this->End_o_s(); // シミュレーションの終了 else { if ($sw == 0) $this->Arrive(); // 客の到着処理 else $this->Service($sw); // サービスの終了 } } } /**************************************************/ /* 次の処理の決定 */ /* return : =-1 : シミュレーションの終了 */ /* =0 : 客の到着処理 */ /* =i : i番目の窓口のサービス終了 */ /**************************************************/ function Next() { $sw = -1; $t = $this->end; // シミュレーション終了時刻で初期設定 // 次の客の到着時刻 if ($this->at >= 0.0 && $this->at < $t) { $sw = 0; $t = $this->at; } // サービス終了時刻 for ($i1 = 0; $i1 < $this->s; $i1++) { if ($this->sb[$i1] > 0 && $this->st_e[$i1] <= $t) { $sw = $i1 + 1; $t = $this->st_e[$i1]; // 窓口i1のサービス終了時刻 } } return $sw; } /**********************************/ /* 終了処理 */ /* return : =-1 : 終了前処理 */ /* =-2 : 実際の終了 */ /**********************************/ function End_o_s() { $sw = -2; $this->p_time = $this->end; // 現在時刻 $this->at = -1.0; // 次の客の到着時刻 for ($i1 = 0; $i1 < $this->s; $i1++) { if ($this->sb[$i1] > 0) { // サービス中の客はいるか? if ($sw == -2) { $sw = -1; $this->end = $this->st_e[$i1]; // 窓口i1のサービス終了時刻 } else { if ($this->st_e[$i1] > $this->end) $this->end = $this->st_e[$i1]; // 窓口i1のサービス終了時刻 } } } return $sw; } /****************/ /* 客の到着処理 */ /****************/ function Arrive() { /* 客数の増加と次の客の到着時刻の設定 */ $this->nc += 1; // 到着客数カウンタ $this->p_time = $this->at; // 現在時刻 $this->at = $this->p_time + $this->Next_at(); // 次の客の到着時刻 if ($this->at >= $this->end) $this->at = -1.0; /* 系内客数の処理 */ $this->c_sc_t += (count($this->cus) * ($this->p_time - $this->sc_t)); // 系内客数にその数が継続した時間を乗じた値の累計 $this->sc_t = $this->p_time; // 現在の系内客数になった時刻 if (count($this->cus)+1 > $this->m_sc) $this->m_sc = count($this->cus) + 1; // 最大系内客数 /* 窓口に空きがない場合 */ if ($this->asb == 0) { $ct_p = new Customer(-1, $this->p_time); $this->cus['no'.strval($this->nc)] = $ct_p; // 客の登録(系内客数) $this->c_lq_t += count($this->que) * ($this->p_time - $this->lq_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 array_push($this->que, $this->nc); // 客の登録(待ち行列) $this->lq_t = $this->p_time; // 現在の待ち行列長になった時刻 if (count($this->que) > $this->m_lq) $this->m_lq = count($this->que); // 最大待ち行列長 } /* すぐサービスを受けられる場合 */ else { $k = -1; for ($i1 = 0; $i1 < $this->s && $k < 0; $i1++) { if ($this->sb[$i1] == 0) { $ct_p = new Customer($i1, $this->p_time); $this->cus['no'.strval($this->nc)] = $ct_p; // 客の登録(系内客数) $k = $i1; $this->sb[$k] = $this->nc; // サービスを受けている客番号 $this->st_e[$k] = $this->p_time + $this->Next_sv(); // 窓口kのサービス終了時刻 $this->asb -= 1; // 空いている窓口数 } } $this->st_s[$k] = $this->p_time; // 窓口kがふさがった時刻 if ($this->asb == 0) $this->asb_t = $this->p_time; // すべての窓口がふさがった時刻 } } /*********************************/ /* サービス終了時の処理 */ /* k : サービス終了窓口番号 */ /*********************************/ function Service($k) { /* 時間の設定 */ $k -= 1; $this->p_time = $this->st_e[$k]; // 現在時刻 $this->st_e[$k] = -1.0; // サービス終了時間 /* 系内客数の処理 */ $this->c_sc_t += (count($this->cus) * ($this->p_time - $this->sc_t)); // 系内客数にその数が継続した時間を乗じた値の累計 $this->sc_t = $this->p_time; // 現在の系内客数になった時刻 /* 滞在時間の処理 */ $it = $this->cus['no'.strval($this->sb[$k])]; $x1 = $this->p_time - $it->time; $this->c_sys += $x1; // 滞在時間の累計 if ($x1 > $this->m_sys) $this->m_sys = $x1; // 最大滞在時間 unset($this->cus['no'.strval($this->sb[$k])]); // 客の削除(系内客数) /* 窓口使用時間の処理 */ $this->asb += 1; // 空いている窓口数 $this->sb[$k] = 0; // 窓口kを空き状態にする $this->c_sb[$k] += ($this->p_time - $this->st_s[$k]); // 窓口kがふさがっている時間の累計 /* 待ち行列がある場合 */ if (count($this->que) > 0) { $this->asb -= 1; // 開いている窓口数 $this->c_lq_t += count($this->que) * ($this->p_time - $this->lq_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 $n = array_shift($this->que); // 客の削除(待ち行列) $this->lq_t = $this->p_time; // 現在の待ち行列長になった時刻 $it = $this->cus['no'.strval($n)]; $x1 = $this->p_time - $it->time; $this->c_wt += $x1; // 待ち時間の累計 if ($x1 > $this->m_wt) $this->m_wt = $x1; // 最大待ち時間 $k = -1; for ($i1 = 0; $i1 < $this->s && $k < 0; $i1++) { if ($this->sb[$i1] == 0) { $k = $i1; $this->sb[$k] = $n; // 窓口kの客番号 $this->st_e[$k] = $this->p_time + $this->Next_sv(); // 窓口kのサービス終了時刻 $this->st_s[$k] = $this->p_time; // 窓口kがふさがった時刻 $it->state = $k; // 客の状態変更 } } } /* 待ち行列がない場合 */ else { if ($this->asb == 1) $this->c_asb += ($this->p_time - $this->asb_t); // すべての窓口がふさがっている時間の累計 } } /************************/ /* 結果の出力 */ /* (カッコ内は理論値) */ /************************/ function Output() { $rn = floatval($this->nc); $rs = floatval($this->s); $ram = 1.0 / $this->m_a; $myu = 1.0 / $this->m_s; $rou = $ram / ($rs * $myu); if ($this->s == 1) { $p0 = 1.0 - $rou; $pi = $rou; } else { $p0 = 1.0 / (1.0 + 2.0 * $rou + 4.0 * $rou * $rou / (2.0 * (1.0 - $rou))); $pi = 4.0 * $rou * $rou * $p0 / (2.0 * (1.0 - $rou)); } $Lq = $pi * $rou / (1.0 - $rou); $L = $Lq + $rs * $rou; $Wq = $Lq / $ram; $W = $Wq + 1.0 / $myu; printf("シミュレーション終了時間=%.3f 客数=%d ρ=%.3f p0=%.3f\n", $this->p_time, $this->nc, $rou, $p0); printf(" すべての窓口が塞がっている割合=%.3f (%.3f)\n", $this->c_asb/$this->p_time, $pi); printf(" 各窓口が塞がっている割合\n"); for ($i1 = 0; $i1 < $this->s; $i1++) printf(" %d %.3f\n", $i1+1, $this->c_sb[$i1]/$this->p_time); printf(" 平均待ち行列長=%.3f (%.3f) 最大待ち行列長=%d\n", $this->c_lq_t/$this->p_time, $Lq, $this->m_lq); printf(" 平均系内客数 =%.3f (%.3f) 最大系内客数 =%d\n", $this->c_sc_t/$this->p_time, $L, $this->m_sc); printf(" 平均待ち時間 =%.3f (%.3f) 最大待ち時間 =%.3f\n", $this->c_wt/$rn, $Wq, $this->m_wt); printf(" 平均滞在時間 =%.3f (%.3f) 最大滞在時間 =%.3f\n", $this->c_sys/$rn, $W, $this->m_sys); } } /****************/ /* main program */ /****************/ /* 入力データ */ printf("窓口の数は? "); fscanf(STDIN, "%d", $s); printf("シミュレーション終了時間? "); fscanf(STDIN, "%lf", $end); printf(" 到着時間間隔の平均値は? "); fscanf(STDIN, "%lf", $m_a); printf(" サービス時間の平均値は? "); fscanf(STDIN, "%lf", $m_s); /* システムの定義 */ $base = new Q_base($s, $m_a, $m_s, $end); /* シミュレーションの実行 */ $base->Control(); /* 出力 */ $base->Output(); ?>