<?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();
?>