| 情報学部 | 菅沼ホーム | 目次 | 索引 |

/******************************/
/* 複雑な待ち行列問題 */
/* 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
*/
| 情報学部 | 菅沼ホーム | 目次 | 索引 |