# -*- 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()