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