/******************************/
/* 簡単な待ち行列問題(M/M/s)*/
/* coded by Y.Suganuma */
/******************************/
import java.io.*;
import java.util.*;
public class Test
{
/****************/
/* main program */
/****************/
public static void main(String args[]) throws IOException
{
int s;
double end, m_a, m_s;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
/*
入力データ
*/
System.out.print("窓口の数は? ");
s = Integer.parseInt(in.readLine());
System.out.print("シミュレーション終了時間? ");
end = Double.parseDouble(in.readLine());
System.out.print(" 到着時間間隔の平均値は? ");
m_a = Double.parseDouble(in.readLine());
System.out.print(" サービス時間の平均値は? ");
m_s = Double.parseDouble(in.readLine());
/*
システムの定義
*/
Q_base base = new Q_base(s, m_a, m_s, end);
/*
シミュレーションの実行
*/
base.Control();
/*
出力
*/
base.Output();
}
}
/**********************/
/* クラスQ_baseの定義 */
/**********************/
class Q_base {
private int s; // 窓口の数
private int asb; // 全窓口の空き状況
// =0 : すべての窓口がふさがっている
// =n : n個の窓口が空いている
private int [] sb; // 各窓口の空き状況
// =0 : 窓口は空いている
// >0 : サービスを受けている客番号
private double asb_t; // すべての窓口がふさがった時間
private double c_asb; // すべての窓口がふさがっている時間の累計
private double [] c_sb; // 各窓口がふさがっている時間の累計
private double [] st_e; // 各窓口のサービス終了時間
private double [] st_s; // 各窓口がふさがった時間
private int m_lq; // 最大待ち行列長
private double c_lq_t; // 待ち行列長にその長さが継続した時間を乗じた値の累計
private double c_wt; // 待ち時間の累計
private double lq_t; // 現在の待ち行列長になった時間
private double m_wt; // 最大待ち時間
private int sc; // 現在の系内客数
private double c_sc_t; // 系内客数にその数が継続した時間を乗じた値の累計
private double c_sys; // 滞在時間の累計
private double m_sys; // 最大滞在時間
private double sc_t; // 現在の系内客数になった時間
private int m_sc; // 最大系内客数
private double m_a; // 到着時間間隔の平均値
private double m_s; // サービス時間の平均値
private double at; // 次の客の到着時間(負の時は,終了)
private double p_time; // 現在時間
private double end; // シミュレーション終了時間
private int nc; // 到着客数カウンタ
private Random rn; // 乱数
private TreeMap <Integer, Customer> cus; // 系内にいる客のリスト
private ArrayDeque <Integer> que; // 待ち行列
/*****************************************/
/* コンストラクタ */
/* s_i : 窓口の数 */
/* m_a_i : 到着時間間隔の平均値 */
/* m_s_i : サービス時間の平均値 */
/* end_i : シミュレーション終了時間 */
/*****************************************/
Q_base (int s_i, double m_a_i, double m_s_i, double end_i)
{
/*
設定
*/
s = s_i;
m_a = m_a_i;
m_s = m_s_i;
end = end_i;
/*
領域の確保
*/
sb = new int [s];
c_sb = new double [s];
st_e = new double [s];
st_s = new double [s];
for (int i1 = 0; i1 < s; i1++) {
sb[i1] = 0;
c_sb[i1] = 0.0;
}
cus = new TreeMap <Integer, Customer> ();
que = new ArrayDeque <Integer> ();
/*
初期設定
*/
p_time = 0.0;
nc = 0;
asb = s;
m_lq = 0;
m_sc = 0;
c_asb = 0.0;
c_wt = 0.0;
m_wt = 0.0;
c_lq_t = 0.0;
lq_t = 0.0;
m_sys = 0.0;
c_sys = 0.0;
c_sc_t = 0.0;
sc_t = 0.0;
/*
乱数の初期設定
*/
rn = new Random();
/*
最初の客の到着時間の設定
*/
at = p_time + Next_at();
}
/**********************************/
/* 次の客の到着までの時間の発生 */
/**********************************/
double Next_at()
{
return -m_a * Math.log(rn.nextDouble());
}
/************************/
/* サービス時間の発生 */
/************************/
double Next_sv()
{
return -m_s * Math.log(rn.nextDouble());
}
/**************/
/* 全体の制御 */
/**************/
void Control()
{
int sw = 0;
while (sw > -2) {
sw = Next(); // 次の処理の選択
if (sw == -1)
sw = End_o_s(); // シミュレーションの終了
else {
if (sw == 0)
Arrive(); // 客の到着処理
else
Service(sw); // サービスの終了
}
}
}
/**************************************************/
/* 次の処理の決定 */
/* return : =-1 : シミュレーションの終了 */
/* =0 : 客の到着処理 */
/* =i : i番目の窓口のサービス終了 */
/**************************************************/
int Next()
{
int sw = -1;
double t = end; // シミュレーション終了時刻で初期設定
// 次の客の到着時刻
if (at >= 0.0 && at < t) {
sw = 0;
t = at;
}
// サービス終了時刻
for (int i1 = 0; i1 < s; i1++) {
if (sb[i1] > 0 && st_e[i1] <= t) {
sw = i1 + 1;
t = st_e[i1]; // 窓口i1のサービス終了時刻
}
}
return sw;
}
/**********************************/
/* 終了処理 */
/* return : =-1 : 終了前処理 */
/* =-2 : 実際の終了 */
/**********************************/
int End_o_s()
{
int sw = -2;
p_time = end; // 現在時刻
at = -1.0; // 次の客の到着時刻
for (int i1 = 0; i1 < s; i1++) {
if (sb[i1] > 0) { // サービス中の客はいるか?
if (sw == -2) {
sw = -1;
end = st_e[i1]; // 窓口i1のサービス終了時刻
}
else {
if (st_e[i1] > end)
end = st_e[i1]; // 窓口i1のサービス終了時刻
}
}
}
return sw;
}
/****************************/
/* 客の到着処理 */
/* ct : 客リストの先頭 */
/****************************/
void Arrive()
{
/*
客数の増加と次の客の到着時刻の設定
*/
nc += 1; // 到着客数カウンタ
p_time = at; // 現在時刻
at = p_time + Next_at(); // 次の客の到着時刻
if (at >= end)
at = -1.0;
/*
系内客数の処理
*/
c_sc_t += cus.size() * (p_time - sc_t); // 系内客数にその数が継続した時間を乗じた値の累計
sc_t = p_time; // 現在の系内客数になった時刻
if (cus.size()+1 > m_sc)
m_sc = cus.size() + 1; // 最大系内客数
/*
窓口に空きがない場合
*/
if (asb == 0) {
Customer ct_p = new Customer(-1, p_time);
cus.put(new Integer(nc), ct_p); // 客の登録(系内客数)
c_lq_t += que.size() * (p_time - lq_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計
que.add(new Integer(nc)); // 客の登録(待ち行列)
lq_t = p_time; // 現在の待ち行列長になった時刻
if (que.size() > m_lq)
m_lq = que.size(); // 最大待ち行列長
}
/*
すぐサービスを受けられる場合
*/
else {
int k = -1;
for (int i1 = 0; i1 < s && k < 0; i1++) {
if (sb[i1] == 0) {
Customer ct_p = new Customer(i1, p_time);
cus.put(new Integer(nc), ct_p); // 客の登録(系内客数)
k = i1;
sb[k] = nc; // サービスを受けている客番号
st_e[k] = p_time + Next_sv(); // 窓口kのサービス終了時刻
asb -= 1; // 空いている窓口数
}
}
st_s[k] = p_time; // 窓口kがふさがった時刻
if (asb == 0)
asb_t = p_time; // すべての窓口がふさがった時刻
}
}
/*********************************/
/* サービス終了時の処理 */
/* k : サービス終了窓口番号 */
/* ct : 客リストの先頭 */
/*********************************/
void Service(int k)
{
/*
時間の設定
*/
k -= 1;
p_time = st_e[k]; // 現在時刻
st_e[k] = -1.0; // サービス終了時間
/*
系内客数の処理
*/
c_sc_t += cus.size() * (p_time - sc_t); // 系内客数にその数が継続した時間を乗じた値の累計
sc_t = p_time; // 現在の系内客数になった時刻
/*
滞在時間の処理
*/
Customer ct = cus.get(new Integer(sb[k]));
double x1 = p_time - ct.time;
c_sys += x1; // 滞在時間の累計
if (x1 > m_sys)
m_sys = x1; // 最大滞在時間
cus.remove(new Integer(sb[k])); // 客の削除(系内客数)
/*
窓口使用時間の処理
*/
asb += 1; // 空いている窓口数
sb[k] = 0; // 窓口kを空き状態にする
c_sb[k] += (p_time - st_s[k]); // 窓口kがふさがっている時間の累計
/*
待ち行列がある場合
*/
if (que.size() > 0) {
asb -= 1; // 開いている窓口数
c_lq_t += que.size() * (p_time - lq_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計
int n = que.pollFirst().intValue(); // 客の削除(待ち行列)
lq_t = p_time; // 現在の待ち行列長になった時刻
ct = cus.get(new Integer(n));
x1 = p_time - ct.time;
c_wt += x1; // 待ち時間の累計
if (x1 > m_wt)
m_wt = x1; // 最大待ち時間
k = -1;
for (int i1 = 0; i1 < s && k < 0; i1++) {
if (sb[i1] == 0) {
k = i1;
sb[k] = n; // 窓口kの客番号
st_e[k] = p_time + Next_sv(); // 窓口kのサービス終了時刻
st_s[k] = p_time; // 窓口kがふさがった時刻
ct.state = k; // 客の状態変更
}
}
}
/*
待ち行列がない場合
*/
else {
if (asb == 1)
c_asb += (p_time - asb_t); // すべての窓口がふさがっている時間の累計
}
}
/************************/
/* 結果の出力 */
/* (カッコ内は理論値) */
/************************/
void Output()
{
double rn = (double)nc;
double rs = (double)s;
double ram = 1.0 / m_a;
double myu = 1.0 / m_s;
double rou = ram / (rs * myu);
double p0, pi;
if (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));
}
double Lq = pi * rou / (1.0 - rou);
double L = Lq + rs * rou;
double Wq = Lq / ram;
double W = Wq + 1.0 / myu;
System.out.printf("シミュレーション終了時間=%.3f 客数=%d ρ=%.3f p0=%.3f\n",
p_time, nc, rou, p0);
System.out.printf(" すべての窓口が塞がっている割合=%.3f (%.3f)\n",
c_asb/p_time, pi);
System.out.printf(" 各窓口が塞がっている割合\n");
for (int i1 = 0; i1 < s; i1++)
System.out.printf(" %d %.3f\n", i1+1, c_sb[i1]/p_time);
System.out.printf(" 平均待ち行列長=%.3f (%.3f) 最大待ち行列長=%d\n",
c_lq_t/p_time, Lq, m_lq);
System.out.printf(" 平均系内客数 =%.3f (%.3f) 最大系内客数 =%d\n",
c_sc_t/p_time, L, m_sc);
System.out.printf(" 平均待ち時間 =%.3f (%.3f) 最大待ち時間 =%.3f\n",
c_wt/rn, Wq, m_wt);
System.out.printf(" 平均滞在時間 =%.3f (%.3f) 最大滞在時間 =%.3f\n",
c_sys/rn, W, m_sys);
}
}
/************************/
/* クラスCustomerの定義 */
/************************/
class Customer
{
double time; // 到着時刻
int state; // 客の状態
// =-1 : 待ち行列に入っている
// >=0 : サービスを受けている窓口番号
/*******************/
/* コンストラクタ */
/* n : 客番号 */
/* s : 状態 */
/*******************/
Customer (int s, double t)
{
time = t;
state = s;
}
}