情報学部 | 菅沼ホーム | 目次 | 索引 |
/******************************/ /* 複雑な待ち行列問題 */ /* coded by Y.Suganuma */ /******************************/ #include <stdio.h> #include <iostream> #include <stdlib.h> #include <math.h> #include <ctime> #include <map> #include <queue> #include <vector> #include <string> #include "MT.h" using namespace std; /**********************/ /* 指数分布乱数の発生 */ /* m : 平均値 */ /* rerutn : 乱数 */ /**********************/ double Exp_b(double m) { return -m * log(genrand_real3()); } /*********************/ /* クラスInletの定義 */ /*********************/ class Inlet { string name; // 入り口名 string out; // 待ち行列名 int out_n; // 待ち行列番号 double arrive_time; // 客の到着時刻(負:客がない) int a_t; // = -n : 到着する客の人数を負の値にしたもの // =0 : 指数分布 // =1 : 一定時間間隔 double mean; // 到着時間間隔またはその平均 queue<double> que; // 客の到着時刻リスト public: /*************************************************************/ /* コンストラクタ */ /* name1 : 入り口名 */ /* a_t1; // = -n : 到着する客の人数を負の値にしたもの */ /* // =0 : 指数分布 */ /* // =1 : 一定時間間隔 */ /* m_a: 到着時間間隔またはその平均 */ /* que1 : 客の到着時刻リスト */ /* name2 : 待ち行列名 */ /*************************************************************/ Inlet (string name1, int a_t1, double m_a, queue<double> que1, string name2) { name = name1; out = name2; a_t = a_t1; mean = m_a; que = que1; if (a_t == 0) arrive_time = Exp_b(mean); else if (a_t == 1) arrive_time = 0; else { arrive_time = que.front(); que.pop(); } } friend class Q_base; }; /*********************/ /* クラスQueueの定義 */ /*********************/ class Queue { string name; // 待ち行列名 int nc; // 待ち行列への到着客数カウンタ int max_q_l; // 最大待ち行列長 double c_ql; // 待ち行列長にその長さが継続した時間を乗じた値の累計 double ql_t; // 現在の待ち行列長になった時間 double max_wt; // 最大待ち時間 double c_wt; // 待ち時間の累計 int n; // =0 : 入り口から入る // >0 : 複数の窓口から入る(窓口数) vector<string> in; // 入り口名,または,窓口名 vector<int> in_n; // 入り口番号,または,窓口番号 int m; // 処理する窓口数 vector<string> out; // 窓口名 vector<int> out_n; // 窓口番号 queue<int> que; // 待ち行列 public: /*********************************************/ /* コンストラクタ */ /* name1 : 待ち行列名 */ /* n1 : =0 : 入り口から入る */ /* >0 : 複数の窓口から入る(窓口数) */ /* in1 : 入り口名,または,窓口名 */ /* m1 : 処理する窓口数 */ /* out1 : 窓口名 */ /*********************************************/ Queue(string name1, int n1, vector<string> in1, int m1, vector<string> out1) { name = name1; n = n1; in = in1; m = m1; out = out1; nc = 0; max_q_l = 0; c_ql = 0.0; ql_t = 0.0; max_wt = 0.0; c_wt = 0.0; } friend class Q_base; }; /**********************/ /* クラスEntityの定義 */ /**********************/ class Entity { string name; // 窓口名 double end_time; // サービス終了時刻(負:何も処理していない) int s_t; // =0 : 指数分布 // =1 : 一定時間 double mean; // 平均サービス時間 double busy_t; // 窓口がふさがった時刻 double c_busy; // 窓口がふさがっている時間の累計 int service; // サービス中の客番号(0のときは無し) string in; // 待ち行列(入力)の名前 int in_n; // 待ち行列(入力)番号 int to; // =0 : システムの外に出る // =1 : 待ち行列に入る string out; // 待ち行列(出力)の名前 int out_n; // 待ち行列(出力)番号 public: /****************************************/ /* コンストラクタ */ /* name1 : 窓口名 */ /* s_t1; // =0 : 指数分布 */ /* // =1 : 一定時間 */ /* m_s:サービス時間またはその平均 */ /* name2 : 待ち行列(入力)の名前 */ /* sw : =0 : システムの外に出る */ /* =1 : 待ち行列に入る */ /* name3 : 待ち行列(出力)の名前 */ /**********************************+++***/ Entity(string name1, int s_t1, double m_s, string name2, int sw, string name3) { name = name1; to = sw; in = name2; if (to > 0) out = name3; end_time = -1.0; s_t = s_t1; mean = m_s; service = 0; busy_t = -1.0; c_busy = 0.0; } friend class Q_base; }; /************************/ /* クラスCustomerの定義 */ /************************/ class Customer { public : double time; // 到着時刻 int state1; // 客の状態1 // =0 : 待ち行列に入っている // =1 : サービスを受けている int state2; // 客の状態2(待ち行列番号,または,窓口番号) /*********************/ /* コンストラクタ */ /* s1,s2 : 状態 */ /* t : 到着時刻 */ /*******************************/ Customer::Customer(int s1, int s2, double t) { time = t; state1 = s1; state2 = s2; } }; /**********************/ /* クラスQ_baseの定義 */ /**********************/ class Q_base { double p_time; // 現在時刻 int max_c; // 最大系内客数 int nc; // システムへの到着客数カウンタ double now_c_t; // 現在の系内客数になった時間 double c_now_c; // 系内客数にその数が継続した時間を乗じた値の累計 double c_sys; // 滞在時間の累計 double max_sys; // 最大滞在時間 double end; // シミュレーション終了時間 map<int, Customer> cus; // 系内にいる客のリスト vector<Inlet> inl; // Inletオブジェクトリスト vector<Queue> que; // Queueオブジェクトリスト vector<Entity> ent; // Entityオブジェクトリスト public: /***************************************/ /* コンストラクタ */ /* v_i : Inletオブジェクトリスト */ /* v_q : Queueオブジェクトリスト */ /* v_e : Entityオブジェクトリスト */ /* e : シミュレーション終了時刻 */ /***************************************/ Q_base(vector<Inlet> v_i, vector<Queue> v_q, vector<Entity> v_e, double e) { // 接続関係のチェック cout << "\n"; // Inlet inl = v_i; que = v_q; ent = v_e; for (int i1 = 0; i1 < (int)inl.size()-1; i1++) { for (int i2 = i1+1; i2 < (int)inl.size(); i2++) { if (inl[i1].name == inl[i2].name) { cout << "***error 同じ名前の入り口があります " + inl[i1].name + "\n"; exit(1); } } } int k; for (int i1 = 0; i1 < (int)inl.size(); i1++) { k = -1; for (int i2 = 0; i2 < (int)que.size(); i2++) { if (inl[i1].out == que[i2].name) { k = i2; break; } } if (k >= 0) inl[i1].out_n = k; else { cout << "***error 入り口から入る待ち行列名が不適当です " + inl[i1].out + "\n"; exit(1); } } // Queue for (int i1 = 0; i1 < (int)que.size()-1; i1++) { for (int i2 = i1+1; i2 < (int)que.size(); i2++) { if (que[i1].name == que[i2].name) { cout << "***error 同じ名前の待ち行列があります " + que[i1].name + "\n"; exit(1); } } } for (int i1 = 0; i1 < (int)que.size(); i1++) { if (que[i1].n == 0) { k = -1; for (int i2 = 0; i2 < (int)inl.size(); i2++) { if (que[i1].in[0] == inl[i2].name) { k = i2; break; } } if (k >= 0) que[i1].in_n.push_back(k); else { cout << "***error 待ち行列に入る入り口名が不適当です " + que[i1].in[0] + "\n"; exit(1); } } else { for (int i2 = 0; i2 < (int)que[i1].n; i2++) { k = -1; for (int i3 = 0; i3 < (int)ent.size(); i3++) { if (que[i1].in[i2] == ent[i3].name) { k = i3; break; } } if (k >= 0) que[i1].in_n.push_back(k); else { cout << "***error 待ち行列に入る窓口名が不適当です " + que[i1].in[i2] + "\n"; exit(1); } } } for (int i2 = 0; i2 < (int)que[i1].m; i2++) { k = -1; for (int i3 = 0; i3 < (int)ent.size(); i3++) { if (que[i1].out[i2] == ent[i3].name) { k = i3; break; } } if (k >= 0) que[i1].out_n.push_back(k); else { cout << "***error 待ち行列を処理する窓口名が不適当です " + que[i1].out[i2] + "\n"; exit(1); } } } // Entity for (int i1 = 0; i1 < (int)ent.size()-1; i1++) { k = -1; for (int i2 = i1+1; i2 < (int)ent.size(); i2++) { if (ent[i1].name == ent[i2].name) { cout << "***error 同じ名前の窓口があります " + ent[i1].name + "\n"; exit(1); } } } for (int i1 = 0; i1 < (int)ent.size(); i1++) { k = -1; for (int i2 = 0; i2 < (int)que.size(); i2++) { if (ent[i1].in == que[i2].name) { k = i2; break; } } if (k >= 0) ent[i1].in_n = k; else { cout << "***error 窓口に入る待ち行列名が不適当です " + ent[i1].in + "\n"; exit(1); } if (ent[i1].to > 0) { k = -1; for (int i2 = 0; i2 < (int)que.size(); i2++) { if (ent[i1].out == que[i2].name) { k = i2; break; } } if (k >= 0) ent[i1].out_n = k; else { cout << "***error 窓口の出口にある待ち行列名が不適当です " + ent[i1].out + "\n"; exit(1); } } } // 初期設定 p_time = 0.0; max_c = 0; nc = 0; now_c_t = 0.0; c_now_c = 0.0; c_sys = 0.0; max_sys = 0.0; end = e; // 乱数の初期設定 init_genrand((unsigned)time(NULL)); } void Control(); // 全体の制御 void Next(int *); // 次の処理の決定 int End_o_s(); // 終了処理 void Arrive(int); // 客の到着 void Service(int); // サービス終了 void Output(); // 結果の出力 }; /**************/ /* 全体の制御 */ /**************/ void Q_base::Control() { int sw[2]; sw[0] = 0; while (sw[0] > -2) { Next(sw); // 次の処理の選択 if (sw[0] == -1) sw[0] = End_o_s(); // シミュレーションの終了 else { if (sw[0] == 0) Arrive(sw[1]); // 客の到着処理 else Service(sw[1]); // サービスの終了 } } } /*********************************************/ /* 次の処理の決定 */ /* sw[0] : =-1 : シミュレーションの終了 */ /* =0 : 客の到着処理 */ /* =1 : 窓口のサービス終了 */ /* [1] : 入り口番号 or 窓口番号 */ /*********************************************/ void Q_base::Next(int *sw) { double tm = end; // 次の処理時刻 sw[0] = -1; // 次の客の到着時刻 for (int i1 = 0; i1 < (int)inl.size(); i1++) { Inlet x = inl[i1]; if (x.arrive_time >= 0.0 && x.arrive_time < tm) { sw[0] = 0; sw[1] = i1; tm = x.arrive_time; } } // サービス終了時刻 for (int i1 = 0; i1 < (int)ent.size(); i1++) { Entity x = ent[i1]; if (x.service > 0 && x.end_time <= tm) { sw[0] = 1; sw[1] = i1; tm = x.end_time; } } if (sw[0] < 0) end = p_time; } /**********************************/ /* 終了処理 */ /* return : =-1 : 終了前処理 */ /* =-2 : 実際の終了 */ /**********************************/ int Q_base::End_o_s() { int sw = -2; p_time = end; // 現在時刻 for (int i1 = 0; i1 < (int)ent.size(); i1++) { Entity x = ent[i1]; if (x.service > 0) { // サービス中の客はいるか? if (sw == -2) { sw = -1; end = x.end_time; // 窓口i1のサービス終了時刻 } else { if (x.end_time > end) end = x.end_time; // 窓口i1のサービス終了時刻 } } } return sw; } /************************/ /* 客の到着処理 */ /* kk : 入り口番号 */ /************************/ void Q_base::Arrive(int kk) { /* 客数の増加と次の客の到着時刻の設定 */ nc += 1; // 到着客数カウンタ p_time = inl[kk].arrive_time; // 現在時刻 if (inl[kk].a_t == 0) // 次の客の到着時刻 inl[kk].arrive_time = p_time + Exp_b(inl[kk].mean); else if (inl[kk].a_t == 1) inl[kk].arrive_time = p_time + inl[kk].mean; else { if (inl[kk].que.empty()) inl[kk].arrive_time = -1.0; else { inl[kk].arrive_time = inl[kk].que.front(); inl[kk].que.pop(); } } if (inl[kk].arrive_time >= end) inl[kk].arrive_time = -1.0; /* 系内客数の処理 */ c_now_c += cus.size() * (p_time - now_c_t); // 系内客数にその数が継続した時間を乗じた値の累計 now_c_t = p_time; // 現在の系内客数になった時刻 if ((int)cus.size()+1 > max_c) max_c = cus.size() + 1; // 最大系内客数 /* 空いている窓口を探す */ int k1 = inl[kk].out_n; que[k1].nc++; int k = -1; for (int i1 = 0; i1 < que[k1].m; i1++) { int k2 = que[k1].out_n[i1]; // 処理窓口 if (ent[k2].service == 0) { k = k2; break; } } /* 窓口に空きがない場合 */ if (k < 0) { Customer ct_p(0, k1, p_time); cus.insert(pair<int, Customer>(nc, ct_p)); // 客の登録(系内客数) que[k1].c_ql += que[k1].que.size() * (p_time - que[k1].ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 que[k1].que.push(nc); // 客の登録(待ち行列) que[k1].ql_t = p_time; // 現在の待ち行列長になった時刻 if ((int)que[k1].que.size() > que[k1].max_q_l) que[k1].max_q_l = que[k1].que.size(); // 最大待ち行列長 } /* すぐサービスをうけられる場合 */ else { Customer ct_p(1, k, p_time); cus.insert(pair<int, Customer>(nc, ct_p)); // 客の登録(系内客数) ent[k].service = nc; // サービスを受けている客番号 ent[k].busy_t = p_time; // 窓口がふさがった時刻 if (ent[k].s_t == 0) // 窓口のサービス終了時刻 ent[k].end_time = p_time + Exp_b(ent[k].mean); else ent[k].end_time = p_time + ent[k].mean; } } /**********************************/ /* サービス終了時の処理 */ /* kk : サービス終了窓口番号 */ /**********************************/ void Q_base::Service(int kk) { map<int, Customer>::iterator it; /* 時間の設定 */ p_time = ent[kk].end_time; // 現在時刻 ent[kk].end_time = -1.0; // サービス終了時間 /* システムの外へ出る場合 */ if (ent[kk].to == 0) { /* 系内客数の処理 */ c_now_c += cus.size() * (p_time - now_c_t); // 系内客数にその数が継続した時間を乗じた値の累計 now_c_t = p_time; // 現在の系内客数になった時刻 /* 滞在時間の処理 */ it = cus.find(ent[kk].service); // サービス中の客 double x1 = p_time - (it->second).time; c_sys += x1; // 滞在時間の累計 if (x1 > max_sys) max_sys = x1; // 最大滞在時間 cus.erase(ent[kk].service); // 客の削除(系内客数) } /* 他の窓口処理へ入る場合の処理 */ else { int k1 = ent[kk].out_n; que[k1].nc++; int sw = 1; int k2 = 0; if (que[k1].que.size() == 0) { for (int i1 = 0; i1 < que[k1].m; i1++) { k2 = que[k1].out_n[i1]; // 窓口 if (ent[k2].service == 0) { sw = 0; break; } } } /* 待ち行列がある場合 */ if (sw > 0) { que[k1].c_ql += que[k1].que.size() * (p_time - que[k1].ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 que[k1].que.push(ent[kk].service); // 客の登録(待ち行列) que[k1].ql_t = p_time; // 現在の待ち行列長になった時刻 if ((int)que[k1].que.size() > que[k1].max_q_l) que[k1].max_q_l = que[k1].que.size(); // 最大待ち行列長 it = cus.find(ent[kk].service); (it->second).state1 = 0; // 客の状態変更(待ち行列) (it->second).state2 = ent[kk].out_n; // 客の状態変更(待ち行列番号) } /* すぐサービスをうけられる場合 */ else { ent[k2].service = ent[kk].service; // サービスを受けている客番号 ent[k2].busy_t = p_time; // 窓口がふさがった時刻 if (ent[k2].s_t == 0) // 窓口のサービス終了時刻 ent[k2].end_time = p_time + Exp_b(ent[k2].mean); else ent[k2].end_time = p_time + ent[k2].mean; } } /* 窓口使用時間の処理 */ ent[kk].service = 0; // 窓口を空き状態にする ent[kk].c_busy += (p_time - ent[kk].busy_t); // 窓口がふさがっている時間の累計 /* この窓口に対する待ち行列がある場合 */ int k3 = ent[kk].in_n; if (que[k3].que.size() > 0) { que[k3].c_ql += que[k3].que.size() * (p_time - que[k3].ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 int n = que[k3].que.front(); // 待ち行列の先頭にいる客 que[k3].que.pop(); // 客の削除(待ち行列) que[k3].ql_t = p_time; // 現在の待ち行列長になった時刻 it = cus.find(n); double x1 = p_time - (it->second).time; que[k3].c_wt += x1; // 待ち時間の累計 if (x1 > que[k3].max_wt) que[k3].max_wt = x1; // 最大待ち時間 for (int i1 = 0; i1 < que[k3].m; i1++) { int k4 = que[k3].out_n[i1]; // 窓口 if (ent[k4].service == 0) { ent[k4].service = n; // 窓口の客番号 ent[k4].busy_t = p_time; // 窓口がふさがった時刻 if (ent[k4].s_t == 0) // 窓口のサービス終了時刻 ent[k4].end_time = p_time + Exp_b(ent[k4].mean); else ent[k4].end_time = p_time + ent[k4].mean; (it->second).state1 = 1; // 客の状態変更(サービス中) (it->second).state2 = k4; // 客の状態変更(窓口番号) break; } } } } /**************************/ /* 統計量の計算と最終出力 */ /**************************/ void Q_base::Output() { // System printf("全客数 %d", nc); printf(" 最大系内客数 %d 最大滞在時間 %.3f\n", max_c, max_sys); printf("平均系内客数 %.3f", c_now_c / p_time); printf(" 平均滞在時間 %.3f", c_sys / nc); printf(" 終了時間 %.3f\n", p_time); // Entity for (int i1 = 0; i1 < (int)ent.size(); i1++) { Entity e = ent[i1]; cout << "Entity " << e.name; printf(" 稼働率 %.3f\n", e.c_busy / p_time); } // Queue for (int i1 = 0; i1 < (int)que.size(); i1++) { Queue q = que[i1]; cout << "Queue " << q.name; printf(" 客数 %d", q.nc); printf(" 最大待ち行列長 %d", q.max_q_l); printf(" 最大待ち時間 %.3f\n", q.max_wt); printf(" 平均待ち行列長 %.3f", q.c_ql / p_time); printf(" 平均待ち時間 %.3f\n", q.c_wt / q.nc); } } /****************/ /* main program */ /****************/ int main() { // 入り口 int n_i; printf("入り口(Inlet)の数は? "); scanf("%d", &n_i); vector<Inlet> inl; for (int i1 = 0; i1 < n_i; i1++) { double m_a; string name1, name2; printf("%d 番目の入り口(Inlet)\n", i1+1); printf(" 名前は? "); cin >> name1; int n; queue<double> que; printf(" 到着分布(= 0: 指数分布,=1: 一定時間間隔,< 0: 指定,客数の負値)? "); scanf("%d", &n); if (n == 0) { printf(" 到着時間間隔の平均値は? "); scanf("%lf", &m_a); } else if (n == 1) { printf(" 到着時間間隔は? "); scanf("%lf", &m_a); } else { double x; for (int i2 = 0; i2 < -n; i2++) { printf(" 到着時間は? "); scanf("%lf", &x); que.push(x); } } printf(" 並ぶ待ち行列の名前は? "); cin >> name2; Inlet inl_e(name1, n, m_a, que, name2); inl.push_back(inl_e); } // 待ち行列 int n_q; printf("待ち行列(Queue)の数は? "); scanf("%d", &n_q); vector<Queue> que; for (int i1 = 0; i1 < n_q; i1++) { string name1; printf("%d 番目の待ち行列(Queue)\n", i1+1); printf(" 名前は? "); cin >> name1; int n; printf(" 入り口(0),または,窓口(n>0,窓口の数)から? "); scanf("%d", &n); vector<string> in; if (n == 0) { string name2; printf(" 入り口の名前は? "); cin >> name2; in.push_back(name2); } else { for (int i2 = 0; i2 < n; i2++) { string name3; printf(" %d 番目の窓口の名前は? ", i2+1); cin >> name3; in.push_back(name3); } } int m; printf(" 処理する窓口の数は? "); scanf("%d", &m); vector<string> out; for (int i2 = 0; i2 < m; i2++) { string name4; printf(" %d 番目の窓口の名前は? ", i2+1); cin >> name4; out.push_back(name4); } Queue que_e(name1, n, in, m, out); que.push_back(que_e); } // 窓口 int n_e; printf("窓口(Entity)の数は? "); scanf("%d", &n_e); vector<Entity> ent; for (int i1 = 0; i1 < n_e; i1++) { double m_s; string name1; printf("%d 番目の窓口(Entity)\n", i1+1); printf(" 名前は? "); cin >> name1; int s_t; printf(" サービス分布(=0:指数分布,=1:一定時間)? "); scanf("%d", &s_t); if (s_t == 0) { printf(" サービス時間の平均値は? "); scanf("%lf", &m_s); } else { printf(" サービス時間は? "); scanf("%lf", &m_s); } printf(" 待ち行列(入力)の名前は? "); string name2; cin >> name2; int sw; printf(" 終了後,外部(0),または,待ち行列(1)? "); scanf("%d", &sw); string name3; if (sw > 0) { printf(" 待ち行列(出力)の名前は? "); cin >> name3; } Entity ent_e(name1, s_t, m_s, name2, sw, name3); ent.push_back(ent_e); } // 全体の制御を行うクラス double end; printf("シミュレーション終了時間? "); scanf("%lf", &end); Q_base base(inl, que, ent, end); // 全体の制御を行うクラス // 実行 base.Control(); // 出力 base.Output(); return 0; } /* ------------入力例(簡単な場合)----------- 1 Inlet 0 5 Queue 1 Queue 0 Inlet 2 Entity1 Entity2 2 Entity1 0 4 Queue 0 Entity2 0 4 Queue 0 10000 ------------入力例(複雑な場合)----------- 2 Inlet1 0 5 Queue1 Inlet2 0 5 Queue2 3 Queue1 0 Inlet1 1 Entity1 Queue2 0 Inlet2 1 Entity2 Queue3 2 Entity1 Entity2 2 Entity3 Entity4 4 Entity1 0 4 Queue1 1 Queue3 Entity2 0 4 Queue2 1 Queue3 Entity3 0 3 Queue3 0 Entity4 0 3 Queue3 0 10000 ------------MT.h----------- // Period parameters #define MT_N 624 #define MT_M 397 #define MATRIX_A 0x9908b0dfUL // constant vector a #define UPPER_MASK 0x80000000UL // most significant w-r bits #define LOWER_MASK 0x7fffffffUL // least significant r bits static unsigned long mt[MT_N]; // the array for the state vector static int mti=MT_N+1; // mti==MT_N+1 means mt[MT_N] is not initialized // initializes mt[MT_N] with a seed void init_genrand(unsigned long s) { mt[0]= s & 0xffffffffUL; for (mti=1; mti<MT_N; mti++) { mt[mti] = (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); // See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. // In the previous versions, MSBs of the seed affect // only MSBs of the array mt[]. // 2002/01/09 modified by Makoto Matsumoto mt[mti] &= 0xffffffffUL; // for >32 bit machines } } // initialize by an array with array-length // init_key is the array for initializing keys // key_length is its length // slight change for C++, 2004/2/26 void init_by_array(unsigned long init_key[], int key_length) { int i, j, k; init_genrand(19650218UL); i=1; j=0; k = (MT_N>key_length ? MT_N : key_length); for (; k; k--) { mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) + init_key[j] + j; // non linear mt[i] &= 0xffffffffUL; // for WORDSIZE > 32 machines i++; j++; if (i>=MT_N) { mt[0] = mt[MT_N-1]; i=1; } if (j>=key_length) j=0; } for (k=MT_N-1; k; k--) { mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) - i; // non linear mt[i] &= 0xffffffffUL; // for WORDSIZE > 32 machines i++; if (i>=MT_N) { mt[0] = mt[MT_N-1]; i=1; } } mt[0] = 0x80000000UL; // MSB is 1; assuring non-zero initial array } // generates a random number on [0,0xffffffff]-interval unsigned long genrand_int32(void) { unsigned long y; static unsigned long mag01[2]={0x0UL, MATRIX_A}; // mag01[x] = x * MATRIX_A for x=0,1 if (mti >= MT_N) { // generate N words at one time int kk; if (mti == MT_N+1) // if init_genrand() has not been called, init_genrand(5489UL); // a default initial seed is used for (kk=0;kk<MT_N-MT_M;kk++) { y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); mt[kk] = mt[kk+MT_M] ^ (y >> 1) ^ mag01[y & 0x1UL]; } for (;kk<MT_N-1;kk++) { y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); mt[kk] = mt[kk+(MT_M-MT_N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; } y = (mt[MT_N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); mt[MT_N-1] = mt[MT_M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; mti = 0; } y = mt[mti++]; // Tempering y ^= (y >> 11); y ^= (y << 7) & 0x9d2c5680UL; y ^= (y << 15) & 0xefc60000UL; y ^= (y >> 18); return y; } // generates a random number on [0,0x7fffffff]-interval long genrand_int31(void) { return (long)(genrand_int32()>>1); } // generates a random number on [0,1]-real-interval double genrand_real1(void) { return genrand_int32()*(1.0/4294967295.0); // divided by 2^32-1 } // generates a random number on [0,1)-real-interval double genrand_real2(void) { return genrand_int32()*(1.0/4294967296.0); // divided by 2^32 } // generates a random number on (0,1)-real-interval double genrand_real3(void) { return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0); // divided by 2^32 } // generates a random number on [0,1) with 53-bit resolution double genrand_res53(void) { unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6; return(a*67108864.0+b)*(1.0/9007199254740992.0); } // These real versions are due to Isaku Wada, 2002/01/09 added */
/******************************/ /* 複雑な待ち行列問題 */ /* coded by Y.Suganuma */ /******************************/ import java.io.*; import java.util.*; public class Test { /****************/ /* main program */ /****************/ public static void main(String args[]) throws IOException { BufferedReader inp = new BufferedReader(new InputStreamReader(System.in)); // 入り口 System.out.print("入り口(Inlet)の数は? "); int n_i = Integer.parseInt(inp.readLine().trim()); ArrayList <Inlet> inl = new ArrayList <Inlet>(); for (int i1 = 0; i1 < n_i; i1++) { double m_a = 0.0; System.out.print((i1+1) + " 番目の入り口(Inlet)\n"); System.out.print(" 名前は? "); String name1 = (inp.readLine()).trim(); ArrayDeque <Double> que = new ArrayDeque <Double>(); System.out.print(" 到着分布(=0:指数分布,1=一定時間間隔,<0:指定:客数の負値))? "); int n = Integer.parseInt(inp.readLine().trim()); if (n == 0) { System.out.print(" 到着時間間隔の平均値は? "); m_a = Double.parseDouble(inp.readLine().trim()); } else if (n == 1) { System.out.print(" 到着時間間隔は? "); m_a = Double.parseDouble(inp.readLine().trim()); } else { double x; for (int i2 = 0; i2 < -n; i2++) { System.out.print(" 到着時間は? "); x = Double.parseDouble(inp.readLine().trim()); que.add(x); } } System.out.print(" 並ぶ待ち行列の名前は? "); String name2 = (inp.readLine()).trim(); Inlet inl_e = new Inlet(name1, n, m_a, que, name2); inl.add(inl_e); } // 待ち行列 System.out.print("待ち行列(Queue)の数は? "); int n_q = Integer.parseInt(inp.readLine().trim()); ArrayList <Queue> que = new ArrayList <Queue>(); for (int i1 = 0; i1 < n_q; i1++) { System.out.print((i1+1) + " 番目の待ち行列(Queue)\n"); System.out.print(" 名前は? "); String name1 = (inp.readLine()).trim(); System.out.print(" 入り口(0),または,窓口(n>0,窓口の数)から? "); int n = Integer.parseInt(inp.readLine().trim()); ArrayList <String> in = new ArrayList <String>(); if (n == 0) { System.out.print(" 入り口の名前は? "); String name2 = (inp.readLine()).trim(); in.add(name2); } else { for (int i2 = 0; i2 < n; i2++) { System.out.print(" " + (i2+1) + " 番目の窓口の名前は? "); String name3 = (inp.readLine()).trim(); in.add(name3); } } int m; System.out.print(" 処理する窓口の数は? "); m = Integer.parseInt(inp.readLine().trim()); ArrayList <String> out = new ArrayList <String>(); for (int i2 = 0; i2 < m; i2++) { System.out.print(" " + (i2+1) + " 番目の窓口の名前は? "); String name4 = (inp.readLine()).trim(); out.add(name4); } Queue que_e = new Queue(name1, n, in, m, out); que.add(que_e); } // 窓口 System.out.print("窓口(Entity)の数は? "); int n_e = Integer.parseInt(inp.readLine().trim()); ArrayList <Entity> ent = new ArrayList <Entity>(); for (int i1 = 0; i1 < n_e; i1++) { double m_s = 0.0; System.out.print((i1+1) + " 番目の窓口(Entity)\n"); System.out.print(" 名前は? "); String name1 = (inp.readLine()).trim(); System.out.print(" サービス分布(=0:指数分布,1=一定時間)? "); int s_t = Integer.parseInt(inp.readLine().trim()); if (s_t == 0) { System.out.print(" サービス時間の平均値は? "); m_s = Double.parseDouble(inp.readLine().trim()); } else { System.out.print(" サービス時間は? "); m_s = Double.parseDouble(inp.readLine().trim()); } System.out.print(" 待ち行列(入力)の名前は? "); String name2 = (inp.readLine()).trim(); int sw; System.out.print(" 終了後,外部(0),または,待ち行列(1)? "); sw = Integer.parseInt(inp.readLine().trim()); String name3 = ""; if (sw > 0) { System.out.print(" 待ち行列(出力)の名前は? "); name3 = (inp.readLine()).trim(); } Entity ent_e = new Entity(name1, s_t, m_s, name2, sw, name3); ent.add(ent_e); } // 全体の制御を行うクラス double end; System.out.print("シミュレーション終了時間? "); end = Double.parseDouble(inp.readLine().trim()); Q_base base = new Q_base(inl, que, ent, end); // 全体の制御を行うクラス // 実行 base.Control(); // 出力 base.Output(); } } /*********************/ /* クラスInletの定義 */ /*********************/ class Inlet { String name; // 入り口名 String out; // 待ち行列名 int out_n; // 待ち行列番号 double arrive_time; // 客の到着時刻(負:客がない) int a_t; // = -n : 到着する客の人数を負の値にしたもの // =0 : 指数分布 // =1 : 一定時間間隔 double mean; // 到着時間間隔またはその平均 ArrayDeque <Double> que; // 客の到着時刻リスト /*************************************************************/ /* コンストラクタ */ /* name1 : 入り口名 */ /* a_t1; // = -n : 到着する客の人数を負の値にしたもの */ /* // =0 : 指数分布 */ /* // =1 : 一定時間間隔 */ /* m_a: 到着時間間隔またはその平均 */ /* que1 : 客の到着時刻リスト */ /* name2 : 待ち行列名 */ /*************************************************************/ Inlet (String name1, int a_t1, double m_a, ArrayDeque<Double> que1, String name2) { name = name1; out = name2; mean = m_a; a_t = a_t1; que = que1; if (a_t == 1) arrive_time = 0; else if (a_t < 0) arrive_time = que.pollFirst().doubleValue(); } } /*********************/ /* クラスQueueの定義 */ /*********************/ class Queue { String name; // 待ち行列名 int nc; // 待ち行列への到着客数カウンタ int max_q_l; // 最大待ち行列長 double c_ql; // 待ち行列長にその長さが継続した時間を乗じた値の累計 double ql_t; // 現在の待ち行列長になった時間 double max_wt; // 最大待ち時間 double c_wt; // 待ち時間の累計 int n; // =0 : 入り口から入る // >0 : 複数の窓口から入る(窓口数) ArrayList <String> in; // 入り口名,または,窓口名 ArrayList <Integer> in_n; // 入り口番号,または,窓口番号 int m; // 処理する窓口数 ArrayList <String> out; // 窓口名 ArrayList <Integer> out_n; // 窓口番号 ArrayDeque <Integer> que; // 待ち行列 /*********************************************/ /* コンストラクタ */ /* name1 : 待ち行列名 */ /* n1 : =0 : 入り口から入る */ /* >0 : 複数の窓口から入る(窓口数) */ /* in1 : 入り口名,または,窓口名 */ /* m1 : 処理する窓口数 */ /* out1 : 窓口名 */ /*********************************************/ Queue(String name1, int n1, ArrayList<String> in1, int m1, ArrayList<String> out1) { name = name1; in = in1; n = n1; in_n = new ArrayList <Integer>(); out = out1; m = m1; out_n = new ArrayList <Integer>(); que = new ArrayDeque <Integer>(); nc = 0; max_q_l = 0; c_ql = 0.0; ql_t = 0.0; max_wt = 0.0; c_wt = 0.0; } } /**********************/ /* クラスEntityの定義 */ /**********************/ class Entity { String name; // Entity名 double busy_t; // 窓口がふさがった時間 double c_busy; // 窓口がふさがっている時間の累計 double end_time; // サービス終了時間(負:何も処理していない) int s_t; // =0 : 指数分布 // =1 : 一定時間 double mean; // 平均サービス時間 int service; // サービス中の客番号(0のときはなし) String in; // 待ち行列(入力)の名前 int in_n; // 待ち行列(入力)番号 int to; // =0 : システムの外に出る // =1 : 待ち行列に入る String out; // 待ち行列(出力)の名前 int out_n; // 待ち行列(出力)番号 /****************************************/ /* コンストラクタ */ /* name1 : 窓口名 */ /* s_t1; // =0 : 指数分布 */ /* // =1 : 一定時間 */ /* m_s:サービス時間またはその平均 */ /* name2 : 待ち行列(入力)の名前 */ /* sw : =0 : システムの外に出る */ /* =1 : 待ち行列に入る */ /* name3 : 待ち行列(出力)の名前 */ /****************************************/ Entity(String name1, int s_t1, double m_s, String name2, int sw, String name3) { name = name1; to = sw; in = name2; if (to > 0) out = name3; end_time = -1.0; s_t = s_t1; mean = m_s; service = 0; busy_t = -1.0; c_busy = 0.0; } } /************************/ /* クラスCustomerの定義 */ /************************/ class Customer { double time; // 到着時刻 int state1; // 客の状態1 // =0 : 待ち行列に入っている // =1 : サービスを受けている int state2; // 客の状態2(待ち行列番号,または,窓口番号) /*********************/ /* コンストラクタ */ /* s1,s2 : 状態 */ /* t : 到着時刻 */ /*******************************/ Customer(int s1, int s2, double t) { time = t; state1 = s1; state2 = s2; } } /**********************/ /* クラスQ_baseの定義 */ /**********************/ class Q_base { private String name; // システム名 private double p_time; // 現在時刻 private int max_c; // 最大系内客数 private int nc; // 到着客数カウンタ private double now_c_t; // 現在の系内客数になった時間 private double c_now_c; // 系内客数にその数が継続した時間を乗じた値の累計 private double c_sys; // 滞在時間の累計 private double max_sys; // 最大滞在時間 private double end; // シミュレーション終了時間 private Random rn; // 乱数 private TreeMap <Integer, Customer> cus; // 系内にいる客のリスト private ArrayList <Inlet> inl; // Inletオブジェクトリスト private ArrayList <Queue> que; // Queueオブジェクトリスト private ArrayList <Entity> ent; // Entityオブジェクトリスト /***************************************/ /* コンストラクタ */ /* v_i : Inletオブジェクトリスト */ /* v_q : Queueオブジェクトリスト */ /* v_e : Entityオブジェクトリスト */ /* e : シミュレーション終了時刻 */ /***************************************/ Q_base(ArrayList<Inlet> v_i, ArrayList<Queue> v_q, ArrayList<Entity> v_e, double e) { // 接続関係のチェック System.out.println(); // Inlet inl = v_i; que = v_q; ent = v_e; for (int i1 = 0; i1 < inl.size()-1; i1++) { for (int i2 = i1+1; i2 < inl.size(); i2++) { if (inl.get(i1).name.equals(inl.get(i2).name)) { System.out.println("***error 同じ名前の入り口があります " + inl.get(i1).name); System.exit(1); } } } int k; for (int i1 = 0; i1 < inl.size(); i1++) { k = -1; for (int i2 = 0; i2 < que.size(); i2++) { if (inl.get(i1).out.equals(que.get(i2).name)) { k = i2; break; } } if (k >= 0) inl.get(i1).out_n = k; else { System.out.println("***error 入り口から入る待ち行列名が不適当です " + inl.get(i1).out); System.exit(1); } } // Queue for (int i1 = 0; i1 < que.size()-1; i1++) { for (int i2 = i1+1; i2 < que.size(); i2++) { if (que.get(i1).name.equals(que.get(i2).name)) { System.out.println("***error 同じ名前の待ち行列があります " + que.get(i1).name); System.exit(1); } } } for (int i1 = 0; i1 < que.size(); i1++) { if (que.get(i1).n == 0) { k = -1; for (int i2 = 0; i2 < inl.size(); i2++) { if (que.get(i1).in.get(0).equals(inl.get(i2).name)) { k = i2; break; } } if (k >= 0) que.get(i1).in_n.add(new Integer(k)); else { System.out.println("***error 待ち行列に入る入り口名が不適当です " + que.get(i1).in.get(0)); System.exit(1); } } else { for (int i2 = 0; i2 < que.get(i1).n; i2++) { k = -1; for (int i3 = 0; i3 < ent.size(); i3++) { if (que.get(i1).in.get(i2).equals(ent.get(i3).name)) { k = i3; break; } } if (k >= 0) que.get(i1).in_n.add(new Integer(k)); else { System.out.println("***error 待ち行列に入る窓口名が不適当です " + que.get(i1).in.get(i2)); System.exit(1); } } } for (int i2 = 0; i2 < que.get(i1).m; i2++) { k = -1; for (int i3 = 0; i3 < ent.size(); i3++) { if (que.get(i1).out.get(i2).equals(ent.get(i3).name)) { k = i3; break; } } if (k >= 0) que.get(i1).out_n.add(new Integer(k)); else { System.out.println("***error 待ち行列を処理する窓口名が不適当です " + que.get(i1).out.get(i2)); System.exit(1); } } } // Entity for (int i1 = 0; i1 < ent.size()-1; i1++) { k = -1; for (int i2 = i1+1; i2 < ent.size(); i2++) { if (ent.get(i1).name.equals(ent.get(i2).name)) { System.out.println("***error 同じ名前の窓口があります " + ent.get(i1).name); System.exit(1); } } } for (int i1 = 0; i1 < ent.size(); i1++) { k = -1; for (int i2 = 0; i2 < que.size(); i2++) { if (ent.get(i1).in.equals(que.get(i2).name)) { k = i2; break; } } if (k >= 0) ent.get(i1).in_n = k; else { System.out.println("***error 窓口に入る待ち行列名が不適当です " + ent.get(i1).in); System.exit(1); } if (ent.get(i1).to > 0) { k = -1; for (int i2 = 0; i2 < que.size(); i2++) { if (ent.get(i1).out.equals(que.get(i2).name)) { k = i2; break; } } if (k >= 0) ent.get(i1).out_n = k; else { System.out.println("***error 窓口の出口にある待ち行列名が不適当です " + ent.get(i1).out); System.exit(1); } } } // 初期設定 cus = new TreeMap <Integer, Customer>(); p_time = 0.0; max_c = 0; nc = 0; now_c_t = 0.0; c_now_c = 0.0; c_sys = 0.0; max_sys = 0.0; end = e; // 乱数の初期設定 rn = new Random(); } /**********************/ /* 指数分布乱数の発生 */ /* m : 平均値 */ /* rerutn : 乱数 */ /**********************/ double Exp_b(double m) { return -m * Math.log(rn.nextDouble()); } /**************/ /* 全体の制御 */ /**************/ void Control() { // 到着時間の初期設定 for (int i1 = 0; i1 < inl.size(); i1++) { if (inl.get(i1).a_t == 0) inl.get(i1).arrive_time = Exp_b(inl.get(i1).mean); } // 実行制御 int sw[] = new int [2]; sw[0] = 0; while (sw[0] > -2) { Next(sw); // 次の処理の選択 if (sw[0] == -1) sw[0] = End_o_s(); // シミュレーションの終了 else { if (sw[0] == 0) Arrive(sw[1]); // 客の到着処理 else Service(sw[1]); // サービスの終了 } } } /*********************************************/ /* 次の処理の決定 */ /* sw[0] : =-1 : シミュレーションの終了 */ /* =0 : 客の到着処理 */ /* =1 : 窓口のサービス終了 */ /* [1] : 入り口番号 or 窓口番号 */ /*********************************************/ void Next(int sw[]) { double tm = end; // 次の処理時刻 sw[0] = -1; // 次の客の到着時刻 for (int i1 = 0; i1 < inl.size(); i1++) { Inlet x = inl.get(i1); if (x.arrive_time >= 0.0 && x.arrive_time < tm) { sw[0] = 0; sw[1] = i1; tm = x.arrive_time; } } // サービス終了時刻 for (int i1 = 0; i1 < ent.size(); i1++) { Entity x = ent.get(i1); if (x.service > 0 && x.end_time <= tm) { sw[0] = 1; sw[1] = i1; tm = x.end_time; } } if (sw[0] < 0) end = p_time; } /**********************************/ /* 終了処理 */ /* return : =-1 : 終了前処理 */ /* =-2 : 実際の終了 */ /**********************************/ int End_o_s() { int sw = -2; p_time = end; // 現在時刻 for (int i1 = 0; i1 < ent.size(); i1++) { Entity x = ent.get(i1); if (x.service > 0) { // サービス中の客はいるか? if (sw == -2) { sw = -1; end = x.end_time; // 窓口i1のサービス終了時刻 } else { if (x.end_time > end) end = x.end_time; // 窓口i1のサービス終了時刻 } } } return sw; } /************************/ /* 客の到着処理 */ /* kk : 入り口番号 */ /************************/ void Arrive(int kk) { /* 客数の増加と次の客の到着時刻の設定 */ nc += 1; // 到着客数カウンタ p_time = inl.get(kk).arrive_time; // 現在時刻 if (inl.get(kk).a_t == 0) // 次の客の到着時刻 inl.get(kk).arrive_time = p_time + Exp_b(inl.get(kk).mean); else if (inl.get(kk).a_t == 1) inl.get(kk).arrive_time = p_time + inl.get(kk).mean; else { if (inl.get(kk).que.isEmpty()) inl.get(kk).arrive_time = -1.0; else inl.get(kk).arrive_time = inl.get(kk).que.pollFirst().doubleValue(); } if (inl.get(kk).arrive_time >= end) inl.get(kk).arrive_time = -1.0; /* 系内客数の処理 */ c_now_c += cus.size() * (p_time - now_c_t); // 系内客数にその数が継続した時間を乗じた値の累計 now_c_t = p_time; // 現在の系内客数になった時刻 if ((int)cus.size()+1 > max_c) max_c = cus.size() + 1; // 最大系内客数 /* 空いている窓口を探す */ int k1 = inl.get(kk).out_n; que.get(k1).nc++; int k = -1; for (int i1 = 0; i1 < que.get(k1).m; i1++) { int k2 = que.get(k1).out_n.get(i1); // 処理窓口 if (ent.get(k2).service == 0) { k = k2; break; } } /* 窓口に空きがない場合 */ if (k < 0) { Customer ct_p = new Customer(0, k1, p_time); cus.put(new Integer(nc), ct_p); // 客の登録(系内客数) que.get(k1).c_ql += que.get(k1).que.size() * (p_time - que.get(k1).ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 que.get(k1).que.add(new Integer(nc)); // 客の登録(待ち行列) que.get(k1).ql_t = p_time; // 現在の待ち行列長になった時刻 if (que.get(k1).que.size() > que.get(k1).max_q_l) que.get(k1).max_q_l = que.get(k1).que.size(); // 最大待ち行列長 } /* すぐサービスをうけられる場合 */ else { Customer ct_p = new Customer(1, k, p_time); cus.put(new Integer(nc), ct_p); // 客の登録(系内客数) ent.get(k).service = nc; // サービスを受けている客番号 ent.get(k).busy_t = p_time; // 窓口がふさがった時刻 if (ent.get(k).s_t == 0) // 窓口のサービス終了時刻 ent.get(k).end_time = p_time + Exp_b(ent.get(k).mean); else ent.get(k).end_time = p_time + ent.get(k).mean; } } /**********************************/ /* サービス終了時の処理 */ /* kk : サービス終了窓口番号 */ /**********************************/ void Service(int kk) { /* 時間の設定 */ p_time = ent.get(kk).end_time; // 現在時刻 ent.get(kk).end_time = -1.0; // サービス終了時間 /* システムの外へ出る場合 */ if (ent.get(kk).to == 0) { /* 系内客数の処理 */ c_now_c += cus.size() * (p_time - now_c_t); // 系内客数にその数が継続した時間を乗じた値の累計 now_c_t = p_time; // 現在の系内客数になった時刻 /* 滞在時間の処理 */ Customer it = cus.get(new Integer(ent.get(kk).service)); // サービス中の客 double x1 = p_time - it.time; c_sys += x1; // 滞在時間の累計 if (x1 > max_sys) max_sys = x1; // 最大滞在時間 cus.remove(new Integer(ent.get(kk).service)); // 客の削除(系内客数) } /* 他の窓口処理へ入る場合の処理 */ else { int k1 = ent.get(kk).out_n; que.get(k1).nc++; int sw = 1; int k2 = 0; if (que.get(k1).que.size() == 0) { for (int i1 = 0; i1 < que.get(k1).m; i1++) { k2 = que.get(k1).out_n.get(i1); // 窓口 if (ent.get(k2).service == 0) { sw = 0; break; } } } /* 待ち行列がある場合 */ if (sw > 0) { que.get(k1).c_ql += que.get(k1).que.size() * (p_time - que.get(k1).ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 que.get(k1).que.add(new Integer(ent.get(kk).service)); // 客の登録(待ち行列) que.get(k1).ql_t = p_time; // 現在の待ち行列長になった時刻 if (que.get(k1).que.size() > que.get(k1).max_q_l) que.get(k1).max_q_l = que.get(k1).que.size(); // 最大待ち行列長 Customer it = cus.get(new Integer(ent.get(kk).service)); it.state1 = 0; // 客の状態変更(待ち行列) it.state2 = ent.get(kk).out_n; // 客の状態変更(待ち行列番号) } /* すぐサービスをうけられる場合 */ else { ent.get(k2).service = ent.get(kk).service; // サービスを受けている客番号 ent.get(k2).busy_t = p_time; // 窓口がふさがった時刻 if (ent.get(k2).s_t == 0) // 窓口のサービス終了時刻 ent.get(k2).end_time = p_time + Exp_b(ent.get(k2).mean); else ent.get(k2).end_time = p_time + ent.get(k2).mean; } } /* 窓口使用時間の処理 */ ent.get(kk).service = 0; // 窓口を空き状態にする ent.get(kk).c_busy += (p_time - ent.get(kk).busy_t); // 窓口がふさがっている時間の累計 /* この窓口に対する待ち行列がある場合 */ int k3 = ent.get(kk).in_n; if (que.get(k3).que.size() > 0) { que.get(k3).c_ql += que.get(k3).que.size() * (p_time - que.get(k3).ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 int n = que.get(k3).que.pollFirst().intValue(); // 待ち行列の先頭にいる客の削除 que.get(k3).ql_t = p_time; // 現在の待ち行列長になった時刻 Customer it = cus.get(new Integer(n)); double x1 = p_time - it.time; que.get(k3).c_wt += x1; // 待ち時間の累計 if (x1 > que.get(k3).max_wt) que.get(k3).max_wt = x1; // 最大待ち時間 for (int i1 = 0; i1 < que.get(k3).m; i1++) { int k4 = que.get(k3).out_n.get(i1); // 窓口 if (ent.get(k4).service == 0) { ent.get(k4).service = n; // 窓口の客番号 ent.get(k4).busy_t = p_time; // 窓口がふさがった時刻 if (ent.get(k4).s_t == 0) // 窓口のサービス終了時刻 ent.get(k4).end_time = p_time + Exp_b(ent.get(k4).mean); else ent.get(k4).end_time = p_time + ent.get(k4).mean; it.state1 = 1; // 客の状態変更(サービス中) it.state2 = k4; // 客の状態変更(窓口番号) break; } } } } /**************************/ /* 統計量の計算と最終出力 */ /**************************/ void Output() { // System System.out.printf("全客数 %d", nc); System.out.printf(" 最大系内客数 %d 最大滞在時間 %.3f\n", max_c, max_sys); System.out.printf(" 平均系内客数 %.3f", c_now_c / p_time); System.out.printf(" 平均滞在時間 %.3f", c_sys / nc); System.out.printf(" 終了時間 %.3f\n", p_time); // Entity for (int i1 = 0; i1 < ent.size(); i1++) { Entity e = ent.get(i1); System.out.printf("Entity %s 稼働率 %.3f\n", e.name, e.c_busy / p_time); } // Queue for (int i1 = 0; i1 < que.size(); i1++) { Queue q = que.get(i1); System.out.printf("Queue %s 客数 %d", q.name, q.nc); System.out.printf(" 最大待ち行列長 %d", q.max_q_l); System.out.printf(" 最大待ち時間 %.3f\n", q.max_wt); System.out.printf(" 平均待ち行列長 %.3f", q.c_ql / p_time); System.out.printf(" 平均待ち時間 %.3f\n", q.c_wt / q.nc); } } } /* ------------入力例(簡単な場合)----------- 1 Inlet 0 5 Queue 1 Queue 0 Inlet 2 Entity1 Entity2 2 Entity1 0 4 Queue 0 Entity2 0 4 Queue 0 10000 ------------入力例(複雑な場合)----------- 2 Inlet1 0 5 Queue1 Inlet2 0 5 Queue2 3 Queue1 0 Inlet1 1 Entity1 Queue2 0 Inlet2 1 Entity2 Queue3 2 Entity1 Entity2 2 Entity3 Entity4 4 Entity1 0 4 Queue1 1 Queue3 Entity2 0 4 Queue2 1 Queue3 Entity3 0 3 Queue3 0 Entity4 0 3 Queue3 0 10000 */
test.html
<!DOCTYPE HTML> <HTML> <HEAD> <TITLE>複雑な待ち行列</TITLE> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8"> <SCRIPT TYPE="text/javascript" SRC="complex.js"></SCRIPT> <SCRIPT TYPE="text/javascript"> max_i = 10; // 最大入り口数 max_q = 10; // 最大待ち行列数 max_e = 10; // 最大窓口数 function change(type, k1, k2) { // 入り口(Inlet)の数 if (type == 1) { let n = parseInt(document.getElementById("n_i").value); if (n <= 0 || n > max_i) alert("***Error 入り口の数が不適当です"); else { for (let i1 = 1; i1 <= n; i1++) document.getElementById("n1"+i1).style.display = ""; for (let i1 = n+1; i1 <= max_i; i1++) document.getElementById("n1"+i1).style.display = "none"; } } // 待ち行列(Queue)の数 else if (type == 2) { let n = parseInt(document.getElementById("n_q").value); if (n <= 0 || n > max_q) alert("***Error 待ち行列の数が不適当です"); else { for (let i1 = 1; i1 <= n; i1++) document.getElementById("n2"+i1).style.display = ""; for (let i1 = n+1; i1 <= max_q; i1++) document.getElementById("n2"+i1).style.display = "none"; } } // 窓口(Entity)の数 else if (type == 3) { let n = parseInt(document.getElementById("n_e").value); if (n <= 0 || n > max_e) alert("***Error 窓口の数が不適当です"); else { for (let i1 = 1; i1 <= n; i1++) document.getElementById("n4"+i1).style.display = ""; for (let i1 = n+1; i1 <= max_e; i1++) document.getElementById("n4"+i1).style.display = "none"; } } // 待ち行列 else if (type == 4) { // 入り口の変更 if (k2 == 0) { let n = parseInt(document.getElementById("qi"+k1).value); if (n == 0) { document.getElementById("n2"+k1+"0").style.display = ""; for (let i1 = 1; i1 <= max_e; i1++) document.getElementById("n2"+k1+i1).style.display = "none"; } else { document.getElementById("n2"+k1+"0").style.display = "none"; for (let i1 = 1; i1 <= n; i1++) document.getElementById("n2"+k1+i1).style.display = ""; for (let i1 = n+1; i1 <= max_e; i1++) document.getElementById("n2"+k1+i1).style.display = "none"; } } // 出口の変更 else { let n = parseInt(document.getElementById("qo"+k1).value); if (n <= 0) alert("error 0 以下にはできません"); else { for (let i1 = 1; i1 <= n; i1++) document.getElementById("n3"+k1+i1).style.display = ""; for (let i1 = n+1; i1 <= max_e; i1++) document.getElementById("n3"+k1+i1).style.display = "none"; } } } // 窓口における出口処理の変更 else if (type == 5) { let n = parseInt(document.getElementById("eo"+k1).value); if (n == 0) document.getElementById("n5"+k1).style.display = "none"; else document.getElementById("n5"+k1).style.display = ""; } // 入り口における到着分布の変更 else if (type == 6) { let n = parseInt(document.getElementById("a_t"+k1).value); if (n == 0) { document.getElementById("n11"+k1).style.display = ""; document.getElementById("n12"+k1).style.display = "none"; document.getElementById("man"+k1).innerHTML = "平均到着間隔"; } else if (n == 1) { document.getElementById("n11"+k1).style.display = ""; document.getElementById("n12"+k1).style.display = "none"; document.getElementById("man"+k1).innerHTML = "到着間隔"; } else if (n < 0) { document.getElementById("n11"+k1).style.display = "none"; document.getElementById("n12"+k1).style.display = ""; } } // 窓口におけるサービス分布の変更 else if (type == 7) { let n = parseInt(document.getElementById("s_t"+k1).value); if (n == 0) document.getElementById("msn"+k1).innerHTML = "平均サービス時間"; else if (n == 1) document.getElementById("msn"+k1).innerHTML = "サービス時間"; } } </SCRIPT> </HEAD> <BODY STYLE="font-size: 130%; background-color: #eeffee;"> <H2 STYLE="text-align:center"><B>複雑な待ち行列</B></H2> <OL CLASS="no"> <DL> <P STYLE="text-align:center"> シミュレーション時間:<INPUT ID="end" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="10000"> 入り口(Inlet)の数(最大:10):<INPUT ID="n_i" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="2" onBlur="change(1, 0, 0)"><BR><BR> 待ち行列(Queue)の数(最大:10):<INPUT ID="n_q" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="3" onBlur="change(2, 0, 0)"> 窓口(Entity)の数(最大:10):<INPUT ID="n_e" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="4" onBlur="change(3, 0, 0)"> </P> <P STYLE="text-align:center">---入り口に対するデータ---</P> <SCRIPT TYPE="text/javascript"> let n_i = document.getElementById("n_i").value; for (let i1 = 1; i1 <= max_i; i1++) { document.write(' <DIV ID="n1' + i1 + '" STYLE="display: none">\n'); document.write(' <DT>入り口' + i1 + 'の名前:<INPUT ID="i' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="Inlet' + i1 + '"></DT>\n'); document.write(' <DD>到着分布(=0:指数分布,=1:一定時間間隔,<0:指定,客数の負値):<INPUT ID="a_t' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="0" onBlur="change(6, ' + i1 + ', 0)"></DD>\n'); document.write(' <DD ID="n11' + i1 + '"> <SPAN ID="man' + i1 + '">平均到着間隔</SPAN>:<INPUT ID="ma' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="5"></DD>\n'); document.write(' <DD ID="n12' + i1 + '" STYLE="display: none"> 到着時刻(半角スペースで区切って入力):<INPUT ID="at' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="30" VALUE=""></DD>\n'); document.write(' <DD>並ぶ待ち行列名:<INPUT ID="iq' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="Queue' + i1 + '"></DD>\n'); document.write(' </DIV>\n'); } for (let i1 = 1; i1 <= n_i; i1++) document.getElementById("n1"+i1).style.display = ""; </SCRIPT> <P STYLE="text-align:center">---待ち行列に対するデータ---</P> <SCRIPT TYPE="text/javascript"> let n_q = document.getElementById("n_q").value; for (let i1 = 1; i1 <= max_q; i1++) { document.write(' <DIV ID="n2' + i1 + '" STYLE="display: none">\n'); document.write(' <DT>待ち行列' + i1 + 'の名前:<INPUT ID="q' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="Queue' + i1 + '"></DT>\n'); document.write(' <DD>待ち行列へは,入り口(0)から or 窓口(n>0,窓口の数)から?:<INPUT ID="qi' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="0" onBlur="change(4, ' + i1 + ', 0)"></DD>\n'); document.write(' <DD ID="n2' + i1 + '0"> 待ち行列に入る入り口名:<INPUT ID="qii' + i1 + '1" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="Inlet' + i1 + '"></DD>\n'); for (let i2 = 1; i2 <= max_e; i2++) document.write(' <DD ID="n2' + i1 + i2 + '" STYLE="display: none"> 待ち行列に入る窓口名:<INPUT ID="qie' + i1 + i2 + '" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="Entity' + i2 + '"></DD>\n'); document.write(' <DD>待ち行列から出たとき,処理を行う窓口の数:<INPUT ID="qo' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="1" onBlur="change(4, ' + i1 + ', 1)"></DD>\n'); for (let i2 = 1; i2 <= max_e; i2++) document.write(' <DD ID="n3' + i1 + i2 + '" STYLE="display: none"> 待ち行列を処理する窓口名:<INPUT ID="qoe' + i1 + i2 + '" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="Entity' + i1 + '"></DD>\n'); document.getElementById("n3"+i1+"1").style.display = ""; document.write(' </DIV>\n'); } for (let i1 = 1; i1 <= n_q; i1++) document.getElementById("n2"+i1).style.display = ""; // 例題のため変更 document.getElementById("n230").style.display = "none"; document.getElementById("qi3").value = "2"; for (let i1 = 1; i1 <= 2; i1++) { document.getElementById("n23"+i1).style.display = ""; document.getElementById("qie3"+i1).value = "Entity" + i1; } document.getElementById("qo3").value = "2"; for (let i1 = 1; i1 <= 2; i1++) { document.getElementById("n33"+i1).style.display = ""; document.getElementById("qoe3"+i1).value = "Entity" + (2+i1); } </SCRIPT> <P STYLE="text-align:center">---窓口に対するデータ---</P> <SCRIPT TYPE="text/javascript"> let n_e = document.getElementById("n_e").value; for (let i1 = 1; i1 <= max_e; i1++) { document.write(' <DIV ID="n4' + i1 + '" STYLE="display: none">\n'); document.write(' <DT>窓口' + i1 + 'の名前:<INPUT ID="e' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="Entity' + i1 + '"></DT>\n'); document.write(' <DD>サービス分布(=0:指数分布,=1:一定時間):<INPUT ID="s_t' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="0" onBlur="change(7, ' + i1 + ', 0)"> '); document.write(' <SPAN ID="msn' + i1 + '">平均サービス時間</SPAN>:<INPUT ID="ms' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="4"></DD>'); document.write(' <DD>処理対象の待ち行列名:<INPUT ID="eq' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="Queue' + i1 + '"></DD>\n'); document.write(' <DD>処理終了後,外部(0),or,他の処理(1)?:<INPUT ID="eo' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="2" VALUE="0" onBlur="change(5, ' + i1 + ', 1)"></DD>\n'); document.write(' <DD ID="n5' + i1 + '" STYLE="display: none"> 他の処理のための待ち行列名:<INPUT ID="eoq' + i1 + '" STYLE="font-size: 100%" TYPE="text" SIZE="3" VALUE="Queue' + i1 + '"></DD>\n'); document.write(' </DIV>\n'); } for (let i1 = 1; i1 <= n_e; i1++) document.getElementById("n4"+i1).style.display = ""; // 例題のため変更 for (let i1 = 1; i1 <= 2; i1++) { document.getElementById("eo"+i1).value = "1"; document.getElementById("n5"+i1).style.display = ""; document.getElementById("eoq"+i1).value = "Queue3"; } for (let i1 = 3; i1 <= 4; i1++) { document.getElementById("ms"+i1).value = "3"; document.getElementById("eq"+i1).value = "Queue3"; } </SCRIPT> <P STYLE="text-align:center"> <BUTTON STYLE="font-size: 100%; background-color: pink" onClick="main()">実行</BUTTON> 結果は下<BR> <TEXTAREA ID="res" COLS="70" ROWS="15" STYLE="font-size: 100%"></TEXTAREA> </P> </DL> </OL> </BODY> </HTML>
complex.js
/****************************/ /* 複雑な待ち行列問題 */ /* coded by Y.Suganuma */ /****************************/ base = null; cus = new Array(); // 系内にいる客のリスト inl = new Array(); // Inletオブジェクトリスト que = new Array(); // Queueオブジェクトリスト ent = new Array(); // Entityオブジェクトリスト check = true; /****************/ /* main program */ /****************/ function main() { check = true; cus.splice(0); // 系内にいる客のリスト inl.splice(0); // Inletオブジェクトリスト que.splice(0); // Queueオブジェクトリスト ent.splice(0); // Entityオブジェクトリスト /* 入力データ */ // 入り口 let n_i = parseInt(document.getElementById("n_i").value); // 入り口(Inlet)の数 for (let i1 = 1; i1 <= n_i; i1++) { let name1 = document.getElementById("i"+i1).value; // 入り口の名前 let n = parseInt(document.getElementById("a_t"+i1).value); // 到着分布 let m_a = 0; let y = new Array(); if (n == 0 || n == 1) m_a = parseFloat(document.getElementById("ma"+i1).value); // 平均到着時間間隔,到着時間間隔 else { if (n > 0) { alert("***error input1 人数が不適当です " + n); check = false; } else { let m = -n; let x = document.getElementById("at"+i1).value.split(" "); if (m != x.length) { alert("***error input2 人数とデータ数が異なっています " + m + " " + x.length); check = false; } else { for (let i2 = 0; i2 < m; i2++) y.push(parseFloat(x[i2])); } } } let name2 = document.getElementById("iq"+i1).value; // 並ぶ待ち行列の名前 let inl_e = new Inlet(name1, n, m_a, y, name2); inl.push(inl_e); } // 待ち行列 let n_q = parseInt(document.getElementById("n_q").value); // 待ち行列(Queue)の数 for (let i1 = 1; i1 <= n_q; i1++) { let name1 = document.getElementById("q"+i1).value; // 待ち行列の名前 let n = parseInt(document.getElementById("qi"+i1).value); // 入り口(0),または,窓口(n>0,窓口の数) let inn = new Array(); if (n == 0) { let name2 = document.getElementById("qii"+i1+"1").value; // 待ち行列の名前; inn.push(name2); } else { for (let i2 = 1; i2 <= n; i2++) { let name3 = document.getElementById("qie"+i1+i2).value; // 窓口の名前 inn.push(name3); } } let m = parseInt(document.getElementById("qo"+i1).value); // 処理する窓口の数 let out = new Array(); for (let i2 = 1; i2 <= m; i2++) { let name4 = document.getElementById("qoe"+i1+i2).value; // 窓口の名前 out.push(name4); } let que_e = new Queue(name1, n, inn, m, out); que.push(que_e); } // 窓口 let n_e = parseInt(document.getElementById("n_e").value); // 窓口(Entity)の数; for (let i1 = 1; i1 <= n_e; i1++) { let name1 = document.getElementById("e"+i1).value; // 窓口の名前 let s_t = parseInt(document.getElementById("s_t"+i1).value); // サービス分布 let m_s = parseFloat(document.getElementById("ms"+i1).value); // 平均サービス時間,サービス時間 let name2 = document.getElementById("eq"+i1).value; // 待ち行列(入力)の名前 let sw = parseInt(document.getElementById("eo"+i1).value); // 外部(0),または,待ち行列(1) let name3; if (sw > 0) name3 = document.getElementById("eoq"+i1).value; // 待ち行列(出力)の名前 let ent_e = new Entity(name1, s_t, m_s, name2, sw, name3); ent.push(ent_e); } // シミュレーション終了時間 let end = parseFloat(document.getElementById("end").value); // シミュレーション終了時間 if (check) { base = new Q_base(end) // 全体の制御を行うクラス if (check) { // 実行 base.Control(); // 出力 base.Output(); } } } /**********************/ /* 指数分布乱数の発生 */ /* m : 平均値 */ /* rerutn : 乱数 */ /**********************/ function Exp_b(m) { return -m * Math.log(Math.random()); } /************************************************************/ /* Inletオブジェクトの定義 */ /* name1 : 入り口名 */ /* a_t; // = -n : 到着する客の人数を負の値にしたもの */ /* // =0 : 指数分布 */ /* // =1 : 一定時間間隔 */ /* m_a: 到着時間間隔またはその平均 */ /* que : 客の到着時刻リスト */ /* name2 : 待ち行列名 */ /************************************************************/ function Inlet(name1, n, m_a, que, name2) { this.name = name1; // 入り口名 this.out = name2; // 待ち行列名 this.out_n; // 待ち行列番号 this.a_t = n; // = -n : 到着する客の人数を負の値にしたもの // =0 : 指数分布 // =1 : 一定時間間隔 this.mean = m_a; // 到着時間間隔またはその平均 this.que = que; // 客の到着時刻リスト if (this.a_t == 0) // 次の客の到着時刻(負:客がない) this.arrive_time = Exp_b(this.mean); else if (this.a_t == 1) this.arrive_time = 0; else this.arrive_time = que.shift(); return this; } /*********************************************/ /* Queueオブジェクトの定義 */ /* name1 : 待ち行列名 */ /* n1 : =0 : 入り口から入る */ /* >0 : 複数の窓口から入る(窓口数) */ /* in1 : 入り口名,または,窓口名 */ /* m1 : 処理する窓口数 */ /* out1 : 窓口名 */ /*********************************************/ function Queue(name1, n1, in1, m1, out1) { this.name = name1; // 待ち行列名 this.nc = 0; // 待ち行列への到着客数カウンタ this.max_q_l = 0; // 最大待ち行列長 this.c_ql = 0.0; // 待ち行列長にその長さが継続した時間を乗じた値の累計 this.ql_t = 0.0; // 現在の待ち行列長になった時間 this.max_wt = 0.0; // 最大待ち時間 this.c_wt = 0.0; // 待ち時間の累計 this.n = n1; // =0 : 入り口から入る // >0 : 複数の窓口から入る(窓口数) this.inn = in1; // 入り口名,または,窓口名 this.in_n = new Array(); // 入り口番号,または,窓口番号 this.m = m1; // 処理する窓口数 this.out = out1; // 窓口名 this.out_n = new Array(); // 窓口番号 this.que = new Array(); // 待ち行列 return this; } /*************************************/ /* Entityオブジェクトの定義 */ /* name1 : 窓口名 */ /* int s_t; // =0 : 指数分布 */ /* // =1 : 一定時間 */ /* m_s:平均サービス時間 */ /* name2 : 待ち行列(入力)の名前 */ /* sw : =0 : システムの外に出る */ /* =1 : 待ち行列に入る */ /* name3 : 待ち行列(出力)の名前 */ /*************************************/ function Entity(name1, s_t, m_s, name2, sw, name3) { this.name = name1; // 窓口名 this.end_time = -1.0; // サービス終了時刻(負:何も処理していない) this.s_t = s_t; // =0 : 指数分布 // =1 : 一定時間 this.mean = m_s; // 平均サービス時間 this.busy_t = -1.0; // 窓口がふさがった時刻 this.c_busy = 0.0; // 窓口がふさがっている時間の累計 this.service = 0; // サービス中の客番号(0のときは無し) this.inn = name2; // 待ち行列(入力)の名前 this.in_n = 0; // 待ち行列(入力)番号 this.to = sw; // =0 : システムの外に出る // =1 : 待ち行列に入る this.out = name3; // 待ち行列(出力)の名前 this.out_n = 0; // 待ち行列(出力)番号 return this; } /******************************/ /* Customerオブジェクトの定義 */ /* n : 客番号 * /* s1,s2 : 状態 */ /* t : 到着時刻 */ /******************************/ function Customer(n, s1, s2, t) { this.num = n; // 客番号 this.state1 = s1; // 客の状態1 // =0 : 待ち行列に入ってい // =1 : サービスを受けている this.state2 = s2; // 客の状態2(待ち行列番号,または,窓口番号) this.time = t; // 到着時刻 return this; } /*************************************/ /* Q_baseオブジェクトの定義 */ /* e : シミュレーション終了時刻 */ /*************************************/ function Q_base(e) { this.p_time = 0.0; // 現在時刻 this.max_c = 0; // 最大系内客数 this.nc = 0; // システムへの到着客数カウンタ this.now_c_t = 0.0; // 現在の系内客数になった時間 this.c_now_c = 0.0; // 系内客数にその数が継続した時間を乗じた値の累計 this.c_sys = 0.0; // 滞在時間の累計 this.max_sys = 0.0; // 最大滞在時間 this.end = e; // シミュレーション終了時間 // 接続関係のチェック // Inlet for (let i1 = 0; i1 < inl.length-1; i1++) { for (let i2 = i1+1; i2 < inl.length; i2++) { if (inl[i1].name == inl[i2].name) { alert("***error1 同じ名前の入り口があります " + inl[i1].name); check = false; } } } let k; for (let i1 = 0; i1 < inl.length; i1++) { k = -1; for (let i2 = 0; i2 < que.length; i2++) { if (inl[i1].out == que[i2].name) { k = i2; break; } } if (k >= 0) inl[i1].out_n = k; else { alert("***error2 入り口から入る待ち行列名が不適当です " + inl[i1].out); check = false; } } // Queue for (let i1 = 0; i1 < que.length-1; i1++) { for (let i2 = i1+1; i2 < que.length; i2++) { if (que[i1].name == que[i2].name) { alert("***error3 同じ名前の待ち行列があります " + que[i1].name); check = false; } } } for (let i1 = 0; i1 < que.length; i1++) { if (que[i1].n == 0) { k = -1; for (let i2 = 0; i2 < inl.length; i2++) { if (que[i1].inn[0] == inl[i2].name) { k = i2; break; } } if (k >= 0) que[i1].in_n.push(k); else { alert("***error4 待ち行列に入る入り口名が不適当です " + que[i1].inn[0]); check = false; } } else { for (let i2 = 0; i2 < que[i1].n; i2++) { k = -1; for (let i3 = 0; i3 < ent.length; i3++) { if (que[i1].inn[i2] == ent[i3].name) { k = i3; break; } } if (k >= 0) que[i1].in_n.push(k); else { alert("***error5 待ち行列に入る窓口名が不適当です " + que[i1].inn[i2]); check = false; } } } for (let i2 = 0; i2 < que[i1].m; i2++) { k = -1; for (let i3 = 0; i3 < ent.length; i3++) { if (que[i1].out[i2] == ent[i3].name) { k = i3; break; } } if (k >= 0) que[i1].out_n.push(k); else { alert("***error6 待ち行列を処理する窓口名が不適当です " + que[i1].out[i2]); check = false; } } } // Entity for (let i1 = 0; i1 < ent.length-1; i1++) { k = -1; for (let i2 = i1+1; i2 < ent.length; i2++) { if (ent[i1].name == ent[i2].name) { alert("***error7 同じ名前の窓口があります " + ent[i1].name); check = false; } } } for (let i1 = 0; i1 < ent.length; i1++) { k = -1; for (let i2 = 0; i2 < que.length; i2++) { if (ent[i1].inn == que[i2].name) { k = i2; break; } } if (k >= 0) ent[i1].in_n = k; else { alert("***error8 窓口に入る待ち行列名が不適当です " + ent[i1].inn); check = false; } if (ent[i1].to > 0) { k = -1; for (let i2 = 0; i2 < que.length; i2++) { if (ent[i1].out == que[i2].name) { k = i2; break; } } if (k >= 0) ent[i1].out_n = k; else { alert("***error9 窓口の出口にある待ち行列名が不適当です " + ent[i1].out); check = false; } } } return this; } /**************/ /* 全体の制御 */ /**************/ Q_base.prototype.Control = function() { let sw = new Array(2); sw[0] = 0; while (sw[0] > -2) { base.Next(sw); // 次の処理の選択 if (sw[0] == -1) sw[0] = base.End_o_s(); // シミュレーションの終了 else { if (sw[0] == 0) base.Arrive(sw[1]); // 客の到着処理 else base.Service(sw[1]); // サービスの終了 } } } /*********************************************/ /* 次の処理の決定 */ /* sw[0] : =-1 : シミュレーションの終了 */ /* =0 : 客の到着処理 */ /* =1 : 窓口のサービス終了 */ /* [1] : 入り口番号 or 窓口番号 */ /*********************************************/ Q_base.prototype.Next = function(sw) { let tm = base.end; // 次の処理時刻 sw[0] = -1; // 次の客の到着時刻 for (let i1 = 0; i1 < inl.length; i1++) { let x = inl[i1]; if (x.arrive_time >= 0.0 && x.arrive_time < tm) { sw[0] = 0; sw[1] = i1; tm = x.arrive_time; } } // サービス終了時刻 for (let i1 = 0; i1 < ent.length; i1++) { let x = ent[i1]; if (x.service > 0 && x.end_time <= tm) { sw[0] = 1; sw[1] = i1; tm = x.end_time; } } if (sw[0] < 0) base.end = base.p_time; return sw; } /**********************************/ /* 終了処理 */ /* return : =-1 : 終了前処理 */ /* =-2 : 実際の終了 */ /**********************************/ Q_base.prototype.End_o_s = function() { let sw = -2; base.p_time = base.end; // 現在時刻 for (let i1 = 0; i1 < ent.length; i1++) { let x = ent[i1]; if (x.service > 0) { // サービス中の客はいるか? if (sw == -2) { sw = -1; base.end = x.end_time; // 窓口i1のサービス終了時刻 } else { if (x.end_time > base.end) base.end = x.end_time; // 窓口i1のサービス終了時刻 } } } return sw; } /************************/ /* 客の到着処理 */ /* kk : 入り口番号 */ /************************/ Q_base.prototype.Arrive = function(kk) { /* 客数の増加と次の客の到着時刻の設定 */ base.nc += 1; // 到着客数カウンタ base.p_time = inl[kk].arrive_time; // 現在時刻 if (inl[kk].a_t == 0) // 次の客の到着時刻 inl[kk].arrive_time = base.p_time + Exp_b(inl[kk].mean); else if (inl[kk].a_t == 1) inl[kk].arrive_time = base.p_time + inl[kk].mean; else { if (inl[kk].que.length <= 0) inl[kk].arrive_time = -1.0; else inl[kk].arrive_time = inl[kk].que.shift(); } if (inl[kk].arrive_time >= base.end) inl[kk].arrive_time = -1.0; /* 系内客数の処理 */ base.c_now_c += cus.length * (base.p_time - base.now_c_t); // 系内客数にその数が継続した時間を乗じた値の累計 base.now_c_t = base.p_time; // 現在の系内客数になった時刻 if (cus.length+1 > base.max_c) base.max_c = cus.length + 1; // 最大系内客数 /* 空いている窓口を探す */ let k1 = inl[kk].out_n; que[k1].nc++; let k = -1; for (let i1 = 0; i1 < que[k1].m; i1++) { let k2 = que[k1].out_n[i1]; // 処理窓口 if (ent[k2].service == 0) { k = k2; break; } } /* 窓口に空きがない場合 */ if (k < 0) { let ct_p = new Customer(base.nc, 0, k1, base.p_time); cus.push(ct_p); // 客の登録(系内客数) que[k1].c_ql += que[k1].que.length * (base.p_time - que[k1].ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 que[k1].que.push(base.nc); // 客の登録(待ち行列) que[k1].ql_t = base.p_time; // 現在の待ち行列長になった時刻 if (que[k1].que.length > que[k1].max_q_l) que[k1].max_q_l = que[k1].que.length; // 最大待ち行列長 } /* すぐサービスをうけられる場合 */ else { let ct_p = new Customer(base.nc, 1, k, base.p_time); cus.push(ct_p); // 客の登録(系内客数) ent[k].service = base.nc; // サービスを受けている客番号 ent[k].busy_t = base.p_time; // 窓口がふさがった時刻 if (ent[k].s_t == 0) // 窓口のサービス終了時刻 ent[k].end_time = base.p_time + Exp_b(ent[k].mean); else ent[k].end_time = base.p_time + ent[k].mean; } } /**********************************/ /* サービス終了時の処理 */ /* kk : サービス終了窓口番号 */ /**********************************/ Q_base.prototype.Service = function(kk) { /* 時間の設定 */ base.p_time = ent[kk].end_time; // 現在時刻 ent[kk].end_time = -1.0; // サービス終了時間 /* システムの外へ出る場合 */ if (ent[kk].to == 0) { /* 系内客数の処理 */ base.c_now_c += cus.length * (base.p_time - base.now_c_t); // 系内客数にその数が継続した時間を乗じた値の累計 base.now_c_t = base.p_time; // 現在の系内客数になった時刻 /* 滞在時間の処理 */ let n = base.Search(ent[kk].service); // サービス中の客 let x1 = base.p_time - cus[n].time; base.c_sys += x1; // 滞在時間の累計 if (x1 > base.max_sys) base.max_sys = x1; // 最大滞在時間 cus.splice(n, 1); // 客の削除(系内客数) } /* 他の窓口処理へ入る場合の処理 */ else { let k1 = ent[kk].out_n; que[k1].nc++; let sw = 1; let k2 = 0; if (que[k1].que.length == 0) { for (let i1 = 0; i1 < que[k1].m; i1++) { k2 = que[k1].out_n[i1]; // 窓口 if (ent[k2].service == 0) { sw = 0; break; } } } /* 待ち行列がある場合 */ if (sw > 0) { que[k1].c_ql += que[k1].que.length * (base.p_time - que[k1].ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 que[k1].que.push(ent[kk].service); // 客の登録(待ち行列) que[k1].ql_t = base.p_time; // 現在の待ち行列長になった時刻 if (que[k1].que.length > que[k1].max_q_l) que[k1].max_q_l = que[k1].que.length; // 最大待ち行列長 let m = base.Search(ent[kk].service); cus[m].state1 = 0; // 客の状態変更(待ち行列) cus[m].state2 = ent[kk].out_n; // 客の状態変更(待ち行列番号) } /* すぐサービスをうけられる場合 */ else { ent[k2].service = ent[kk].service; // サービスを受けている客番号 ent[k2].busy_t = base.p_time; // 窓口がふさがった時刻 if (ent[k2].s_t == 0) // 窓口のサービス終了時刻 ent[k2].end_time = base.p_time + Exp_b(ent[k2].mean); else ent[k2].end_time = base.p_time + ent[k2].mean; } } /* 窓口使用時間の処理 */ ent[kk].service = 0; // 窓口を空き状態にする ent[kk].c_busy += (base.p_time - ent[kk].busy_t); // 窓口がふさがっている時間の累計 /* この窓口に対する待ち行列がある場合 */ let k3 = ent[kk].in_n; if (que[k3].que.length > 0) { que[k3].c_ql += que[k3].que.length * (base.p_time - que[k3].ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 let n = que[k3].que.shift(); // 待ち行列の先頭にいる客の削除 que[k3].ql_t = base.p_time; // 現在の待ち行列長になった時刻 let m = base.Search(n); x1 = base.p_time - cus[m].time; que[k3].c_wt += x1; // 待ち時間の累計 if (x1 > que[k3].max_wt) que[k3].max_wt = x1; // 最大待ち時間 for (let i1 = 0; i1 < que[k3].m; i1++) { let k4 = que[k3].out_n[i1]; // 窓口 if (ent[k4].service == 0) { ent[k4].service = n; // 窓口の客番号 ent[k4].busy_t = base.p_time; // 窓口がふさがった時刻 if (ent[k4].s_t == 0) // 窓口のサービス終了時刻 ent[k4].end_time = base.p_time + Exp_b(ent[k4].mean); else ent[k4].end_time = base.p_time + ent[k4].mean; cus[m].state1 = 1; // 客の状態変更(サービス中) cus[m].state2 = k4; // 客の状態変更(窓口番号) break; } } } } /****************************/ /* 指定した客の配列上の位置 */ /* nm : 客番号 */ /* return : 要素番号 */ /****************************/ Q_base.prototype.Search = function(nm) { let n = -1; for (let i1 = 0; i1 < cus.length; i1++) { if (cus[i1].num == nm) { n = i1; break; } } return n; } /**************************/ /* 統計量の計算と最終出力 */ /**************************/ Q_base.prototype.Output = function() { // System let str = "全客数 " + base.nc; str += " 最大系内客数 " + base.max_c + " 最大滞在時間 " + Math.round(1000*base.max_sys)/1000 + "\n"; str += "平均系内客数 " + Math.round(1000*base.c_now_c/base.p_time)/1000; str += " 平均滞在時間 " + Math.round(1000*base.c_sys/base.nc)/1000; str += " 終了時間 " + Math.round(1000*base.p_time)/1000 + "\n"; // Entity for (let i1 = 0; i1 < ent.length; i1++) { let e = ent[i1]; str += "Entity " + e.name; str += " 稼働率 " + Math.round(1000*e.c_busy/base.p_time)/1000 + "\n"; } // Queue for (let i1 = 0; i1 < que.length; i1++) { let q = que[i1]; str += "Queue " + q.name; str += " 客数 " + q.nc; str += " 最大待ち行列長 " + q.max_q_l; str += " 最大待ち時間 " + Math.round(1000*q.max_wt)/1000 + "\n"; str += " 平均待ち行列長 " + Math.round(1000*q.c_ql/base.p_time)/1000; str += " 平均待ち時間 " + Math.round(1000*q.c_wt/q.nc)/1000 + "\n"; } document.getElementById("res").value = str; }
<?php /******************************/ /* 複雑な待ち行列問題 */ /* coded by Y.Suganuma */ /******************************/ /**********************/ /* 指数分布乱数の発生 */ /* m : 平均値 */ /* rerutn : 乱数 */ /**********************/ function Exp_b($m) { return -$m * log(mt_rand() / mt_getrandmax()); } /*********************/ /* クラスInletの定義 */ /*********************/ class Inlet { public $name; // 入り口名 public $out; // 待ち行列名 public $out_n; // 待ち行列番号 public $arrive_time; // 客の到着時刻(負:客がない) public $a_t; // = -n : 到着する客の人数を負の値にしたもの // =0 : 指数分布 // =1 : 一定時間間隔 public $mean; // 到着時間間隔またはその平均 public $que; // 客の到着時刻リスト /*************************************************************/ /* コンストラクタ */ /* name1 : 入り口名 */ /* a_t1; // = -n : 到着する客の人数を負の値にしたもの */ /* // =0 : 指数分布 */ /* // =1 : 一定時間間隔 */ /* m_a: 到着時間間隔またはその平均 */ /* que1 : 客の到着時刻リスト */ /* name2 : 待ち行列名 */ /*************************************************************/ function Inlet ($name1, $a_t1, $m_a, $que1, $name2) { $this->name = $name1; $this->out = $name2; $this->a_t = $a_t1; $this->mean = $m_a; if ($this->a_t == 0) $this->arrive_time = Exp_b($this->mean); else if ($this->a_t == 1) $this->arrive_time = 0; else { $this->que = $que1; $this->arrive_time = array_shift($this->que); } } } /*********************/ /* クラスQueueの定義 */ /*********************/ class Queue { public $name; // 待ち行列名 public $nc; // 待ち行列への到着客数カウンタ public $max_q_l; // 最大待ち行列長 public $c_ql; // 待ち行列長にその長さが継続した時間を乗じた値の累計 public $ql_t; // 現在の待ち行列長になった時間 public $max_wt; // 最大待ち時間 public $c_wt; // 待ち時間の累計 public $n; // =0 : 入り口から入る // >0 : 複数の窓口から入る(窓口数) public $in; // 入り口名,または,窓口名 public $in_n; // 入り口番号,または,窓口番号 public $m; // 処理する窓口数 public $out; // 窓口名 public $out_n; // 窓口番号 public $que; // 待ち行列 /*********************************************/ /* コンストラクタ */ /* name1 : 待ち行列名 */ /* n1 : =0 : 入り口から入る */ /* >0 : 複数の窓口から入る(窓口数) */ /* in1 : 入り口名,または,窓口名 */ /* m1 : 処理する窓口数 */ /* out1 : 窓口名 */ /*********************************************/ function Queue($name1, $n1, $in1, $m1, $out1) { $this->name = $name1; $this->n = $n1; $this->in = $in1; $this->m = $m1; $this->out = $out1; $this->nc = 0; $this->max_q_l = 0; $this->c_ql = 0.0; $this->ql_t = 0.0; $this->max_wt = 0.0; $this->c_wt = 0.0; $this->in_n = array(); $this->out_n = array(); $this->que = array(); } } /**********************/ /* クラスEntityの定義 */ /**********************/ class Entity { public $name; // 窓口名 public $end_time; // サービス終了時刻(負:何も処理していない) public $s_t; // =0 : 指数分布 // =1 : 一定時間 public $mean; // 平均サービス時間 public $busy_t; // 窓口がふさがった時刻 public $c_busy; // 窓口がふさがっている時間の累計 public $service; // サービス中の客番号(0のときは無し) public $in; // 待ち行列(入力)の名前 public $in_n; // 待ち行列(入力)番号 public $to; // =0 : システムの外に出る // =1 : 待ち行列に入る public $out; // 待ち行列(出力)の名前 public $out_n; // 待ち行列(出力)番号 /****************************************/ /* コンストラクタ */ /* name1 : 窓口名 */ /* s_t1; // =0 : 指数分布 */ /* // =1 : 一定時間 */ /* m_s:サービス時間またはその平均 */ /* name2 : 待ち行列(入力)の名前 */ /* sw : =0 : システムの外に出る */ /* =1 : 待ち行列に入る */ /* name3 : 待ち行列(出力)の名前 */ /**********************************+++***/ function Entity($name1, $s_t1, $m_s, $name2, $sw, $name3) { $this->name = $name1; $this->to = $sw; $this->in = $name2; if ($this->to > 0) $this->out = $name3; $this->end_time = -1.0; $this->s_t = $s_t1; $this->mean = $m_s; $this->service = 0; $this->busy_t = -1.0; $this->c_busy = 0.0; } } /************************/ /* クラスCustomerの定義 */ /************************/ class Customer { public $time; // 到着時刻 public $state1; // 客の状態1 // =0 : 待ち行列に入っている // =1 : サービスを受けている public $state2; // 客の状態2(待ち行列番号,または,窓口番号) /*********************/ /* コンストラクタ */ /* s1,s2 : 状態 */ /* t : 到着時刻 */ /*******************************/ function Customer($s1, $s2, $t) { $this->time = $t; $this->state1 = $s1; $this->state2 = $s2; } } /**********************/ /* クラスQ_baseの定義 */ /**********************/ class Q_base { public $p_time; // 現在時刻 public $max_c; // 最大系内客数 public $nc; // システムへの到着客数カウンタ public $now_c_t; // 現在の系内客数になった時間 public $c_now_c; // 系内客数にその数が継続した時間を乗じた値の累計 public $c_sys; // 滞在時間の累計 public $max_sys; // 最大滞在時間 public $end; // シミュレーション終了時間 public $cus; // 系内にいる客のリスト public $inl; // Inletオブジェクトリスト public $que; // Queueオブジェクトリスト public $ent; // Entityオブジェクトリスト /***************************************/ /* コンストラクタ */ /* v_i : Inletオブジェクトリスト */ /* v_q : Queueオブジェクトリスト */ /* v_e : Entityオブジェクトリスト */ /* e : シミュレーション終了時刻 */ /***************************************/ function Q_base($v_i, $v_q, $v_e, $e) { // 接続関係のチェック print("\n"); // Inlet $this->inl = $v_i; $this->que = $v_q; $this->ent = $v_e; for ($i1 = 0; $i1 < count($this->inl)-1; $i1++) { for ($i2 = $i1+1; $i2 < count($this->inl); $i2++) { if ($this->inl[$i1]->name == $this->inl[$i2]->name) exit("***error 同じ名前の入り口があります ".$this->inl[$i1]->name."\n"); } } $k; for ($i1 = 0; $i1 < count($this->inl); $i1++) { $k = -1; for ($i2 = 0; $i2 < count($this->que); $i2++) { if ($this->inl[$i1]->out == $this->que[$i2]->name) { $k = $i2; break; } } if ($k >= 0) $this->inl[$i1]->out_n = $k; else exit("***error 入り口から入る待ち行列名が不適当です ".$this->inl[$i1]->out."\n"); } // Queue for ($i1 = 0; $i1 < count($this->que)-1; $i1++) { for ($i2 = $i1+1; $i2 < count($this->que); $i2++) { if ($this->que[$i1]->name == $this->que[$i2]->name) exit("***error 同じ名前の待ち行列があります ".$this->que[$i1]->name."\n"); } } for ($i1 = 0; $i1 < count($this->que); $i1++) { if ($this->que[$i1]->n == 0) { $k = -1; for ($i2 = 0; $i2 < count($this->inl); $i2++) { if ($this->que[$i1]->in[0] == $this->inl[$i2]->name) { $k = $i2; break; } } if ($k >= 0) array_push($this->que[$i1]->in_n, $k); else exit("***error 待ち行列に入る入り口名が不適当です ".$this->que[$i1]->in[0]."\n"); } else { for ($i2 = 0; $i2 < $this->que[$i1]->n; $i2++) { $k = -1; for ($i3 = 0; $i3 < count($this->ent); $i3++) { if ($this->que[$i1]->in[$i2] == $this->ent[$i3]->name) { $k = $i3; break; } } if ($k >= 0) array_push($this->que[$i1]->in_n, $k); else exit("***error 待ち行列に入る窓口名が不適当です ".$this->que[$i1]->in[$i2]."\n"); } } for ($i2 = 0; $i2 < $this->que[$i1]->m; $i2++) { $k = -1; for ($i3 = 0; $i3 < count($this->ent); $i3++) { if ($this->que[$i1]->out[$i2] == $this->ent[$i3]->name) { $k = $i3; break; } } if ($k >= 0) array_push($this->que[$i1]->out_n, $k); else exit("***error 待ち行列を処理する窓口名が不適当です ".$this->que[$i1]->out[$i2]."\n"); } } // Entity for ($i1 = 0; $i1 < count($this->ent)-1; $i1++) { $k = -1; for ($i2 = $i1+1; $i2 < count($this->ent); $i2++) { if ($this->ent[$i1]->name == $this->ent[$i2]->name) exit("***error 同じ名前の窓口があります ".$this->ent[$i1]->name."\n"); } } for ($i1 = 0; $i1 < count($this->ent); $i1++) { $k = -1; for ($i2 = 0; $i2 < count($this->que); $i2++) { if ($this->ent[$i1]->in == $this->que[$i2]->name) { $k = $i2; break; } } if ($k >= 0) $this->ent[$i1]->in_n = $k; else exit("***error 窓口に入る待ち行列名が不適当です ".$this->ent[$i1]->in."\n"); if ($this->ent[$i1]->to > 0) { $k = -1; for ($i2 = 0; $i2 < count($this->que); $i2++) { if ($this->ent[$i1]->out == $this->que[$i2]->name) { $k = $i2; break; } } if ($k >= 0) $this->ent[$i1]->out_n = $k; else exit("***error 窓口の出口にある待ち行列名が不適当です ".$this->ent[$i1]->out."\n"); } } // 初期設定 $this->p_time = 0.0; $this->max_c = 0; $this->nc = 0; $this->now_c_t = 0.0; $this->c_now_c = 0.0; $this->c_sys = 0.0; $this->max_sys = 0.0; $this->end = $e; $this->cus = array(); // 乱数の初期設定 mt_srand(); } /**************/ /* 全体の制御 */ /**************/ function Control() { $sw = array(2); $sw[0] = 0; while ($sw[0] > -2) { $this->Next($sw); // 次の処理の選択 if ($sw[0] == -1) $sw[0] = $this->End_o_s(); // シミュレーションの終了 else { if ($sw[0] == 0) $this->Arrive($sw[1]); // 客の到着処理 else $this->Service($sw[1]); // サービスの終了 } } } /*********************************************/ /* 次の処理の決定 */ /* sw[0] : =-1 : シミュレーションの終了 */ /* =0 : 客の到着処理 */ /* =1 : 窓口のサービス終了 */ /* [1] : 入り口番号 or 窓口番号 */ /*********************************************/ function Next(&$sw) { $tm = $this->end; // 次の処理時刻 $sw[0] = -1; // 次の客の到着時刻 for ($i1 = 0; $i1 < count($this->inl); $i1++) { $x = $this->inl[$i1]; if ($x->arrive_time >= 0.0 && $x->arrive_time < $tm) { $sw[0] = 0; $sw[1] = $i1; $tm = $x->arrive_time; } } // サービス終了時刻 for ($i1 = 0; $i1 < count($this->ent); $i1++) { $x = $this->ent[$i1]; if ($x->service > 0 && $x->end_time <= $tm) { $sw[0] = 1; $sw[1] = $i1; $tm = $x->end_time; } } if ($sw[0] < 0) $this->end = $this->p_time; } /**********************************/ /* 終了処理 */ /* return : =-1 : 終了前処理 */ /* =-2 : 実際の終了 */ /**********************************/ function End_o_s() { $sw = -2; $this->p_time = $this->end; // 現在時刻 for ($i1 = 0; $i1 < count($this->ent); $i1++) { $x = $this->ent[$i1]; if ($x->service > 0) { // サービス中の客はいるか? if ($sw == -2) { $sw = -1; $this->end = $x->end_time; // 窓口$i1のサービス終了時刻 } else { if ($x->end_time > $this->end) $this->end = $x->end_time; // 窓口$i1のサービス終了時刻 } } } return $sw; } /************************/ /* 客の到着処理 */ /* kk : 入り口番号 */ /************************/ function Arrive($kk) { /* 客数の増加と次の客の到着時刻の設定 */ $this->nc += 1; // 到着客数カウンタ $this->p_time = $this->inl[$kk]->arrive_time; // 現在時刻 if ($this->inl[$kk]->a_t == 0) // 次の客の到着時刻 $this->inl[$kk]->arrive_time = $this->p_time + Exp_b($this->inl[$kk]->mean); else if ($this->inl[$kk]->a_t == 1) $this->inl[$kk]->arrive_time = $this->p_time + $this->inl[$kk]->mean; else { if (count($this->inl[$kk]->que) <= 0) $this->inl[$kk]->arrive_time = -1.0; else $this->inl[$kk]->arrive_time = array_shift($this->inl[$kk]->que); } if ($this->inl[$kk]->arrive_time >= $this->end) $this->inl[$kk]->arrive_time = -1.0; /* 系内客数の処理 */ $this->c_now_c += count($this->cus) * ($this->p_time - $this->now_c_t); // 系内客数にその数が継続した時間を乗じた値の累計 $this->now_c_t = $this->p_time; // 現在の系内客数になった時刻 if (count($this->cus)+1 > $this->max_c) $this->max_c = count($this->cus) + 1; // 最大系内客数 /* 空いている窓口を探す */ $k1 = $this->inl[$kk]->out_n; $this->que[$k1]->nc++; $k = -1; for ($i1 = 0; $i1 < $this->que[$k1]->m; $i1++) { $k2 = $this->que[$k1]->out_n[$i1]; // 処理窓口 if ($this->ent[$k2]->service == 0) { $k = $k2; break; } } /* 窓口に空きがない場合 */ if ($k < 0) { $ct_p = new Customer(0, $k1, $this->p_time); $this->cus['no'.strval($this->nc)] = $ct_p; // 客の登録(系内客数) $this->que[$k1]->c_ql += count($this->que[$k1]->que) * ($this->p_time - $this->que[$k1]->ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 array_push($this->que[$k1]->que, $this->nc); // 客の登録(待ち行列) $this->que[$k1]->ql_t = $this->p_time; // 現在の待ち行列長になった時刻 if (count($this->que[$k1]->que) > $this->que[$k1]->max_q_l) $this->que[$k1]->max_q_l = count($this->que[$k1]->que); // 最大待ち行列長 } /* すぐサービスをうけられる場合 */ else { $ct_p = new Customer(1, $k, $this->p_time); $this->cus['no'.strval($this->nc)] = $ct_p; // 客の登録(系内客数) $this->ent[$k]->service = $this->nc; // サービスを受けている客番号 $this->ent[$k]->busy_t = $this->p_time; // 窓口がふさがった時刻 if ($this->ent[$k]->s_t == 0) // 窓口のサービス終了時刻 $this->ent[$k]->end_time = $this->p_time + Exp_b($this->ent[$k]->mean); else $this->ent[$k]->end_time = $this->p_time + $this->ent[$k]->mean; } } /**********************************/ /* サービス終了時の処理 */ /* kk : サービス終了窓口番号 */ /**********************************/ function Service($kk) { /* 時間の設定 */ $this->p_time = $this->ent[$kk]->end_time; // 現在時刻 $this->ent[$kk]->end_time = -1.0; // サービス終了時間 /* システムの外へ出る場合 */ if ($this->ent[$kk]->to == 0) { /* 系内客数の処理 */ $this->c_now_c += count($this->cus) * ($this->p_time - $this->now_c_t); // 系内客数にその数が継続した時間を乗じた値の累計 $this->now_c_t = $this->p_time; // 現在の系内客数になった時刻 /* 滞在時間の処理 */ $it = $this->cus['no'.strval($this->ent[$kk]->service)]; // サービス中の客 $x1 = $this->p_time - $it->time; $this->c_sys += $x1; // 滞在時間の累計 if ($x1 > $this->max_sys) $this->max_sys = $x1; // 最大滞在時間 unset($this->cus['no'.strval($this->ent[$kk]->service)]); // 客の削除(系内客数) } /* 他の窓口処理へ入る場合の処理 */ else { $k1 = $this->ent[$kk]->out_n; $this->que[$k1]->nc++; $sw = 1; $k2 = 0; if (count($this->que[$k1]->que) == 0) { for ($i1 = 0; $i1 < $this->que[$k1]->m; $i1++) { $k2 = $this->que[$k1]->out_n[$i1]; // 窓口 if ($this->ent[$k2]->service == 0) { $sw = 0; break; } } } /* 待ち行列がある場合 */ if ($sw > 0) { $this->que[$k1]->c_ql += count($this->que[$k1]->que) * ($this->p_time - $this->que[$k1]->ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 array_push($this->que[$k1]->que, $this->ent[$kk]->service); // 客の登録(待ち行列) $this->que[$k1]->ql_t = $this->p_time; // 現在の待ち行列長になった時刻 if (count($this->que[$k1]->que) > $this->que[$k1]->max_q_l) $this->que[$k1]->max_q_l = count($this->que[$k1]->que); // 最大待ち行列長 $it = $this->cus['no'.strval($this->ent[$kk]->service)]; $it->state1 = 0; // 客の状態変更(待ち行列) $it->state2 = $this->ent[$kk]->out_n; // 客の状態変更(待ち行列番号) } /* すぐサービスをうけられる場合 */ else { $this->ent[$k2]->service = $this->ent[$kk]->service; // サービスを受けている客番号 $this->ent[$k2]->busy_t = $this->p_time; // 窓口がふさがった時刻 if ($this->ent[$k2]->s_t == 0) // 窓口のサービス終了時刻 $this->ent[$k2]->end_time = $this->p_time + Exp_b($this->ent[$k2]->mean); else $this->ent[$k2]->end_time = $this->p_time + $this->ent[$k2]->mean; } } /* 窓口使用時間の処理 */ $this->ent[$kk]->service = 0; // 窓口を空き状態にする $this->ent[$kk]->c_busy += ($this->p_time - $this->ent[$kk]->busy_t); // 窓口がふさがっている時間の累計 /* この窓口に対する待ち行列がある場合 */ $k3 = $this->ent[$kk]->in_n; if (count($this->que[$k3]->que) > 0) { $this->que[$k3]->c_ql += count($this->que[$k3]->que) * ($this->p_time - $this->que[$k3]->ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 $n = array_shift($this->que[$k3]->que); // 待ち行列の先頭にいる客 $this->que[$k3]->ql_t = $this->p_time; // 現在の待ち行列長になった時刻 $it = $this->cus['no'.$n]; $x1 = $this->p_time - $it->time; $this->que[$k3]->c_wt += $x1; // 待ち時間の累計 if ($x1 > $this->que[$k3]->max_wt) $this->que[$k3]->max_wt = $x1; // 最大待ち時間 for ($i1 = 0; $i1 < $this->que[$k3]->m; $i1++) { $k4 = $this->que[$k3]->out_n[$i1]; // 窓口 if ($this->ent[$k4]->service == 0) { $this->ent[$k4]->service = $n; // 窓口の客番号 $this->ent[$k4]->busy_t = $this->p_time; // 窓口がふさがった時刻 if ($this->ent[$k4]->s_t == 0) // 窓口のサービス終了時刻 $this->ent[$k4]->end_time = $this->p_time + Exp_b($this->ent[$k4]->mean); else $this->ent[$k4]->end_time = $this->p_time + $this->ent[$k4]->mean; $it->state1 = 1; // 客の状態変更(サービス中) $it->state2 = $k4; // 客の状態変更(窓口番号) break; } } } } /**************************/ /* 統計量の計算と最終出力 */ /**************************/ function Output() { // System printf("全客数 %d", $this->nc); printf(" 最大系内客数 %d 最大滞在時間 %.3f\n", $this->max_c, $this->max_sys); printf("平均系内客数 %.3f", $this->c_now_c / $this->p_time); printf(" 平均滞在時間 %.3f", $this->c_sys / $this->nc); printf(" 終了時間 %.3f\n", $this->p_time); // Entity for ($i1 = 0; $i1 < count($this->ent); $i1++) { $e = $this->ent[$i1]; print("Entity ".$e->name); printf(" 稼働率 %.3f\n", $e->c_busy / $this->p_time); } // Queue for ($i1 = 0; $i1 < count($this->que); $i1++) { $q = $this->que[$i1]; print("Queue ".$q->name); printf(" 客数 %d", $q->nc); printf(" 最大待ち行列長 %d", $q->max_q_l); printf(" 最大待ち時間 %.3f\n", $q->max_wt); printf(" 平均待ち行列長 %.3f", $q->c_ql / $this->p_time); printf(" 平均待ち時間 %.3f\n", $q->c_wt / $q->nc); } } } /****************/ /* main program */ /****************/ // 入り口 printf("入り口(Inlet)の数は? "); fscanf(STDIN, "%d", $n_i); $inl = array(); for ($i1 = 0; $i1 < $n_i; $i1++) { printf("%d 番目の入り口(Inlet)\n", $i1+1); printf(" 名前は? "); fscanf(STDIN, "%s", $name1); $que = array(); printf(" 到着分布(=0:指数分布,=1:一定時間間隔,<0:指定,客数の負値)? "); fscanf(STDIN, "%d", $n); if ($n == 0) { printf(" 到着時間間隔の平均値は? "); fscanf(STDIN, "%lf", $m_a); } else if ($n == 1) { printf(" 到着時間間隔は? "); fscanf(STDIN, "%lf", $m_a); } else { for ($i2 = 0; $i2 < -$n; $i2++) { printf(" 到着時間は? "); fscanf(STDIN, "%lf", $x); array_push($que, $x); } } printf(" 並ぶ待ち行列の名前は? "); fscanf(STDIN, "%s", $name2); $inl_e = new Inlet($name1, $n, $m_a, $que, $name2); array_push($inl, $inl_e); } // 待ち行列 printf("待ち行列(Queue)の数は? "); fscanf(STDIN, "%d", $n_q); $que = array(); for ($i1 = 0; $i1 < $n_q; $i1++) { printf("%d 番目の待ち行列(Queue)\n", $i1+1); printf(" 名前は? "); fscanf(STDIN, "%s", $name1); printf(" 入り口(0),または,窓口(n>0,窓口の数)から? "); fscanf(STDIN, "%d", $n); $in = array(); if ($n == 0) { printf(" 入り口の名前は? "); fscanf(STDIN, "%s", $name2); array_push($in, $name2); } else { for ($i2 = 0; $i2 < $n; $i2++) { printf(" %d 番目の窓口の名前は? ", $i2+1); fscanf(STDIN, "%s", $name3); array_push($in, $name3); } } printf(" 処理する窓口の数は? "); fscanf(STDIN, "%d", $m); $out = array(); for ($i2 = 0; $i2 < $m; $i2++) { printf(" %d 番目の窓口の名前は? ", $i2+1); fscanf(STDIN, "%s", $name4); array_push($out, $name4); } $que_e = new Queue($name1, $n, $in, $m, $out); array_push($que, $que_e); } // 窓口 printf("窓口(Entity)の数は? "); fscanf(STDIN, "%d", $n_e); $ent = array(); for ($i1 = 0; $i1 < $n_e; $i1++) { printf("%d 番目の窓口(Entity)\n", $i1+1); printf(" 名前は? "); fscanf(STDIN, "%s", $name1); printf(" サービス分布(=0:指数分布,=1:一定時間)? "); fscanf(STDIN, "%d", $s_t); if ($s_t == 0) { printf(" サービス時間の平均値は? "); fscanf(STDIN, "%lf", $m_s); } else { printf(" サービス時間は? "); fscanf(STDIN, "%lf", $m_s); } printf(" 待ち行列(入力)の名前は? "); fscanf(STDIN, "%s", $name2); printf(" 終了後,外部(0),または,待ち行列(1)? "); fscanf(STDIN, "%d", $sw); $name3 = ""; if ($sw > 0) { printf(" 待ち行列(出力)の名前は? "); fscanf(STDIN, "%s", $name3); } $ent_e = new Entity($name1, $s_t, $m_s, $name2, $sw, $name3); array_push($ent, $ent_e); } // 全体の制御を行うクラス printf("シミュレーション終了時間? "); fscanf(STDIN, "%lf", $end); $base = new Q_base($inl, $que, $ent, $end); // 全体の制御を行うクラス // 実行 $base->Control(); // 出力 $base->Output(); /* ------------入力例(簡単な場合)----------- 1 Inlet 0 5 Queue 1 Queue 0 Inlet 2 Entity1 Entity2 2 Entity1 0 4 Queue 0 Entity2 0 4 Queue 0 10000 ------------入力例(複雑な場合)----------- 2 Inlet1 0 5 Queue1 Inlet2 0 5 Queue2 3 Queue1 0 Inlet1 1 Entity1 Queue2 0 Inlet2 1 Entity2 Queue3 2 Entity1 Entity2 2 Entity3 Entity4 4 Entity1 0 4 Queue1 1 Queue3 Entity2 0 4 Queue2 1 Queue3 Entity3 0 3 Queue3 0 Entity4 0 3 Queue3 0 10000 */ ?>
############################# # 複雑な待ち行列問題 # coded by Y.Suganuma ############################# ##################### # クラスInletの定義 ##################### class Inlet ########################################################### # コンストラクタ # name1 : 入り口名 # a_t1 # = -n : 到着する客の人数を負の値にしたもの # # =0 : 指数分布 # # =1 : 一定時間間隔 # m_a: 到着時間間隔またはその平均 # que1 : 客の到着時刻リスト # name2 : 待ち行列名 ########################################################### def initialize(name1, a_t1, m_a, que1, name2) @_name = name1 # 入り口名 @_out = name2 # 待ち行列名 @_a_t = a_t1 # = -n : 到着する客の人数を負の値にしたもの # =0 : 指数分布 # =1 : 一定時間間隔 @_mean = m_a # 到着時間間隔またはその平均 @_que = que1 # 客の到着時刻リスト if @_a_t == 0 @_arrive_time = -@_mean * Math.log(rand(0)) # 客の到着時刻(負:客がない) elsif @_a_t == 1 @_arrive_time = 0 else @_arrive_time = @_que.delete_at(0) end @_out_n = 0 # 待ち行列番号(Q_baseのコンストラクタで設定) end attr_accessor("_out_n", "_out", "_name", "_arrive_time", "_a_t", "_mean", "_que") end ##################### # クラスQueueの定義 ##################### class Queue ############################################# # コンストラクタ # name1 : 待ち行列名 # n1 : =0 : 入り口から入る # >0 : 複数の窓口から入る(窓口数) # in1 : 入り口名,または,窓口名 # m1 : 処理する窓口数 # out1 : 窓口名 ############################################# def initialize(name1, n1, in1, m1, out1) @_name = name1 # 待ち行列名 @_n = n1 # =0 : 入り口から入る # >0 : 複数の窓口から入る(窓口数) @_inn = in1 # 入り口名,または,窓口名(入り口) @_m = m1 # 処理する窓口数 @_out = out1 # 窓口名(出口) @_nc = 0 # 待ち行列への到着客数カウンタ @_max_q_l = 0 # 最大待ち行列長 @_c_ql = 0.0 # 待ち行列長にその長さが継続した時間を乗じた値の累計 @_ql_t = 0.0 # 現在の待ち行列長になった時間 @_max_wt = 0.0 # 最大待ち時間 @_c_wt = 0.0 # 待ち時間の累計 @_in_n = Array.new() # 入り口番号,または,窓口番号(Q_baseのコンストラクタで設定) @_out_n = Array.new() # 窓口番号(Q_baseのコンストラクタで設定) @_que = Array.new() # 待ち行列 end attr_accessor("_name", "_n", "_m", "_inn", "_in_n", "_out", "_out_n", "_nc", "_c_ql", "_que", "_ql_t", "_max_q_l", "_c_wt", "_max_wt") end ###################### # クラスEntityの定義 ###################### class Entity ######################################## # コンストラクタ # name1 : 窓口名 # s_t1 # =0 : 指数分布 # # =1 : 一定時間 # m_s:サービス時間またはその平均 # name2 : 待ち行列(入力)の名前 # sw : =0 : システムの外に出る # =1 : 待ち行列に入る # name3 : 待ち行列(出力)の名前 ######################################## def initialize(name1, s_t1, m_s, name2, sw, name3) @_name = name1 # 窓口名 @_to = sw # =0 : システムの外に出る # =1 : 待ち行列に入る @_inn = name2 # 待ち行列(入力)の名前 if @_to > 0 @_out = name3 # 待ち行列(出力)の名前 end @_end_time = -1.0 # サービス終了時刻(負:何も処理していない) @_s_t = s_t1 # =0 : 指数分布 # =1 : 一定時間 @_mean = m_s # 平均サービス時間 @_service = 0 # サービス中の客番号(0のときは無し) @_busy_t = -1.0 # 窓口がふさがった時刻 @_c_busy = 0.0 # 窓口がふさがっている時間の累計 @_in_n = -1 # 待ち行列(入力)番号(Q_baseのコンストラクタで設定) @_out_n = -1 # 待ち行列(出力)番号(Q_baseのコンストラクタで設定) end attr_accessor("_service", "_name", "_inn", "_in_n", "_to", "_out", "_out_n", "_end_time", "_busy_t", "_s_t", "_c_busy", "_mean") end ######################## # クラスCustomerの定義 ######################## class Customer ##################### # コンストラクタ # s1,s2 : 状態 # t : 到着時刻 ##################### def initialize(s1, s2, t) @_time = t # 到着時刻 @_state1 = s1 # 客の状態1 # =0 : 待ち行列に入っている # =1 : サービスを受けている @_state2 = s2 # 客の状態2(待ち行列番号,または,窓口番号) end attr_accessor("_state1", "_state2", "_time") end ####################### # クラスQ_baseの定義 ####################### class Q_base ######################################## # コンストラクタ # v_i : Inletオブジェクトリスト # v_q : Queueオブジェクトリスト # v_e : Entityオブジェクトリスト # e : シミュレーション終了時刻 ######################################## def initialize(v_i, v_q, v_e, e) print("\n") # 接続関係のチェック @_inl = v_i # Inletオブジェクトリスト @_que = v_q # Queueオブジェクトリスト @_ent = v_e # Entityオブジェクトリスト # Inlet for i1 in 0 ... @_inl.length-1 for i2 in i1+1 ... @_inl.length if @_inl[i1]._name == @_inl[i2]._name print("***error 同じ名前の入り口があります " + @_inl[i1]._name + "\n") end end end for i1 in 0 ... @_inl.length k = -1 for i2 in 0 ... @_que.length if @_inl[i1]._out == @_que[i2]._name k = i2 break end end if k >= 0 @_inl[i1]._out_n = k else print("***error 入り口から入る待ち行列名が不適当です " + @_inl[i1]._out + "\n") end end # Queue for i1 in 0 ... @_que.length-1 for i2 in i1+1 ... @_que.length if @_que[i1]._name == @_que[i2]._name print("***error 同じ名前の待ち行列があります " + @_que[i1]._name + "\n") end end end for i1 in 0 ... @_que.length if @_que[i1]._n == 0 k = -1 for i2 in 0 ... @_inl.length if @_que[i1]._inn[0] == @_inl[i2]._name k = i2 break end end if k >= 0 @_que[i1]._in_n.push(k) else print("***error 待ち行列に入る入り口名が不適当です " + @_que[i1]._inn[0] + "\n") end else for i2 in 0 ... @_que[i1]._n k = -1 for i3 in 0 ... @_ent.length if @_que[i1]._inn[i2] == @_ent[i3]._name k = i3 break end end if k >= 0 @_que[i1]._in_n.push(k) else print("***error 待ち行列に入る窓口名が不適当です " + @_que[i1]._inn[i2] + "\n") end end end for i2 in 0 ... @_que[i1]._m k = -1 for i3 in 0 ... @_ent.length if @_que[i1]._out[i2] == @_ent[i3]._name k = i3 break end end if k >= 0 @_que[i1]._out_n.push(k) else print("***error 待ち行列を処理する窓口名が不適当です " + @_que[i1]._out[i2]) end end end # Entity for i1 in 0 ... @_ent.length-1 k = -1 for i2 in i1+1 ... @_ent.length if @_ent[i1]._name == @_ent[i2]._name print("***error 同じ名前の窓口があります " + @_ent[i1]._name + "\n") end end end for i1 in 0 ... @_ent.length k = -1 for i2 in 0 ... @_que.length if @_ent[i1]._inn == @_que[i2]._name k = i2 break end end if k >= 0 @_ent[i1]._in_n = k else print("***error 窓口に入る待ち行列名が不適当です " + @_ent[i1]._inn + "\n") end if @_ent[i1]._to > 0 k = -1 for i2 in 0 ... @_que.length if @_ent[i1]._out == @_que[i2]._name k = i2 break end end if k >= 0 @_ent[i1]._out_n = k else print("***error 窓口の出口にある待ち行列名が不適当です " + @_ent[i1]._out) end end end # 初期設定 @_p_time = 0.0 # 現在時刻 @_max_c = 0 # 最大系内客数 @_nc = 0 # システムへの到着客数カウンタ @_now_c_t = 0.0 # 現在の系内客数になった時間 @_c_now_c = 0.0 # 系内客数にその数が継続した時間を乗じた値の累計 @_c_sys = 0.0 # 滞在時間の累計 @_max_sys = 0.0 # 最大滞在時間 @_end = e # シミュレーション終了時間 @_cus = Hash.new() # 系内にいる客のリスト # 乱数の初期設定 srand() end ############# # 全体の制御 ############# def Control() sw = [0, 0] while sw[0] > -2 Next(sw) # 次の処理の選択 if sw[0] == -1 sw[0] = End_o_s() # シミュレーションの終了 else if sw[0] == 0 Arrive(sw[1]) # 客の到着処理 else Service(sw[1]) # サービスの終了 end end end end ############################################# # 次の処理の決定 # sw[0] : =-1 : シミュレーションの終了 # =0 : 客の到着処理 # =1 : 窓口のサービス終了 # [1] : 入り口番号 or 窓口番号 ############################################# def Next(sw) tm = @_end # 次の処理時刻 sw[0] = -1 # 次の客の到着時刻 for i1 in 0 ... @_inl.length x = @_inl[i1] if x._arrive_time >= 0.0 and x._arrive_time < tm sw[0] = 0 sw[1] = i1 tm = x._arrive_time end end # サービス終了時刻 for i1 in 0 ... @_ent.length x = @_ent[i1] if x._service > 0 and x._end_time <= tm sw[0] = 1 sw[1] = i1 tm = x._end_time end end if sw[0] < 0 @_end = @_p_time end end ################################## # 終了処理 # return : =-1 : 終了前処理 # =-2 : 実際の終了 ################################## def End_o_s() sw = -2 @_p_time = @_end # 現在時刻 for i1 in 0 ... @_ent.length x = @_ent[i1] if x._service > 0 # サービス中の客はいるか? if sw == -2 sw = -1 @_end = x._end_time # 窓口i1のサービス終了時刻 else if x._end_time > @_end @_end = x._end_time # 窓口i1のサービス終了時刻 end end end end return sw end ######################## # 客の到着処理 # kk : 入り口番号 ######################## def Arrive(kk) # 客数の増加と次の客の到着時刻の設定 @_nc += 1 # 到着客数カウンタ @_p_time = @_inl[kk]._arrive_time # 現在時刻 if @_inl[kk]._a_t == 0 # 次の客の到着時刻 @_inl[kk]._arrive_time = @_p_time - @_inl[kk]._mean * Math.log(rand(0)) elsif @_inl[kk]._a_t == 1 @_inl[kk]._arrive_time = @_p_time + @_inl[kk]._mean else if (@_inl[kk]._que).length < 1 @_inl[kk]._arrive_time = -1.0 else @_inl[kk]._arrive_time = @_inl[kk]._que.delete_at(0) end end if @_inl[kk]._arrive_time >= @_end @_inl[kk]._arrive_time = -1.0 end # 系内客数の処理 @_c_now_c += @_cus.length * (@_p_time - @_now_c_t) # 系内客数にその数が継続した時間を乗じた値の累計 @_now_c_t = @_p_time # 現在の系内客数になった時刻 if @_cus.length+1 > @_max_c @_max_c = @_cus.length + 1 # 最大系内客数 end # 空いている窓口を探す k1 = @_inl[kk]._out_n @_que[k1]._nc += 1 k = -1 for i1 in 0 ... @_que[k1]._m k2 = @_que[k1]._out_n[i1] # 処理窓口 if @_ent[k2]._service == 0 k = k2 break end end # 窓口に空きがない場合 if k < 0 ct_p = Customer.new(0, k1, @_p_time) @_cus[@_nc] = ct_p # 客の登録(系内客数) @_que[k1]._c_ql += (@_que[k1]._que).length * (@_p_time - @_que[k1]._ql_t) # 待ち行列長にその長さが継続した時間を乗じた値の累計 @_que[k1]._que.push(@_nc) # 客の登録(待ち行列) @_que[k1]._ql_t = @_p_time # 現在の待ち行列長になった時刻 if (@_que[k1]._que).length > @_que[k1]._max_q_l @_que[k1]._max_q_l = (@_que[k1]._que).length # 最大待ち行列長 end # すぐサービスをうけられる場合 else ct_p = Customer.new(1, k, @_p_time) @_cus[@_nc] = ct_p # 客の登録(系内客数) @_ent[k]._service = @_nc # サービスを受けている客番号 @_ent[k]._busy_t = @_p_time # 窓口がふさがった時刻 if @_ent[k]._s_t == 0 # 窓口のサービス終了時刻 @_ent[k]._end_time = @_p_time - @_ent[k]._mean * Math.log(rand(0)) else @_ent[k]._end_time = @_p_time + @_ent[k]._mean end end end ################################### # サービス終了時の処理 # kk : サービス終了窓口番号 ################################### def Service(kk) # 時間の設定 @_p_time = @_ent[kk]._end_time # 現在時刻 @_ent[kk]._end_time = -1.0 # サービス終了時間 # システムの外へ出る場合 if @_ent[kk]._to == 0 # 系内客数の処理 @_c_now_c += @_cus.length * (@_p_time - @_now_c_t) # 系内客数にその数が継続した時間を乗じた値の累計 @_now_c_t = @_p_time # 現在の系内客数になった時刻 # 滞在時間の処理 it = @_cus[@_ent[kk]._service] # サービス中の客 x1 = @_p_time - it._time @_c_sys += x1 # 滞在時間の累計 if x1 > @_max_sys @_max_sys = x1 # 最大滞在時間 end @_cus.delete(@_ent[kk]._service) # 客の削除(系内客数) # 他の窓口処理へ入る場合の処理 else k1 = @_ent[kk]._out_n @_que[k1]._nc += 1 sw = 1 k2 = 0 if (@_que[k1]._que).length == 0 for i1 in 0 ... @_que[k1]._m k2 = @_que[k1]._out_n[i1] # 窓口 if @_ent[k2]._service == 0 sw = 0 break end end end # 待ち行列がある場合 if sw > 0 @_que[k1]._c_ql += (@_que[k1]._que).length * (@_p_time - @_que[k1]._ql_t) # 待ち行列長にその長さが継続した時間を乗じた値の累計 @_que[k1]._que.push(@_ent[kk]._service) # 客の登録(待ち行列) @_que[k1]._ql_t = @_p_time # 現在の待ち行列長になった時刻 if (@_que[k1]._que).length > @_que[k1]._max_q_l @_que[k1]._max_q_l = (@_que[k1]._que).length # 最大待ち行列長 end it = @_cus[@_ent[kk]._service] it._state1 = 0 # 客の状態変更(待ち行列) it._state2 = @_ent[kk]._out_n # 客の状態変更(待ち行列番号) # すぐサービスをうけられる場合 else @_ent[k2]._service = @_ent[kk]._service # サービスを受けている客番号 @_ent[k2]._busy_t = @_p_time # 窓口がふさがった時刻 if @_ent[k2]._s_t == 0 # 窓口のサービス終了時刻 @_ent[k2]._end_time = @_p_time - @_ent[k2]._mean * Math.log(rand(0)) else @_ent[k2]._end_time = @_p_time + @_ent[k2]._mean end end end # 窓口使用時間の処理 @_ent[kk]._service = 0 # 窓口を空き状態にする @_ent[kk]._c_busy += (@_p_time - @_ent[kk]._busy_t) # 窓口がふさがっている時間の累計 # この窓口に対する待ち行列がある場合 k3 = @_ent[kk]._in_n if (@_que[k3]._que).length > 0 @_que[k3]._c_ql += (@_que[k3]._que).length * (@_p_time - @_que[k3]._ql_t) # 待ち行列長にその長さが継続した時間を乗じた値の累計 n = @_que[k3]._que.delete_at(0) # 待ち行列の先頭にいる客 @_que[k3]._ql_t = @_p_time # 現在の待ち行列長になった時刻 x1 = @_p_time - @_cus[n]._time @_que[k3]._c_wt += x1 # 待ち時間の累計 if x1 > @_que[k3]._max_wt @_que[k3]._max_wt = x1 # 最大待ち時間 end for i1 in 0 ... @_que[k3]._m k4 = @_que[k3]._out_n[i1] # 窓口 if @_ent[k4]._service == 0 @_ent[k4]._service = n # 窓口の客番号 @_ent[k4]._busy_t = @_p_time # 窓口がふさがった時刻 if @_ent[k4]._s_t == 0 # 窓口のサービス終了時刻 @_ent[k4]._end_time = @_p_time - @_ent[k4]._mean * Math.log(rand(0)) else @_ent[k4]._end_time = @_p_time + @_ent[k4]._mean end @_cus[n]._state1 = 1 # 客の状態変更(サービス中) @_cus[n]._state2 = k4 # 客の状態変更(窓口番号) break end end end end ########################## # 統計量の計算と最終出力 ########################## def Output() # System printf("全客数 %d", @_nc) printf(" 最大系内客数 %d 最大滞在時間 %.3f\n", @_max_c, @_max_sys) printf("平均系内客数 %.3f", (@_c_now_c / @_p_time)) printf(" 平均滞在時間 %.3f", (@_c_sys / @_nc)) printf(" 終了時間 %.3f\n", @_p_time) # Entity for i1 in 0 ... @_ent.length e = @_ent[i1] printf("Entity " + e._name) printf(" 稼働率 %.3f\n", (e._c_busy / @_p_time)) end # Queue for i1 in 0 ... @_que.length q = @_que[i1] printf("Queue " + q._name) printf(" 客数 %d", q._nc) printf(" 最大待ち行列長 %d", q._max_q_l) printf(" 最大待ち時間 %.3f\n", q._max_wt) printf(" 平均待ち行列長 %.3f", (q._c_ql / @_p_time)) printf(" 平均待ち時間 %.3f\n", (q._c_wt / q._nc)) end end end # 入り口 print("入り口(Inlet)の数は? ") n_i = Integer(gets()) inl = Array.new() for i1 in 0 ... n_i print(String(i1+1) + " 番目の入り口(Inlet)\n") print(" 名前は? ") name1 = gets().strip() qq = Array.new() print(" 到着分布(=0:指数分布,=1:一定時間間隔,<0:指定,客数の負値)? ") n = Integer(gets()) if n == 0 print(" 到着時間間隔の平均値は? ") m_a = Float(gets()) elsif n == 1 print(" 到着時間間隔は? ") m_a = Float(gets()) else for i2 in range(0, -n) print(" 到着時間は? ") x = Float(gets()) qq.push(x) end end print(" 並ぶ待ち行列の名前は? ") name2 = gets().strip() inl_e = Inlet.new(name1, n, m_a, qq, name2) inl.push(inl_e) end # 待ち行列 print("待ち行列(Queue)の数は? ") n_q = Integer(gets()) que = Array.new() for i1 in 0 ... n_q print(String(i1+1) + " 番目の待ち行列(Queue)\n") print(" 名前は? ") name1 = gets().strip() print(" 入り口(0),または,窓口(n>0,窓口の数)から? ") n = Integer(gets()) inn = Array.new() if n == 0 print(" 入り口の名前は? ") name2 = gets().strip() inn.push(name2) else for i2 in 0 ... n print(" " + String(i2+1) + " 番目の窓口の名前は? ") name3 = gets().strip() inn.push(name3) end end print(" 処理する窓口の数は? ") m = Integer(gets()) out = Array.new() for i2 in 0 ... m print(" " +String(i2+1) + " 番目の窓口の名前は? ") name4 = gets().strip() out.push(name4) end que_e = Queue.new(name1, n, inn, m, out) que.push(que_e) end # 窓口 print("窓口(Entity)の数は? ") n_e = Integer(gets()) ent = Array.new() for i1 in 0 ... n_e print(String(i1+1) + " 番目の窓口(Entity)\n") print(" 名前は? ") name1 = gets().strip() print(" サービス分布(=0:指数分布,=1:一定時間)? ") s_t = Integer(gets()) if s_t == 0 print(" サービス時間の平均値は? ") m_s = Float(gets()) else print(" サービス時間は? ") m_s = Float(gets()) end print(" 待ち行列(入力)の名前は? ") name2 = gets().strip() print(" 終了後,外部(0),または,待ち行列(1)? ") sw = Integer(gets()) name3 = "" if sw > 0 print(" 待ち行列(出力)の名前は? ") name3 = gets().strip() end ent_e = Entity.new(name1, s_t, m_s, name2, sw, name3) ent.push(ent_e) end # 全体の制御を行うクラス print("シミュレーション終了時間? ") stp = Float(gets()) base = Q_base.new(inl, que, ent, stp) # 全体の制御を行うクラス # 実行 base.Control() # 出力 base.Output() =begin ------------入力例(簡単な場合)----------- 1 Inlet 0 5 Queue 1 Queue 0 Inlet 2 Entity1 Entity2 2 Entity1 0 4 Queue 0 Entity2 0 4 Queue 0 10000 ------------入力例(複雑な場合)----------- 2 Inlet1 0 5 Queue1 Inlet2 0 5 Queue2 3 Queue1 0 Inlet1 1 Entity1 Queue2 0 Inlet2 1 Entity2 Queue3 2 Entity1 Entity2 2 Entity3 Entity4 4 Entity1 0 4 Queue1 1 Queue3 Entity2 0 4 Queue2 1 Queue3 Entity3 0 3 Queue3 0 Entity4 0 3 Queue3 0 10000 =end
# -*- coding: UTF-8 -*- import numpy as np import sys from math import * from random import * ##################### # クラスInletの定義 ##################### class Inlet : ########################################################### # コンストラクタ # name1 : 入り口名 # a_t1 # = -n : 到着する客の人数を負の値にしたもの # # =0 : 指数分布 # # =1 : 一定時間間隔 # m_a: 到着時間間隔またはその平均 # que1 : 客の到着時刻リスト # name2 : 待ち行列名 ########################################################### def __init__(self, name1, a_t1, m_a, que1, name2) : self.name = name1 # 入り口名 self.out = name2 # 待ち行列名 self.a_t = a_t1 # = -n : 到着する客の人数を負の値にしたもの # =0 : 指数分布 # =1 : 一定時間間隔 self.mean = m_a # 到着時間間隔またはその平均 self.que = que1 # 客の到着時刻リスト if self.a_t == 0 : self.arrive_time = expovariate(1.0 / self.mean) # 客の到着時刻(負:客がない) elif self.a_t == 1 : self.arrive_time = 0 else : self.arrive_time = self.que.pop(0) self.out_n = 0 # 待ち行列番号(Q_baseのコンストラクタで設定) ##################### # クラスQueueの定義 ##################### class Queue : ############################################# # コンストラクタ # name1 : 待ち行列名 # n1 : =0 : 入り口から入る # >0 : 複数の窓口から入る(窓口数) # in1 : 入り口名,または,窓口名 # m1 : 処理する窓口数 # out1 : 窓口名 ############################################# def __init__(self, name1, n1, in1, m1, out1) : self.name = name1 # 待ち行列名 self.n = n1 # =0 : 入り口から入る # >0 : 複数の窓口から入る(窓口数) self.inn = in1 # 入り口名,または,窓口名(入り口) self.m = m1 # 処理する窓口数 self.out = out1 # 窓口名(出口) self.nc = 0 # 待ち行列への到着客数カウンタ self.max_q_l = 0 # 最大待ち行列長 self.c_ql = 0.0 # 待ち行列長にその長さが継続した時間を乗じた値の累計 self.ql_t = 0.0 # 現在の待ち行列長になった時間 self.max_wt = 0.0 # 最大待ち時間 self.c_wt = 0.0 # 待ち時間の累計 self.in_n = [] # 入り口番号,または,窓口番号(Q_baseのコンストラクタで設定) self.out_n = [] # 窓口番号(Q_baseのコンストラクタで設定) self.que = [] # 待ち行列 ###################### # クラスEntityの定義 ###################### class Entity : ######################################## # コンストラクタ # name1 : 窓口名 # s_t1 # =0 : 指数分布 # # =1 : 一定時間 # m_s:サービス時間またはその平均 # name2 : 待ち行列(入力)の名前 # sw : =0 : システムの外に出る # =1 : 待ち行列に入る # name3 : 待ち行列(出力)の名前 ######################################## def __init__(self, name1, s_t1, m_s, name2, sw, name3) : self.name = name1 # 窓口名 self.to = sw # =0 : システムの外に出る # =1 : 待ち行列に入る self.inn = name2 # 待ち行列(入力)の名前 if self.to > 0 : self.out = name3 # 待ち行列(出力)の名前 self.end_time = -1.0 # サービス終了時刻(負:何も処理していない) self.s_t = s_t1 # =0 : 指数分布 # =1 : 一定時間 self.mean = m_s # 平均サービス時間 self.service = 0 # サービス中の客番号(0のときは無し) self.busy_t = -1.0 # 窓口がふさがった時刻 self.c_busy = 0.0 # 窓口がふさがっている時間の累計 self.in_n = -1 # 待ち行列(入力)番号(Q_baseのコンストラクタで設定) self.out_n = -1 # 待ち行列(出力)番号(Q_baseのコンストラクタで設定) ######################## # クラスCustomerの定義 ######################## class Customer : ##################### # コンストラクタ # s1,s2 : 状態 # t : 到着時刻 ##################### def __init__(self, s1, s2, t) : self.time = t # 到着時刻 self.state1 = s1 # 客の状態1 # =0 : 待ち行列に入っている # =1 : サービスを受けている self.state2 = s2 # 客の状態2(待ち行列番号,または,窓口番号) ####################### # クラスQ_baseの定義 ####################### class Q_base : ######################################## # コンストラクタ # v_i : Inletオブジェクトリスト # v_q : Queueオブジェクトリスト # v_e : Entityオブジェクトリスト # e : シミュレーション終了時刻 ######################################## def __init__(self, v_i, v_q, v_e, e) : print("") # 接続関係のチェック self.inl = v_i # Inletオブジェクトリスト self.que = v_q # Queueオブジェクトリスト self.ent = v_e # Entityオブジェクトリスト # Inlet for i1 in range(0, len(self.inl)-1) : for i2 in range(i1+1, len(self.inl)) : if self.inl[i1].name == self.inl[i2].name : print("***error 同じ名前の入り口があります " + self.inl[i1].name) for i1 in range(0, len(self.inl)) : k = -1 for i2 in range(0, len(self.que)) : if self.inl[i1].out == self.que[i2].name : k = i2 break if k >= 0 : self.inl[i1].out_n = k else : print("***error 入り口から入る待ち行列名が不適当です " + self.inl[i1].out) # Queue for i1 in range(0, len(self.que)-1) : for i2 in range(i1+1, len(self.que)) : if self.que[i1].name == self.que[i2].name : print("***error 同じ名前の待ち行列があります " + self.que[i1].name) for i1 in range(0, len(self.que)) : if self.que[i1].n == 0 : k = -1 for i2 in range(0, len(self.inl)) : if self.que[i1].inn[0] == self.inl[i2].name : k = i2 break if k >= 0 : self.que[i1].in_n.append(k) else : print("***error 待ち行列に入る入り口名が不適当です " + self.que[i1].inn[0]) else : for i2 in range(0, self.que[i1].n) : k = -1 for i3 in range(0, len(self.ent)) : if self.que[i1].inn[i2] == self.ent[i3].name : k = i3 break if k >= 0 : self.que[i1].in_n.append(k) else : print("***error 待ち行列に入る窓口名が不適当です " + self.que[i1].inn[i2]) for i2 in range(0, self.que[i1].m) : k = -1 for i3 in range(0, len(self.ent)) : if self.que[i1].out[i2] == self.ent[i3].name : k = i3 break if k >= 0 : self.que[i1].out_n.append(k) else : print("***error 待ち行列を処理する窓口名が不適当です " + self.que[i1].out[i2]) # Entity for i1 in range(0, len(self.ent)-1) : k = -1 for i2 in range(i1+1, len(self.ent)) : if self.ent[i1].name == self.ent[i2].name : print("***error 同じ名前の窓口があります " + self.ent[i1].name) for i1 in range(0, len(self.ent)) : k = -1 for i2 in range(0, len(self.que)) : if self.ent[i1].inn == self.que[i2].name : k = i2 break if k >= 0 : self.ent[i1].in_n = k else : print("***error 窓口に入る待ち行列名が不適当です " + self.ent[i1].inn) if self.ent[i1].to > 0 : k = -1 for i2 in range(0, len(self.que)) : if self.ent[i1].out == self.que[i2].name : k = i2 break if k >= 0 : self.ent[i1].out_n = k else : print("***error 窓口の出口にある待ち行列名が不適当です " + self.ent[i1].out) # 初期設定 self.p_time = 0.0 # 現在時刻 self.max_c = 0 # 最大系内客数 self.nc = 0 # システムへの到着客数カウンタ self.now_c_t = 0.0 # 現在の系内客数になった時間 self.c_now_c = 0.0 # 系内客数にその数が継続した時間を乗じた値の累計 self.c_sys = 0.0 # 滞在時間の累計 self.max_sys = 0.0 # 最大滞在時間 self.end = e # シミュレーション終了時間 self.cus = dict() # 系内にいる客のリスト # 乱数の初期設定 seed() ############# # 全体の制御 ############# def Control(self) : sw = np.zeros(2, np.int) while sw[0] > -2 : self.Next(sw) # 次の処理の選択 if sw[0] == -1 : sw[0] = self.End_o_s() # シミュレーションの終了 else : if sw[0] == 0 : self.Arrive(sw[1]) # 客の到着処理 else : self.Service(sw[1]) # サービスの終了 ############################################# # 次の処理の決定 # sw[0] : =-1 : シミュレーションの終了 # =0 : 客の到着処理 # =1 : 窓口のサービス終了 # [1] : 入り口番号 or 窓口番号 ############################################# def Next(self, sw) : tm = self.end # 次の処理時刻 sw[0] = -1 # 次の客の到着時刻 for i1 in range(0, len(self.inl)) : x = self.inl[i1] if x.arrive_time >= 0.0 and x.arrive_time < tm : sw[0] = 0 sw[1] = i1 tm = x.arrive_time # サービス終了時刻 for i1 in range(0, len(self.ent)) : x = self.ent[i1] if x.service > 0 and x.end_time <= tm : sw[0] = 1 sw[1] = i1 tm = x.end_time if sw[0] < 0 : self.end = self.p_time ################################## # 終了処理 # return : =-1 : 終了前処理 # =-2 : 実際の終了 ################################## def End_o_s(self) : sw = -2 self.p_time = self.end # 現在時刻 for i1 in range(0, len(self.ent)) : x = self.ent[i1] if x.service > 0 : # サービス中の客はいるか? if sw == -2 : sw = -1 self.end = x.end_time # 窓口i1のサービス終了時刻 else : if x.end_time > self.end : self.end = x.end_time # 窓口i1のサービス終了時刻 return sw ######################## # 客の到着処理 # kk : 入り口番号 ######################## def Arrive(self, kk) : # 客数の増加と次の客の到着時刻の設定 self.nc += 1 # 到着客数カウンタ self.p_time = self.inl[kk].arrive_time # 現在時刻 if self.inl[kk].a_t == 0 : # 次の客の到着時刻 self.inl[kk].arrive_time = self.p_time + expovariate(1.0 / self.inl[kk].mean) elif self.inl[kk].a_t == 1 : self.inl[kk].arrive_time = self.p_time + self.inl[kk].mean else : if len(self.inl[kk].que) < 1 : self.inl[kk].arrive_time = -1.0 else : self.inl[kk].arrive_time = self.inl[kk].que.pop(0) if self.inl[kk].arrive_time >= self.end : self.inl[kk].arrive_time = -1.0 # 系内客数の処理 self.c_now_c += len(self.cus) * (self.p_time - self.now_c_t) # 系内客数にその数が継続した時間を乗じた値の累計 self.now_c_t = self.p_time # 現在の系内客数になった時刻 if len(self.cus)+1 > self.max_c : self.max_c = len(self.cus) + 1 # 最大系内客数 # 空いている窓口を探す k1 = self.inl[kk].out_n self.que[k1].nc += 1 k = -1 for i1 in range(0, self.que[k1].m) : k2 = self.que[k1].out_n[i1] # 処理窓口 if self.ent[k2].service == 0 : k = k2 break # 窓口に空きがない場合 if k < 0 : ct_p = Customer(0, k1, self.p_time) self.cus[self.nc] = ct_p # 客の登録(系内客数) self.que[k1].c_ql += len(self.que[k1].que) * (self.p_time - self.que[k1].ql_t) # 待ち行列長にその長さが継続した時間を乗じた値の累計 self.que[k1].que.append(self.nc) # 客の登録(待ち行列) self.que[k1].ql_t = self.p_time # 現在の待ち行列長になった時刻 if len(self.que[k1].que) > self.que[k1].max_q_l : self.que[k1].max_q_l = len(self.que[k1].que) # 最大待ち行列長 # すぐサービスをうけられる場合 else : ct_p = Customer(1, k, self.p_time) self.cus[self.nc] = ct_p # 客の登録(系内客数) self.ent[k].service = self.nc # サービスを受けている客番号 self.ent[k].busy_t = self.p_time # 窓口がふさがった時刻 if self.ent[k].s_t == 0 : # 窓口のサービス終了時刻 self.ent[k].end_time = self.p_time + expovariate(1.0 / self.ent[k].mean) else : self.ent[k].end_time = self.p_time + self.ent[k].mean ################################### # サービス終了時の処理 # kk : サービス終了窓口番号 ################################### def Service(self, kk) : # 時間の設定 self.p_time = self.ent[kk].end_time # 現在時刻 self.ent[kk].end_time = -1.0 # サービス終了時間 # システムの外へ出る場合 if self.ent[kk].to == 0 : # 系内客数の処理 self.c_now_c += len(self.cus) * (self.p_time - self.now_c_t) # 系内客数にその数が継続した時間を乗じた値の累計 self.now_c_t = self.p_time # 現在の系内客数になった時刻 # 滞在時間の処理 it = self.cus[self.ent[kk].service] # サービス中の客 x1 = self.p_time - it.time self.c_sys += x1 # 滞在時間の累計 if x1 > self.max_sys : self.max_sys = x1 # 最大滞在時間 self.cus.pop(self.ent[kk].service) # 客の削除(系内客数) # 他の窓口処理へ入る場合の処理 else : k1 = self.ent[kk].out_n self.que[k1].nc += 1 sw = 1 k2 = 0 if len(self.que[k1].que) == 0 : for i1 in range(0, self.que[k1].m) : k2 = self.que[k1].out_n[i1] # 窓口 if self.ent[k2].service == 0 : sw = 0 break # 待ち行列がある場合 if sw > 0 : self.que[k1].c_ql += len(self.que[k1].que) * (self.p_time - self.que[k1].ql_t) # 待ち行列長にその長さが継続した時間を乗じた値の累計 self.que[k1].que.append(self.ent[kk].service) # 客の登録(待ち行列) self.que[k1].ql_t = self.p_time # 現在の待ち行列長になった時刻 if len(self.que[k1].que) > self.que[k1].max_q_l : self.que[k1].max_q_l = len(self.que[k1].que) # 最大待ち行列長 it = self.cus[self.ent[kk].service] it.state1 = 0 # 客の状態変更(待ち行列) it.state2 = self.ent[kk].out_n # 客の状態変更(待ち行列番号) # すぐサービスをうけられる場合 else : self.ent[k2].service = self.ent[kk].service # サービスを受けている客番号 self.ent[k2].busy_t = self.p_time # 窓口がふさがった時刻 if self.ent[k2].s_t == 0 : # 窓口のサービス終了時刻 self.ent[k2].end_time = self.p_time + expovariate(1.0 / self.ent[k2].mean) else : self.ent[k2].end_time = self.p_time + self.ent[k2].mean # 窓口使用時間の処理 self.ent[kk].service = 0 # 窓口を空き状態にする self.ent[kk].c_busy += (self.p_time - self.ent[kk].busy_t) # 窓口がふさがっている時間の累計 # この窓口に対する待ち行列がある場合 k3 = self.ent[kk].in_n if len(self.que[k3].que) > 0 : self.que[k3].c_ql += len(self.que[k3].que) * (self.p_time - self.que[k3].ql_t) # 待ち行列長にその長さが継続した時間を乗じた値の累計 n = self.que[k3].que.pop(0) # 待ち行列の先頭にいる客 self.que[k3].ql_t = self.p_time # 現在の待ち行列長になった時刻 x1 = self.p_time - self.cus[n].time self.que[k3].c_wt += x1 # 待ち時間の累計 if x1 > self.que[k3].max_wt : self.que[k3].max_wt = x1 # 最大待ち時間 for i1 in range(0, self.que[k3].m) : k4 = self.que[k3].out_n[i1] # 窓口 if self.ent[k4].service == 0 : self.ent[k4].service = n # 窓口の客番号 self.ent[k4].busy_t = self.p_time # 窓口がふさがった時刻 if self.ent[k4].s_t == 0 : # 窓口のサービス終了時刻 self.ent[k4].end_time = self.p_time + expovariate(1.0 / self.ent[k4].mean) else : self.ent[k4].end_time = self.p_time + self.ent[k4].mean self.cus[n].state1 = 1 # 客の状態変更(サービス中) self.cus[n].state2 = k4 # 客の状態変更(窓口番号) break ########################## # 統計量の計算と最終出力 ########################## def Output(self) : # System print("全客数 {0:d}".format(self.nc), end="") print(" 最大系内客数 {0:d} 最大滞在時間 {1:.3f}".format(self.max_c, self.max_sys)) print("平均系内客数 {0:.3f}".format(self.c_now_c / self.p_time), end="") print(" 平均滞在時間 {0:.3f}".format(self.c_sys / self.nc), end="") print(" 終了時間 {0:.3f}".format(self.p_time)) # Entity for i1 in range(0, len(self.ent)) : e = self.ent[i1] print("Entity " + e.name, end="") print(" 稼働率 {0:.3f}".format(e.c_busy / self.p_time)) # Queue for i1 in range(0, len(self.que)) : q = self.que[i1] print("Queue " + q.name, end="") print(" 客数 {0:d}".format(q.nc), end="") print(" 最大待ち行列長 {0:d}".format(q.max_q_l), end="") print(" 最大待ち時間 {0:.3f}".format(q.max_wt)) print(" 平均待ち行列長 {0:.3f}".format(q.c_ql / self.p_time), end="") print(" 平均待ち時間 {0:.3f}".format(q.c_wt / q.nc)) ############################# # 複雑な待ち行列問題 # coded by Y.Suganuma ############################# # 入り口 n_i = int(input("入り口(Inlet)の数は? ")) inl = [] for i1 in range(0, n_i) : print(str(i1+1) + " 番目の入り口(Inlet)") name1 = input(" 名前は? ").strip(" \n") qq = [] n = int(input(" 到着分布(=0:指数分布,=1:一定時間間隔,<0:指定,客数の負値)? ")) if n == 0 : m_a = float(input(" 到着時間間隔の平均値は? ")) elif n == 1 : m_a = float(input(" 到着時間間隔は? ")) else : for i2 in range(0, -n) : x = float(input(" 到着時間は? ")) qq.append(x) name2 = input(" 並ぶ待ち行列の名前は? ").strip(" \n") inl_e = Inlet(name1, n, m_a, qq, name2) inl.append(inl_e) # 待ち行列 n_q = int(input("待ち行列(Queue)の数は? ")) que = [] for i1 in range(0, n_q) : print(str(i1+1) + " 番目の待ち行列(Queue)") name1 = input(" 名前は? ").strip(" \n") n = int(input(" 入り口(0),または,窓口(n>0,窓口の数)から? ")) inn = [] if n == 0 : name2 = input(" 入り口の名前は? ").strip(" \n") inn.append(name2) else : for i2 in range(0, n) : name3 = input(" " + str(i2+1) + " 番目の窓口の名前は? ").strip(" \n") inn.append(name3) m = int(input(" 処理する窓口の数は? ")) out = [] for i2 in range(0, m) : name4 = input(" " +str(i2+1) + " 番目の窓口の名前は? ").strip(" \n") out.append(name4) que_e = Queue(name1, n, inn, m, out) que.append(que_e) # 窓口 n_e = int(input("窓口(Entity)の数は? ")) ent = [] for i1 in range(0, n_e) : print(str(i1+1) + " 番目の窓口(Entity)") name1 = input(" 名前は? ").strip(" \n") s_t = int(input(" サービス分布(=0:指数分布,=1:一定時間)? ")) if s_t == 0 : m_s = float(input(" サービス時間の平均値は? ")) else : m_s = float(input(" サービス時間は? ")) name2 = input(" 待ち行列(入力)の名前は? ").strip(" \n") sw = int(input(" 終了後,外部(0),または,待ち行列(1)? ")) name3 = "" if sw > 0 : name3 = input(" 待ち行列(出力)の名前は? ").strip(" \n") ent_e = Entity(name1, s_t, m_s, name2, sw, name3) ent.append(ent_e) # 全体の制御を行うクラス end = float(input("シミュレーション終了時間? ")) base = Q_base(inl, que, ent, end) # 全体の制御を行うクラス # 実行 base.Control() # 出力 base.Output() """ ------------入力例(簡単な場合)----------- 1 Inlet 0 5 Queue 1 Queue 0 Inlet 2 Entity1 Entity2 2 Entity1 0 4 Queue 0 Entity2 0 4 Queue 0 10000 ------------入力例(複雑な場合)----------- 2 Inlet1 0 5 Queue1 Inlet2 0 5 Queue2 3 Queue1 0 Inlet1 1 Entity1 Queue2 0 Inlet2 1 Entity2 Queue3 2 Entity1 Entity2 2 Entity3 Entity4 4 Entity1 0 4 Queue1 1 Queue3 Entity2 0 4 Queue2 1 Queue3 Entity3 0 3 Queue3 0 Entity4 0 3 Queue3 0 10000 """
/******************************/ /* 複雑な待ち行列問題 */ /* coded by Y.Suganuma */ /******************************/ using System; using System.Collections.Generic; class Program { /****************/ /* main program */ /****************/ static void Main() { // 入り口 Console.Write("入り口(Inlet)の数は? "); int n_i = int.Parse(Console.ReadLine()); List <Inlet> inl = new List <Inlet>(); for (int i1 = 0; i1 < n_i; i1++) { double m_a = 0.0; Console.WriteLine((i1+1) + " 番目の入り口(Inlet)"); Console.Write(" 名前は? "); String name1 = Console.ReadLine().Trim(); Queue <double> que_i = new Queue <double>(); Console.Write(" 到着分布(=0:指数分布,1=一定時間間隔,<0:指定:客数の負値))? "); int n = int.Parse(Console.ReadLine()); if (n == 0) { Console.Write(" 到着時間間隔の平均値は? "); m_a = double.Parse(Console.ReadLine()); } else if (n == 1) { Console.Write(" 到着時間間隔は? "); m_a = double.Parse(Console.ReadLine()); } else { double x; for (int i2 = 0; i2 < -n; i2++) { Console.Write(" 到着時間は? "); x = double.Parse(Console.ReadLine()); que_i.Enqueue(x); } } Console.Write(" 並ぶ待ち行列の名前は? "); String name2 = Console.ReadLine().Trim(); Inlet inl_e = new Inlet(name1, n, m_a, que_i, name2); inl.Add(inl_e); } // 待ち行列 Console.Write("待ち行列(Queue)の数は? "); int n_q = int.Parse(Console.ReadLine()); List <Wait_c> wt = new List <Wait_c>(); for (int i1 = 0; i1 < n_q; i1++) { Console.WriteLine((i1+1) + " 番目の待ち行列(Queue)"); Console.Write(" 名前は? "); String name1 = Console.ReadLine().Trim(); Console.Write(" 入り口(0),または,窓口(n>0,窓口の数)から? "); int n = int.Parse(Console.ReadLine()); List <String> in1 = new List <String>(); if (n == 0) { Console.Write(" 入り口の名前は? "); String name2 = Console.ReadLine().Trim(); in1.Add(name2); } else { for (int i2 = 0; i2 < n; i2++) { Console.Write(" " + (i2+1) + " 番目の窓口の名前は? "); String name3 = Console.ReadLine().Trim(); in1.Add(name3); } } Console.Write(" 処理する窓口の数は? "); int m = int.Parse(Console.ReadLine()); List <String> out1 = new List <String>(); for (int i2 = 0; i2 < m; i2++) { Console.Write(" " + (i2+1) + " 番目の窓口の名前は? "); String name4 = Console.ReadLine().Trim(); out1.Add(name4); } Wait_c wt_e = new Wait_c(name1, n, in1, m, out1); wt.Add(wt_e); } // 窓口 Console.Write("窓口(Entity)の数は? "); int n_e = int.Parse(Console.ReadLine()); List <Entity> ent = new List <Entity>(); for (int i1 = 0; i1 < n_e; i1++) { double m_s = 0.0; Console.WriteLine((i1+1) + " 番目の窓口(Entity)"); Console.Write(" 名前は? "); String name1 = Console.ReadLine().Trim(); Console.Write(" サービス分布(=0:指数分布,1=一定時間)? "); int s_t = int.Parse(Console.ReadLine()); if (s_t == 0) { Console.Write(" サービス時間の平均値は? "); m_s = double.Parse(Console.ReadLine()); } else { Console.Write(" サービス時間は? "); m_s = double.Parse(Console.ReadLine()); } Console.Write(" 待ち行列(入力)の名前は? "); String name2 = Console.ReadLine().Trim(); Console.Write(" 終了後,外部(0),または,待ち行列(1)? "); int sw = int.Parse(Console.ReadLine()); String name3 = ""; if (sw > 0) { Console.Write(" 待ち行列(出力)の名前は? "); name3 = Console.ReadLine().Trim(); } Entity ent_e = new Entity(name1, s_t, m_s, name2, sw, name3); ent.Add(ent_e); } // 全体の制御を行うクラス Console.Write("シミュレーション終了時間? "); double end = double.Parse(Console.ReadLine()); Q_base base1 = new Q_base(inl, wt, ent, end); // 全体の制御を行うクラス // 実行 base1.Control(); // 出力 base1.Output(); } } /*********************/ /* クラスInletの定義 */ /*********************/ class Inlet { public String name; // 入り口名 public String out1; // 待ち行列名 public int out_n; // 待ち行列番号 public double arrive_time; // 客の到着時刻(負:客がない) public int a_t; // = -n : 到着する客の人数を負の値にしたもの // =0 : 指数分布 // =1 : 一定時間間隔 public double mean; // 到着時間間隔またはその平均 public Queue <double> que_i; // 客の到着時刻リスト /*************************************************************/ /* コンストラクタ */ /* name1 : 入り口名 */ /* a_t1; // = -n : 到着する客の人数を負の値にしたもの */ /* // =0 : 指数分布 */ /* // =1 : 一定時間間隔 */ /* m_a: 到着時間間隔またはその平均 */ /* que1 : 客の到着時刻リスト */ /* name2 : 待ち行列名 */ /*************************************************************/ public Inlet (String name1, int a_t1, double m_a, Queue<double> que1, String name2) { name = name1; out1 = name2; mean = m_a; a_t = a_t1; que_i = que1; if (a_t == 1) arrive_time = 0; else if (a_t < 0) arrive_time = que_i.Dequeue(); } } /**********************/ /* クラスWait_cの定義 */ /**********************/ class Wait_c { public String name; // 待ち行列名 public int nc; // 待ち行列への到着客数カウンタ public int max_q_l; // 最大待ち行列長 public double c_ql; // 待ち行列長にその長さが継続した時間を乗じた値の累計 public double ql_t; // 現在の待ち行列長になった時間 public double max_wt; // 最大待ち時間 public double c_wt; // 待ち時間の累計 public int n; // =0 : 入り口から入る // >0 : 複数の窓口から入る(窓口数) public List <String> in1; // 入り口名,または,窓口名 public List <int> in_n; // 入り口番号,または,窓口番号 public int m; // 処理する窓口数 public List <String> out1; // 窓口名 public List <int> out_n; // 窓口番号 public Queue <int> que; // 待ち行列 /*********************************************/ /* コンストラクタ */ /* name1 : 待ち行列名 */ /* n1 : =0 : 入り口から入る */ /* >0 : 複数の窓口から入る(窓口数) */ /* in0 : 入り口名,または,窓口名 */ /* m1 : 処理する窓口数 */ /* out0 : 窓口名 */ /*********************************************/ public Wait_c(String name1, int n1, List<String> in0, int m1, List<String> out0) { name = name1; in1 = in0; n = n1; in_n = new List <int>(); out1 = out0; m = m1; out_n = new List <int>(); que = new Queue <int>(); nc = 0; max_q_l = 0; c_ql = 0.0; ql_t = 0.0; max_wt = 0.0; c_wt = 0.0; } } /**********************/ /* クラスEntityの定義 */ /**********************/ class Entity { public String name; // Entity名 public double busy_t; // 窓口がふさがった時間 public double c_busy; // 窓口がふさがっている時間の累計 public double end_time; // サービス終了時間(負:何も処理していない) public int s_t; // =0 : 指数分布 // =1 : 一定時間 public double mean; // 平均サービス時間 public int service; // サービス中の客番号(0のときはなし) public String in1; // 待ち行列(入力)の名前 public int in_n; // 待ち行列(入力)番号 public int to; // =0 : システムの外に出る // =1 : 待ち行列に入る public String out1; // 待ち行列(出力)の名前 public int out_n; // 待ち行列(出力)番号 /****************************************/ /* コンストラクタ */ /* name1 : 窓口名 */ /* s_t1; // =0 : 指数分布 */ /* // =1 : 一定時間 */ /* m_s:サービス時間またはその平均 */ /* name2 : 待ち行列(入力)の名前 */ /* sw : =0 : システムの外に出る */ /* =1 : 待ち行列に入る */ /* name3 : 待ち行列(出力)の名前 */ /****************************************/ public Entity(String name1, int s_t1, double m_s, String name2, int sw, String name3) { name = name1; to = sw; in1 = name2; if (to > 0) out1 = name3; end_time = -1.0; s_t = s_t1; mean = m_s; service = 0; busy_t = -1.0; c_busy = 0.0; } } /************************/ /* クラスCustomerの定義 */ /************************/ class Customer { public double time; // 到着時刻 public int state1; // 客の状態1 // =0 : 待ち行列に入っている // =1 : サービスを受けている public int state2; // 客の状態2(待ち行列番号,または,窓口番号) /*********************/ /* コンストラクタ */ /* s1,s2 : 状態 */ /* t : 到着時刻 */ /*********************/ public Customer(int s1, int s2, double t) { time = t; state1 = s1; state2 = s2; } } /**********************/ /* クラスQ_baseの定義 */ /**********************/ class Q_base { private double p_time; // 現在時刻 private int max_c; // 最大系内客数 private int nc; // 到着客数カウンタ private double now_c_t; // 現在の系内客数になった時間 private double c_now_c; // 系内客数にその数が継続した時間を乗じた値の累計 private double c_sys; // 滞在時間の累計 private double max_sys; // 最大滞在時間 private double end; // シミュレーション終了時間 private Random rn; // 乱数 private Dictionary <int, Customer> cus; // 系内にいる客のリスト private List <Inlet> inl; // Inletオブジェクトリスト private List <Wait_c> wt; // Wait_cオブジェクトリスト private List <Entity> ent; // Entityオブジェクトリスト /***************************************/ /* コンストラクタ */ /* v_i : Inletオブジェクトリスト */ /* v_q : Wait_cオブジェクトリスト */ /* v_e : Entityオブジェクトリスト */ /* e : シミュレーション終了時刻 */ /***************************************/ public Q_base(List<Inlet> v_i, List<Wait_c> v_q, List<Entity> v_e, double e) { // 接続関係のチェック Console.WriteLine(); // Inlet inl = v_i; wt = v_q; ent = v_e; for (int i1 = 0; i1 < inl.Count-1; i1++) { for (int i2 = i1+1; i2 < inl.Count; i2++) { if (inl[i1].name == inl[i2].name) { Console.WriteLine("***error 同じ名前の入り口があります " + inl[i1].name); Environment.Exit(1); } } } int k; for (int i1 = 0; i1 < inl.Count; i1++) { k = -1; for (int i2 = 0; i2 < wt.Count; i2++) { if (inl[i1].out1 == wt[i2].name) { k = i2; break; } } if (k >= 0) inl[i1].out_n = k; else { Console.WriteLine("***error 入り口から入る待ち行列名が不適当です " + inl[i1].out1); Environment.Exit(1); } } // Queue for (int i1 = 0; i1 < wt.Count-1; i1++) { for (int i2 = i1+1; i2 < wt.Count; i2++) { if (wt[i1].name == wt[i2].name) { Console.WriteLine("***error 同じ名前の待ち行列があります " + wt[i1].name); Environment.Exit(1); } } } for (int i1 = 0; i1 < wt.Count; i1++) { if (wt[i1].n == 0) { k = -1; for (int i2 = 0; i2 < inl.Count; i2++) { if (wt[i1].in1[0] == inl[i2].name) { k = i2; break; } } if (k >= 0) wt[i1].in_n.Add(k); else { Console.WriteLine("***error 待ち行列に入る入り口名が不適当です " + wt[i1].in1[0]); Environment.Exit(1); } } else { for (int i2 = 0; i2 < wt[i1].n; i2++) { k = -1; for (int i3 = 0; i3 < ent.Count; i3++) { if (wt[i1].in1[i2] == ent[i3].name) { k = i3; break; } } if (k >= 0) wt[i1].in_n.Add(k); else { Console.WriteLine("***error 待ち行列に入る窓口名が不適当です " + wt[i1].in1[i2]); Environment.Exit(1); } } } for (int i2 = 0; i2 < wt[i1].m; i2++) { k = -1; for (int i3 = 0; i3 < ent.Count; i3++) { if (wt[i1].out1[i2] == ent[i3].name) { k = i3; break; } } if (k >= 0) wt[i1].out_n.Add(k); else { Console.WriteLine("***error 待ち行列を処理する窓口名が不適当です " + wt[i1].out1[i2]); Environment.Exit(1); } } } // Entity for (int i1 = 0; i1 < ent.Count-1; i1++) { k = -1; for (int i2 = i1+1; i2 < ent.Count; i2++) { if (ent[i1].name == ent[i2].name) { Console.WriteLine("***error 同じ名前の窓口があります " + ent[i1].name); Environment.Exit(1); } } } for (int i1 = 0; i1 < ent.Count; i1++) { k = -1; for (int i2 = 0; i2 < wt.Count; i2++) { if (ent[i1].in1 == wt[i2].name) { k = i2; break; } } if (k >= 0) ent[i1].in_n = k; else { Console.WriteLine("***error 窓口に入る待ち行列名が不適当です " + ent[i1].in1); Environment.Exit(1); } if (ent[i1].to > 0) { k = -1; for (int i2 = 0; i2 < wt.Count; i2++) { if (ent[i1].out1 == wt[i2].name) { k = i2; break; } } if (k >= 0) ent[i1].out_n = k; else { Console.WriteLine("***error 窓口の出口にある待ち行列名が不適当です " + ent[i1].out1); Environment.Exit(1); } } } // 初期設定 cus = new Dictionary <int, Customer>(); p_time = 0.0; max_c = 0; nc = 0; now_c_t = 0.0; c_now_c = 0.0; c_sys = 0.0; max_sys = 0.0; end = e; // 乱数の初期設定 rn = new Random(); } /**********************/ /* 指数分布乱数の発生 */ /* m : 平均値 */ /* rerutn : 乱数 */ /**********************/ double Exp_b(double m) { return -m * Math.Log(rn.NextDouble()); } /**************/ /* 全体の制御 */ /**************/ public void Control() { // 到着時間の初期設定 for (int i1 = 0; i1 < inl.Count; i1++) { if (inl[i1].a_t == 0) inl[i1].arrive_time = Exp_b(inl[i1].mean); } // 実行制御 int[] sw = new int [2]; sw[0] = 0; while (sw[0] > -2) { Next(sw); // 次の処理の選択 if (sw[0] == -1) sw[0] = End_o_s(); // シミュレーションの終了 else { if (sw[0] == 0) Arrive(sw[1]); // 客の到着処理 else Service(sw[1]); // サービスの終了 } } } /*********************************************/ /* 次の処理の決定 */ /* sw[0] : =-1 : シミュレーションの終了 */ /* =0 : 客の到着処理 */ /* =1 : 窓口のサービス終了 */ /* [1] : 入り口番号 or 窓口番号 */ /*********************************************/ void Next(int[] sw) { double tm = end; // 次の処理時刻 sw[0] = -1; // 次の客の到着時刻 for (int i1 = 0; i1 < inl.Count; i1++) { Inlet x = inl[i1]; if (x.arrive_time >= 0.0 && x.arrive_time < tm) { sw[0] = 0; sw[1] = i1; tm = x.arrive_time; } } // サービス終了時刻 for (int i1 = 0; i1 < ent.Count; i1++) { Entity x = ent[i1]; if (x.service > 0 && x.end_time <= tm) { sw[0] = 1; sw[1] = i1; tm = x.end_time; } } if (sw[0] < 0) end = p_time; } /**********************************/ /* 終了処理 */ /* return : =-1 : 終了前処理 */ /* =-2 : 実際の終了 */ /**********************************/ int End_o_s() { int sw = -2; p_time = end; // 現在時刻 for (int i1 = 0; i1 < ent.Count; i1++) { Entity x = ent[i1]; if (x.service > 0) { // サービス中の客はいるか? if (sw == -2) { sw = -1; end = x.end_time; // 窓口i1のサービス終了時刻 } else { if (x.end_time > end) end = x.end_time; // 窓口i1のサービス終了時刻 } } } return sw; } /************************/ /* 客の到着処理 */ /* kk : 入り口番号 */ /************************/ void Arrive(int kk) { // // 客数の増加と次の客の到着時刻の設定 // nc += 1; // 到着客数カウンタ p_time = inl[kk].arrive_time; // 現在時刻 if (inl[kk].a_t == 0) // 次の客の到着時刻 inl[kk].arrive_time = p_time + Exp_b(inl[kk].mean); else if (inl[kk].a_t == 1) inl[kk].arrive_time = p_time + inl[kk].mean; else { if (inl[kk].que_i.Count == 0) inl[kk].arrive_time = -1.0; else inl[kk].arrive_time = inl[kk].que_i.Dequeue(); } if (inl[kk].arrive_time >= end) inl[kk].arrive_time = -1.0; // // 系内客数の処理 // c_now_c += cus.Count * (p_time - now_c_t); // 系内客数にその数が継続した時間を乗じた値の累計 now_c_t = p_time; // 現在の系内客数になった時刻 if ((int)cus.Count+1 > max_c) max_c = cus.Count + 1; // 最大系内客数 // // 空いている窓口を探す // int k1 = inl[kk].out_n; wt[k1].nc++; int k = -1; for (int i1 = 0; i1 < wt[k1].m; i1++) { int k2 = wt[k1].out_n[i1]; // 処理窓口 if (ent[k2].service == 0) { k = k2; break; } } // // 窓口に空きがない場合 // if (k < 0) { Customer ct_p = new Customer(0, k1, p_time); cus.Add(nc, ct_p); // 客の登録(系内客数) wt[k1].c_ql += wt[k1].que.Count * (p_time - wt[k1].ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 wt[k1].que.Enqueue(nc); // 客の登録(待ち行列) wt[k1].ql_t = p_time; // 現在の待ち行列長になった時刻 if (wt[k1].que.Count > wt[k1].max_q_l) wt[k1].max_q_l = wt[k1].que.Count; // 最大待ち行列長 } // // すぐサービスをうけられる場合 // else { Customer ct_p = new Customer(1, k, p_time); cus.Add(nc, ct_p); // 客の登録(系内客数) ent[k].service = nc; // サービスを受けている客番号 ent[k].busy_t = p_time; // 窓口がふさがった時刻 if (ent[k].s_t == 0) // 窓口のサービス終了時刻 ent[k].end_time = p_time + Exp_b(ent[k].mean); else ent[k].end_time = p_time + ent[k].mean; } } /**********************************/ /* サービス終了時の処理 */ /* kk : サービス終了窓口番号 */ /**********************************/ void Service(int kk) { // // 時間の設定 // p_time = ent[kk].end_time; // 現在時刻 ent[kk].end_time = -1.0; // サービス終了時間 // // システムの外へ出る場合 // if (ent[kk].to == 0) { // // 系内客数の処理 // c_now_c += cus.Count * (p_time - now_c_t); // 系内客数にその数が継続した時間を乗じた値の累計 now_c_t = p_time; // 現在の系内客数になった時刻 // // 滞在時間の処理 // Customer it = cus[ent[kk].service]; // サービス中の客 double x1 = p_time - it.time; c_sys += x1; // 滞在時間の累計 if (x1 > max_sys) max_sys = x1; // 最大滞在時間 cus.Remove(ent[kk].service); // 客の削除(系内客数) } // // 他の窓口処理へ入る場合の処理 // else { int k1 = ent[kk].out_n; wt[k1].nc++; int sw = 1; int k2 = 0; if (wt[k1].que.Count == 0) { for (int i1 = 0; i1 < wt[k1].m; i1++) { k2 = wt[k1].out_n[i1]; // 窓口 if (ent[k2].service == 0) { sw = 0; break; } } } // // 待ち行列がある場合 // if (sw > 0) { wt[k1].c_ql += wt[k1].que.Count * (p_time - wt[k1].ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 wt[k1].que.Enqueue(ent[kk].service); // 客の登録(待ち行列) wt[k1].ql_t = p_time; // 現在の待ち行列長になった時刻 if (wt[k1].que.Count > wt[k1].max_q_l) wt[k1].max_q_l = wt[k1].que.Count; // 最大待ち行列長 Customer it = cus[ent[kk].service]; it.state1 = 0; // 客の状態変更(待ち行列) it.state2 = ent[kk].out_n; // 客の状態変更(待ち行列番号) } // // すぐサービスをうけられる場合 // else { ent[k2].service = ent[kk].service; // サービスを受けている客番号 ent[k2].busy_t = p_time; // 窓口がふさがった時刻 if (ent[k2].s_t == 0) // 窓口のサービス終了時刻 ent[k2].end_time = p_time + Exp_b(ent[k2].mean); else ent[k2].end_time = p_time + ent[k2].mean; } } // // 窓口使用時間の処理 // ent[kk].service = 0; // 窓口を空き状態にする ent[kk].c_busy += (p_time - ent[kk].busy_t); // 窓口がふさがっている時間の累計 // // この窓口に対する待ち行列がある場合 // int k3 = ent[kk].in_n; if (wt[k3].que.Count > 0) { wt[k3].c_ql += wt[k3].que.Count * (p_time - wt[k3].ql_t); // 待ち行列長にその長さが継続した時間を乗じた値の累計 int n = wt[k3].que.Dequeue(); // 待ち行列の先頭にいる客の削除 wt[k3].ql_t = p_time; // 現在の待ち行列長になった時刻 Customer it = cus[n]; double x1 = p_time - it.time; wt[k3].c_wt += x1; // 待ち時間の累計 if (x1 > wt[k3].max_wt) wt[k3].max_wt = x1; // 最大待ち時間 for (int i1 = 0; i1 < wt[k3].m; i1++) { int k4 = wt[k3].out_n[i1]; // 窓口 if (ent[k4].service == 0) { ent[k4].service = n; // 窓口の客番号 ent[k4].busy_t = p_time; // 窓口がふさがった時刻 if (ent[k4].s_t == 0) // 窓口のサービス終了時刻 ent[k4].end_time = p_time + Exp_b(ent[k4].mean); else ent[k4].end_time = p_time + ent[k4].mean; it.state1 = 1; // 客の状態変更(サービス中) it.state2 = k4; // 客の状態変更(窓口番号) break; } } } } /**************************/ /* 統計量の計算と最終出力 */ /**************************/ public void Output() { // System Console.Write("全客数 " + nc); Console.WriteLine(" 最大系内客数 " + max_c + " 最大滞在時間 " + max_sys); Console.Write(" 平均系内客数 " + (c_now_c / p_time)); Console.Write(" 平均滞在時間 " + (c_sys / nc)); Console.WriteLine(" 終了時間 " + p_time); // Entity for (int i1 = 0; i1 < ent.Count; i1++) { Entity e = ent[i1]; Console.WriteLine("Entity " + e.name + " 稼働率 " + (e.c_busy / p_time)); } // Queue for (int i1 = 0; i1 < wt.Count; i1++) { Wait_c q = wt[i1]; Console.Write("Queue " + q.name + " 客数 " + q.nc); Console.Write(" 最大待ち行列長 " + q.max_q_l); Console.WriteLine(" 最大待ち時間 " + q.max_wt); Console.Write(" 平均待ち行列長 " + (q.c_ql / p_time)); Console.WriteLine(" 平均待ち時間 " + (q.c_wt / q.nc)); } } } /* ------------入力例(簡単な場合)----------- 1 Inlet 0 5 Queue 1 Queue 0 Inlet 2 Entity1 Entity2 2 Entity1 0 4 Queue 0 Entity2 0 4 Queue 0 10000 ------------入力例(複雑な場合)----------- 2 Inlet1 0 5 Queue1 Inlet2 0 5 Queue2 3 Queue1 0 Inlet1 1 Entity1 Queue2 0 Inlet2 1 Entity2 Queue3 2 Entity1 Entity2 2 Entity3 Entity4 4 Entity1 0 4 Queue1 1 Queue3 Entity2 0 4 Queue2 1 Queue3 Entity3 0 3 Queue3 0 Entity4 0 3 Queue3 0 10000 */
'****************************' ' 複雑な待ち行列問題 ' ' coded by Y.Suganuma ' '****************************' Imports System.IO Imports System.Collections.Generic Module Test Sub Main() ' 入り口 Console.Write("入り口(Inlet)の数は? ") Dim n_i As Integer = Integer.Parse(Console.ReadLine().Trim()) Dim inl As New List(Of Inlet) For i1 As Integer = 0 To n_i-1 Dim m_a As Double = 0.0 Console.WriteLine((i1+1) & " 番目の入り口(Inlet)") Console.Write(" 名前は? ") Dim name1 As String = Console.ReadLine().Trim() Dim que_i As New Queue(Of Double) Console.Write(" 到着分布(=0:指数分布,1=一定時間間隔,<0:指定:客数の負値))? ") Dim n As Integer = Integer.Parse(Console.ReadLine().Trim()) If n = 0 Console.Write(" 到着時間間隔の平均値は? ") m_a = Double.Parse(Console.ReadLine().Trim()) Else If n = 1 Console.Write(" 到着時間間隔は? ") m_a = Double.Parse(Console.ReadLine().Trim()) Else Dim x As Double For i2 As Integer = 0 To -n-1 Console.Write(" 到着時間は? ") x = Double.Parse(Console.ReadLine().Trim()) que_i.Enqueue(x) Next End If Console.Write(" 並ぶ待ち行列の名前は? ") Dim name2 As String = Console.ReadLine().Trim() Dim inl_e As Inlet = new Inlet(name1, n, m_a, que_i, name2) inl.Add(inl_e) Next ' 待ち行列 Console.Write("待ち行列(Queue)の数は? ") Dim n_q As Integer = Integer.Parse(Console.ReadLine().Trim()) Dim wt As New List(Of Wait_c) For i1 As Integer = 0 To n_q-1 Console.WriteLine((i1+1) & " 番目の待ち行列(Queue)") Console.Write(" 名前は? ") Dim name1 As String = Console.ReadLine().Trim() Console.Write(" 入り口(0),または,窓口(n>0,窓口の数)から? ") Dim n As Integer = Integer.Parse(Console.ReadLine().Trim()) Dim in1 As New List(Of String) If n = 0 Console.Write(" 入り口の名前は? ") Dim name2 As String = Console.ReadLine().Trim() in1.Add(name2) Else For i2 As Integer = 0 To n-1 Console.Write(" " & (i2+1) & " 番目の窓口の名前は? ") Dim name3 As String = Console.ReadLine().Trim() in1.Add(name3) Next End If Console.Write(" 処理する窓口の数は? ") Dim m As Integer = Integer.Parse(Console.ReadLine().Trim()) Dim out1 As New List (Of String) For i2 As Integer = 0 To m-1 Console.Write(" " & (i2+1) & " 番目の窓口の名前は? ") Dim name4 As String = Console.ReadLine().Trim() out1.Add(name4) Next Dim wt_e As Wait_c = new Wait_c(name1, n, in1, m, out1) wt.Add(wt_e) Next ' 窓口 Console.Write("窓口(Entity)の数は? ") Dim n_e As Integer = Integer.Parse(Console.ReadLine().Trim()) Dim ent As New List (Of Entity) For i1 As Integer = 0 To n_e-1 Dim m_s As Double = 0.0 Console.WriteLine((i1+1) & " 番目の窓口(Entity)") Console.Write(" 名前は? ") Dim name1 As String = Console.ReadLine().Trim() Console.Write(" サービス分布(=0:指数分布,1=一定時間)? ") Dim s_t As Integer = Integer.Parse(Console.ReadLine().Trim()) If s_t = 0 Console.Write(" サービス時間の平均値は? ") m_s = Double.Parse(Console.ReadLine().Trim()) Else Console.Write(" サービス時間は? ") m_s = Double.Parse(Console.ReadLine().Trim()) End IF Console.Write(" 待ち行列(入力)の名前は? ") Dim name2 As String = Console.ReadLine().Trim() Console.Write(" 終了後,外部(0),または,待ち行列(1)? ") Dim sw As Integer = Integer.Parse(Console.ReadLine().Trim()) Dim name3 As String = "" If sw > 0 Console.Write(" 待ち行列(出力)の名前は? ") name3 = Console.ReadLine().Trim() End If Dim ent_e As Entity = new Entity(name1, s_t, m_s, name2, sw, name3) ent.Add(ent_e) Next ' 全体の制御を行うクラス Console.Write("シミュレーション終了時間? ") Dim end_s As Double = Double.Parse(Console.ReadLine().Trim()) Dim base1 As Q_base = new Q_base(inl, wt, ent, end_s) ' 全体の制御を行うクラス ' 実行 base1.Control() ' 出力 base1.Output() End Sub '*******************' ' クラスInletの定義 ' '*******************' Class Inlet Public name As String ' 入り口名 Public out1 As String ' 待ち行列名 Public out_n As Integer ' 待ち行列番号 Public arrive_time As Double ' 客の到着時刻(負:客がない) Public a_t As Integer ' = -n : 到着する客の人数を負の値にしたもの ' =0 : 指数分布 ' =1 : 一定時間間隔 Public mean As Double ' 到着時間間隔またはその平均 Public que_i As New Queue(Of Double) ' 客の到着時刻リスト '***********************************************************' ' コンストラクタ ' ' name1 : 入り口名 ' ' a_t1 ' = -n : 到着する客の人数を負の値にしたもの ' ' ' =0 : 指数分布 ' ' ' =1 : 一定時間間隔 ' ' m_a: 到着時間間隔またはその平均 ' ' que1 : 客の到着時刻リスト ' ' name2 : 待ち行列名 ' '***********************************************************' Public Sub New (name1 As String, a_t1 As Integer, m_a As Double, que1 As Queue(Of Double), name2 As String) name = name1 out1 = name2 mean = m_a a_t = a_t1 que_i = que1 If a_t = 1 arrive_time = 0 ElseIf a_t < 0 arrive_time = que_i.Dequeue() End If End Sub End Class '********************' ' クラスWait_cの定義 ' '********************' Class Wait_c Public name As String ' 待ち行列名 Public nc As Integer ' 待ち行列への到着客数カウンタ Public max_q_l As Integer ' 最大待ち行列長 Public c_ql As Double ' 待ち行列長にその長さが継続した時間を乗じた値の累計 Public ql_t As Double ' 現在の待ち行列長になった時間 Public max_wt As Double ' 最大待ち時間 Public c_wt As Double ' 待ち時間の累計 Public n As Integer ' =0 : 入り口から入る ' >0 : 複数の窓口から入る(窓口数) Public in1 As New List(Of String) ' 入り口名,または,窓口名 Public in_n As New List(Of Integer) ' 入り口番号,または,窓口番号 Public m As Integer ' 処理する窓口数 Public out1 As New List(Of String) ' 窓口名 Public out_n As New List(Of Integer) ' 窓口番号 Public que As New Queue(Of Integer) ' 待ち行列 '*******************************************' ' コンストラクタ ' ' name1 : 待ち行列名 ' ' n1 : =0 : 入り口から入る ' ' >0 : 複数の窓口から入る(窓口数) ' ' in0 : 入り口名,または,窓口名 ' ' m1 : 処理する窓口数 ' ' out0 : 窓口名 ' '*******************************************' Public Sub New(name1 As String, n1 As Integer, in0 As List(Of String), m1 As Integer, out0 As List(Of String)) name = name1 in1 = in0 n = n1 out1 = out0 m = m1 nc = 0 max_q_l = 0 c_ql = 0.0 ql_t = 0.0 max_wt = 0.0 c_wt = 0.0 End Sub End Class '********************' ' クラスEntityの定義 ' '********************' Class Entity Public name As String ' Entity名 Public busy_t As Double ' 窓口がふさがった時間 Public c_busy As Double ' 窓口がふさがっている時間の累計 Public end_time As Double ' サービス終了時間(負:何も処理していない) Public s_t As Integer ' =0 : 指数分布 ' =1 : 一定時間 Public mean As Double ' 平均サービス時間 Public service As Integer ' サービス中の客番号(0のときはなし) Public in1 As String ' 待ち行列(入力)の名前 Public in_n As Integer ' 待ち行列(入力)番号 Public to_w As Integer ' =0 : システムの外に出る ' =1 : 待ち行列に入る Public out1 As String ' 待ち行列(出力)の名前 Public out_n As Integer ' 待ち行列(出力)番号 '**************************************' ' コンストラクタ ' ' name1 : 窓口名 ' ' s_t1 ' =0 : 指数分布 ' ' ' =1 : 一定時間 ' ' m_s:サービス時間またはその平均 ' ' name2 : 待ち行列(入力)の名前 ' ' sw : =0 : システムの外に出る ' ' =1 : 待ち行列に入る ' ' name3 : 待ち行列(出力)の名前 ' '**************************************' Public Sub New(name1 As String, s_t1 As Integer, m_s As Double, name2 As String, sw As Integer, name3 As String) name = name1 to_w = sw in1 = name2 If to_w > 0 out1 = name3 End If end_time = -1.0 s_t = s_t1 mean = m_s service = 0 busy_t = -1.0 c_busy = 0.0 End Sub End Class '**********************' ' クラスCustomerの定義 ' '**********************' Class Customer Public time As Double ' 到着時刻 Public state1 As Integer ' 客の状態1 ' =0 : 待ち行列に入っている ' =1 : サービスを受けている Public state2 As Integer ' 客の状態2(待ち行列番号,または,窓口番号) '*******************' ' コンストラクタ ' ' s1,s2 : 状態 ' ' t : 到着時刻 ' '*******************' Public Sub New(s1 As Integer, s2 As Integer, t As Double) time = t state1 = s1 state2 = s2 End Sub End Class '********************' ' クラスQ_baseの定義 ' '********************' Class Q_base Private p_time As Double ' 現在時刻 Private max_c As Integer ' 最大系内客数 Private nc As Integer ' 到着客数カウンタ Private now_c_t As Double ' 現在の系内客数になった時間 Private c_now_c As Double ' 系内客数にその数が継続した時間を乗じた値の累計 Private c_sys As Double ' 滞在時間の累計 Private max_sys As Double ' 最大滞在時間 Private end_s As Double ' シミュレーション終了時間 Private rn As New Random() ' 乱数 Private cus As New Dictionary(Of Integer, Customer) ' 系内にいる客のリスト Private inl As List(Of Inlet) ' Inletオブジェクトリスト Private wt As List(Of Wait_c) ' Wait_cオブジェクトリスト Private ent As List(Of Entity) ' Entityオブジェクトリスト '*************************************' ' コンストラクタ ' ' v_i : Inletオブジェクトリスト ' ' v_q : Wait_cオブジェクトリスト ' ' v_e : Entityオブジェクトリスト ' ' e : シミュレーション終了時刻 ' '*************************************' Public Sub New(v_i As List(Of Inlet), v_q As List(Of Wait_c), v_e As List(Of Entity), e As Double) ' 接続関係のチェック Console.WriteLine() ' Inlet inl = v_i wt = v_q ent = v_e For i1 As Integer = 0 To inl.Count-2 For i2 As Integer = i1+1 To inl.Count-1 If inl(i1).name = inl(i2).name Console.WriteLine("***error 同じ名前の入り口があります " & inl(i1).name) Environment.Exit(1) End If Next Next Dim k As Integer For i1 As Integer = 0 To inl.Count-1 k = -1 For i2 As Integer = 0 To wt.Count-1 If inl(i1).out1 = wt(i2).name k = i2 Exit For End If Next If k >= 0 inl(i1).out_n = k Else Console.WriteLine("***error 入り口から入る待ち行列名が不適当です " & inl(i1).out1) Environment.Exit(1) End If Next ' Queue For i1 As Integer = 0 To wt.Count-2 For i2 As Integer = i1+1 To wt.Count-1 If wt(i1).name = wt(i2).name Console.WriteLine("***error 同じ名前の待ち行列があります " & wt(i1).name) Environment.Exit(1) End If Next Next For i1 As Integer = 0 To wt.Count-1 If wt(i1).n = 0 k = -1 For i2 As Integer = 0 To inl.Count-1 If wt(i1).in1(0) = inl(i2).name k = i2 Exit For End If Next If k >= 0 wt(i1).in_n.Add(k) Else Console.WriteLine("***error 待ち行列に入る入り口名が不適当です " & wt(i1).in1(0)) Environment.Exit(1) End If Else For i2 As Integer = 0 To wt(i1).n-1 k = -1 For i3 As Integer = 0 To ent.Count-1 If wt(i1).in1(i2) = ent(i3).name k = i3 Exit For End If Next If k >= 0 wt(i1).in_n.Add(k) Else Console.WriteLine("***error 待ち行列に入る窓口名が不適当です " & wt(i1).in1(i2)) Environment.Exit(1) End If Next End If For i2 As Integer = 0 To wt(i1).m-1 k = -1 For i3 As Integer = 0 To ent.Count-1 If wt(i1).out1(i2) = ent(i3).name k = i3 Exit For End If Next If k >= 0 wt(i1).out_n.Add(k) Else Console.WriteLine("***error 待ち行列を処理する窓口名が不適当です " & wt(i1).out1(i2)) Environment.Exit(1) End If Next Next ' Entity For i1 As Integer = 0 To ent.Count-2 k = -1 For i2 As Integer = i1+1 To ent.Count-1 If ent(i1).name = ent(i2).name Console.WriteLine("***error 同じ名前の窓口があります " & ent(i1).name) Environment.Exit(1) End If Next Next For i1 As Integer = 0 To ent.Count-1 k = -1 For i2 As Integer = 0 To wt.Count-1 If ent(i1).in1 = wt(i2).name k = i2 Exit For End If Next If k >= 0 ent(i1).in_n = k Else Console.WriteLine("***error 窓口に入る待ち行列名が不適当です " & ent(i1).in1) Environment.Exit(1) End If If ent(i1).to_w > 0 k = -1 For i2 As Integer = 0 To wt.Count-1 If ent(i1).out1 = wt(i2).name k = i2 Exit For End If Next If k >= 0 ent(i1).out_n = k Else Console.WriteLine("***error 窓口の出口にある待ち行列名が不適当です " & ent(i1).out1) Environment.Exit(1) End If End If Next ' 初期設定 p_time = 0.0 max_c = 0 nc = 0 now_c_t = 0.0 c_now_c = 0.0 c_sys = 0.0 max_sys = 0.0 end_s = e End Sub '********************' ' 指数分布乱数の発生 ' ' m : 平均値 ' ' rerutn : 乱数 ' '********************' Function Exp_b(m As Double) Return -m * Math.Log(rn.NextDouble()) End Function '************' ' 全体の制御 ' '************' Public Sub Control() ' 到着時間の初期設定 For i1 As Integer = 0 To inl.Count-1 If inl(i1).a_t = 0 inl(i1).arrive_time = Exp_b(inl(i1).mean) End If Next ' 実行制御 Dim sw(2) As Integer sw(0) = 0 Do While sw(0) > -2 Next_e(sw) ' 次の処理の選択 If sw(0) = -1 sw(0) = End_o_s() ' シミュレーションの終了 Else If sw(0) = 0 Arrive(sw(1)) ' 客の到着処理 Else Service(sw(1)) ' サービスの終了 End If End If Loop End Sub '*******************************************' ' 次の処理の決定 ' ' sw(0) : =-1 : シミュレーションの終了 ' ' =0 : 客の到着処理 ' ' =1 : 窓口のサービス終了 ' ' (1) : 入り口番号 or 窓口番号 ' '*******************************************' Sub Next_e(sw() As Integer) Dim tm As Double = end_s ' 次の処理時刻 sw(0) = -1 ' 次の客の到着時刻 For i1 As Integer = 0 To inl.Count-1 Dim x As Inlet = inl(i1) If x.arrive_time >= 0.0 and x.arrive_time < tm sw(0) = 0 sw(1) = i1 tm = x.arrive_time End If Next ' サービス終了時刻 For i1 As Integer = 0 To ent.Count-1 Dim x As Entity = ent(i1) If x.service > 0 and x.end_time <= tm sw(0) = 1 sw(1) = i1 tm = x.end_time End If Next If sw(0) < 0 end_s = p_time End If End Sub '********************************' ' 終了処理 ' ' return : =-1 : 終了前処理 ' ' =-2 : 実際の終了 ' '********************************' Function End_o_s() Dim sw As Integer = -2 p_time = end_s ' 現在時刻 For i1 As Integer = 0 To ent.Count-1 Dim x As Entity = ent(i1) If x.service > 0 ' サービス中の客はいるか? If sw = -2 sw = -1 end_s = x.end_time ' 窓口i1のサービス終了時刻 Else If x.end_time > end_s end_s = x.end_time ' 窓口i1のサービス終了時刻 End If End If End If Next Return sw End Function '**********************' ' 客の到着処理 ' ' kk : 入り口番号 ' '**********************' Sub Arrive(kk As Integer) ' ' 客数の増加と次の客の到着時刻の設定 ' nc += 1 ' 到着客数カウンタ p_time = inl(kk).arrive_time ' 現在時刻 If inl(kk).a_t = 0 ' 次の客の到着時刻 inl(kk).arrive_time = p_time + Exp_b(inl(kk).mean) ElseIf inl(kk).a_t = 1 inl(kk).arrive_time = p_time + inl(kk).mean Else If inl(kk).que_i.Count = 0 inl(kk).arrive_time = -1.0 Else inl(kk).arrive_time = inl(kk).que_i.Dequeue() End If End If If inl(kk).arrive_time >= end_s inl(kk).arrive_time = -1.0 End If ' ' 系内客数の処理 ' c_now_c += cus.Count * (p_time - now_c_t) ' 系内客数にその数が継続した時間を乗じた値の累計 now_c_t = p_time ' 現在の系内客数になった時刻 If cus.Count+1 > max_c max_c = cus.Count + 1 ' 最大系内客数 End If ' ' 空いている窓口を探す ' Dim k1 As Integer = inl(kk).out_n wt(k1).nc += 1 Dim k As Integer = -1 For i1 As Integer = 0 To wt(k1).m-1 Dim k2 As Integer = wt(k1).out_n(i1) ' 処理窓口 If ent(k2).service = 0 k = k2 Exit For End If Next ' ' 窓口に空きがない場合 ' If k < 0 Dim ct_p As Customer = new Customer(0, k1, p_time) cus.Add(nc, ct_p) ' 客の登録(系内客数) wt(k1).c_ql += wt(k1).que.Count * (p_time - wt(k1).ql_t) ' 待ち行列長にその長さが継続した時間を乗じた値の累計 wt(k1).que.Enqueue(nc) ' 客の登録(待ち行列) wt(k1).ql_t = p_time ' 現在の待ち行列長になった時刻 If wt(k1).que.Count > wt(k1).max_q_l wt(k1).max_q_l = wt(k1).que.Count ' 最大待ち行列長 End If ' ' すぐサービスをうけられる場合 ' Else Dim ct_p As Customer = new Customer(1, k, p_time) cus.Add(nc, ct_p) ' 客の登録(系内客数) ent(k).service = nc ' サービスを受けている客番号 ent(k).busy_t = p_time ' 窓口がふさがった時刻 If ent(k).s_t = 0 ' 窓口のサービス終了時刻 ent(k).end_time = p_time + Exp_b(ent(k).mean) Else ent(k).end_time = p_time + ent(k).mean End If End If End Sub '********************************' ' サービス終了時の処理 ' ' kk : サービス終了窓口番号 ' '********************************' Sub Service(kk As Integer) ' ' 時間の設定 ' p_time = ent(kk).end_time ' 現在時刻 ent(kk).end_time = -1.0 ' サービス終了時間 ' ' システムの外へ出る場合 ' If ent(kk).to_w = 0 ' ' 系内客数の処理 ' c_now_c += cus.Count * (p_time - now_c_t) ' 系内客数にその数が継続した時間を乗じた値の累計 now_c_t = p_time ' 現在の系内客数になった時刻 ' ' 滞在時間の処理 ' Dim it As Customer = cus(ent(kk).service) ' サービス中の客 Dim x1 As Double = p_time - it.time c_sys += x1 ' 滞在時間の累計 If x1 > max_sys max_sys = x1 ' 最大滞在時間 End If cus.Remove(ent(kk).service) ' 客の削除(系内客数) ' ' 他の窓口処理へ入る場合の処理 ' Else Dim k1 As Integer = ent(kk).out_n wt(k1).nc += 1 Dim sw As Integer = 1 Dim k2 As Integer = 0 If wt(k1).que.Count = 0 For i1 As Integer = 0 To wt(k1).m-1 k2 = wt(k1).out_n(i1) ' 窓口 If ent(k2).service = 0 sw = 0 Exit For End If Next End If ' ' 待ち行列がある場合 ' If sw > 0 wt(k1).c_ql += wt(k1).que.Count * (p_time - wt(k1).ql_t) ' 待ち行列長にその長さが継続した時間を乗じた値の累計 wt(k1).que.Enqueue(ent(kk).service) ' 客の登録(待ち行列) wt(k1).ql_t = p_time ' 現在の待ち行列長になった時刻 If wt(k1).que.Count > wt(k1).max_q_l wt(k1).max_q_l = wt(k1).que.Count ' 最大待ち行列長 End If Dim it As Customer = cus(ent(kk).service) it.state1 = 0 ' 客の状態変更(待ち行列) it.state2 = ent(kk).out_n ' 客の状態変更(待ち行列番号) ' ' すぐサービスをうけられる場合 ' Else ent(k2).service = ent(kk).service ' サービスを受けている客番号 ent(k2).busy_t = p_time ' 窓口がふさがった時刻 If ent(k2).s_t = 0 ' 窓口のサービス終了時刻 ent(k2).end_time = p_time + Exp_b(ent(k2).mean) Else ent(k2).end_time = p_time + ent(k2).mean End If End If End If ' ' 窓口使用時間の処理 ' ent(kk).service = 0 ' 窓口を空き状態にする ent(kk).c_busy += (p_time - ent(kk).busy_t) ' 窓口がふさがっている時間の累計 ' ' この窓口に対する待ち行列がある場合 ' Dim k3 As Integer = ent(kk).in_n If wt(k3).que.Count > 0 wt(k3).c_ql += wt(k3).que.Count * (p_time - wt(k3).ql_t) ' 待ち行列長にその長さが継続した時間を乗じた値の累計 Dim n As Integer = wt(k3).que.Dequeue() ' 待ち行列の先頭にいる客の削除 wt(k3).ql_t = p_time ' 現在の待ち行列長になった時刻 Dim it As Customer = cus(n) Dim x1 As Double = p_time - it.time wt(k3).c_wt += x1 ' 待ち時間の累計 If x1 > wt(k3).max_wt wt(k3).max_wt = x1 ' 最大待ち時間 End If For i1 As Integer = 0 To wt(k3).m-1 Dim k4 As Integer = wt(k3).out_n(i1) ' 窓口 If ent(k4).service = 0 ent(k4).service = n ' 窓口の客番号 ent(k4).busy_t = p_time ' 窓口がふさがった時刻 If ent(k4).s_t = 0 ' 窓口のサービス終了時刻 ent(k4).end_time = p_time + Exp_b(ent(k4).mean) Else ent(k4).end_time = p_time + ent(k4).mean End If it.state1 = 1 ' 客の状態変更(サービス中) it.state2 = k4 ' 客の状態変更(窓口番号) Exit For End If Next End If End Sub '************************' ' 統計量の計算と最終出力 ' '************************' Public Sub Output() ' System Console.Write("全客数 " & nc) Console.WriteLine(" 最大系内客数 " & max_c & " 最大滞在時間 " & max_sys) Console.Write(" 平均系内客数 " & (c_now_c / p_time)) Console.Write(" 平均滞在時間 " & (c_sys / nc)) Console.WriteLine(" 終了時間 " & p_time) ' Entity For i1 As Integer = 0 To ent.Count-1 Dim e As Entity = ent(i1) Console.WriteLine("Entity " & e.name & " 稼働率 " & (e.c_busy / p_time)) Next ' Queue For i1 As Integer = 0 To wt.Count-1 Dim q As Wait_c = wt(i1) Console.Write("Queue " & q.name & " 客数 " & q.nc) Console.Write(" 最大待ち行列長 " & q.max_q_l) Console.WriteLine(" 最大待ち時間 " & q.max_wt) Console.Write(" 平均待ち行列長 " & (q.c_ql / p_time)) Console.WriteLine(" 平均待ち時間 " & (q.c_wt / q.nc)) Next End Sub End Class End Module /* ------------入力例(簡単な場合)----------- 1 Inlet 0 5 Queue 1 Queue 0 Inlet 2 Entity1 Entity2 2 Entity1 0 4 Queue 0 Entity2 0 4 Queue 0 10000 ------------入力例(複雑な場合)----------- 2 Inlet1 0 5 Queue1 Inlet2 0 5 Queue2 3 Queue1 0 Inlet1 1 Entity1 Queue2 0 Inlet2 1 Entity2 Queue3 2 Entity1 Entity2 2 Entity3 Entity4 4 Entity1 0 4 Queue1 1 Queue3 Entity2 0 4 Queue2 1 Queue3 Entity3 0 3 Queue3 0 Entity4 0 3 Queue3 0 10000 */
情報学部 | 菅沼ホーム | 目次 | 索引 |