複雑な待ち行列問題

# -*- coding: UTF-8 -*-
import sys
from math import *
from random import *
import numpy as np

#####################
# クラス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:サービス時間またはその平均
	#      in : 待ち行列(入力)の名前
	#      sw : =0 : システムの外に出る
	#           =1 : 待ち行列に入る
	#      out : 待ち行列(出力)の名前
	########################################

	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 :
					cout << "***error 同じ名前の待ち行列があります " + self.que[i1].name + "\n"

		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()   # 系内にいる客のリスト
	
	#############
	# 全体の制御
	#############

	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))

----------------------------------

# -*- coding: UTF-8 -*-
import numpy as np
import sys
from math import *
from random import *
from function import Inlet, Queue, Entity, Customer, Q_base

#############################
# 複雑な待ち行列問題
#      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