簡単な待ち行列問題

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