簡単な待ち行列問題

#############################
# 簡単な待ち行列問題(M/M/s)
#      coded by Y.Suganuma
#############################

########################
# クラスCustomerの定義
########################

class Customer

	#####################
	# コンストラクタ
	#      s : 状態
	#      t : 到着時刻
	#####################
	def initialize(s, t)
		@_state = s   # 客の状態
	                  #   =-1 : 待ち行列に入っている
	                  #   >=0 : サービスを受けている窓口番号
		@_time  = t   # 到着時刻
	end
	attr_accessor("_state", "_time")
end

########################
# クラスQ_baseの定義
########################

class Q_base

	#########################################
	# コンストラクタ
	#      s_i : 窓口の数
	#      m_a_i : 到着時間間隔の平均値
	#      m_s_i : サービス時間の平均値
	#      stp_i : シミュレーション終了時刻
	#########################################

	def initialize(s_i, m_a_i, m_s_i, stp_i)
				# 設定
		@_s   = s_i   # 窓口の数
		@_m_a = m_a_i   # 到着時間間隔の平均値
		@_m_s = m_s_i   # サービス時間の平均値
		@_stp = stp_i   # シミュレーション終了時刻
				# 領域の確保
		@_sb   = Array.new(@_s)   # 各窓口の空き状況
		                          #    =0 : 窓口は空いている
		                          #    >0 : サービスを受けている客番号
		@_c_sb = Array.new(@_s)   # 各窓口がふさがっている時間の累計
		for i1 in 0 ... @_s
			@_sb[i1]   = 0
			@_c_sb[i1] = 0.0
		end
		@_st_e = Array.new(@_s)   # 各窓口のサービス終了時刻
		@_st_s = Array.new(@_s)   # 各窓口がふさがった時刻
				# 初期設定
		@_p_time = 0.0  # 現在時刻
		@_nc     = 0   # 到着客数カウンタ
		@_asb    = @_s   # 全窓口の空き状況
		                 #    =0 : すべての窓口がふさがっている
		                 #    =n : n個の窓口が空いている
		@_m_lq   = 0   # 最大待ち行列長
		@_m_sc   = 0   # 最大系内客数
		@_c_asb  = 0.0   # すべての窓口がふさがっている時間の累計
		@_c_wt   = 0.0   # 待ち時間の累計
		@_m_wt   = 0.0   # 最大待ち時間
		@_c_lq_t = 0.0   # 待ち行列長にその長さが継続した時間を乗じた値の累計
		@_lq_t   = 0.0   # 現在の待ち行列長になった時刻
		@_m_sys  = 0.0   # 最大滞在時間
		@_c_sys  = 0.0   # 滞在時間の累計
		@_c_sc_t = 0.0   # 系内客数にその数が継続した時間を乗じた値の累計
		@_sc_t   = 0.0   # 現在の系内客数になった時刻
		@_asb_t  = 0.0   # すべての窓口がふさがった時刻
		@_cus    = Hash.new()   # 系内にいる客のリスト
		@_que    = Array.new()   # 待ち行列
			# 最初の客の到着時刻の設定
		@_at = @_p_time + Next_at()   # 次の客の到着時刻(負の時は,終了)
	end
	
	################################
	# 次の客の到着までの時間の発生
	################################

	def Next_at()
		return -@_m_a * Math.log(rand(0))
	end
	
	#####################
	# サービス時間の発生
	#####################

	def Next_sv()
		return -@_m_s * Math.log(rand(0))
	end
	
	##############
	# 全体の制御
	##############

	def Control()
	
		sw = 0
		while sw > -2
			sw = Next()   # 次の処理の選択
			if sw == -1
				sw = End_o_s()   # シミュレーションの終了
			else
				if sw == 0
					Arrive()   # 客の到着処理
				else
					Service(sw)   # サービスの終了
				end
			end
		end
	end
	
	##################################################
	# 次の処理の決定
	#      return : =-1 : シミュレーションの終了
	#               =0  : 客の到着処理
	#               =i  : i番目の窓口のサービス終了
	##################################################

	def Next()
	
		sw = -1
		t  = @_stp   # シミュレーション終了時刻で初期設定
						# 次の客の到着時刻
		if @_at >= 0.0 && @_at < t
			sw = 0
			t  = @_at
		end
						# サービス終了時刻
		for i1 in 0 ... @_s
			if @_sb[i1] > 0 && @_st_e[i1] <= t
				sw = i1 + 1
				t  = @_st_e[i1]   # 窓口i1のサービス終了時刻
			end
		end
	
		return sw
	end
	
	##################################
	# 終了処理
	#      return : =-1 : 終了前処理
	#               =-2 : 実際の終了
	##################################

	def End_o_s()
	
		sw       = -2
		@_p_time = @_stp   # 現在時刻
		@_at     = -1.0   # 次の客の到着時刻
	
		for i1 in 0 ... @_s
			if @_sb[i1] > 0   # サービス中の客はいるか?
				if sw == -2
					sw    = -1
					@_stp = @_st_e[i1]   # 窓口i1のサービス終了時刻
				else
					if @_st_e[i1] > @_stp
						@_stp = @_st_e[i1]   # 窓口i1のサービス終了時刻
					end
				end
			end
		end
	
		return sw
	end
	
	################
	# 客の到着処理
	################

	def Arrive()
				# 客数の増加と次の客の到着時刻の設定
		@_nc     += 1   # 到着客数カウンタ
		@_p_time  = @_at   # 現在時刻
		@_at      = @_p_time + Next_at()   # 次の客の到着時刻
		if @_at >= @_stp
			@_at = -1.0
		end
				# 系内客数の処理
		@_c_sc_t += @_cus.length * (@_p_time - @_sc_t)   # 系内客数にその数が継続した時間を乗じた値の累計
		@_sc_t    = @_p_time   # 現在の系内客数になった時刻
		if @_cus.length+1 > @_m_sc
			@_m_sc = @_cus.length + 1   # 最大系内客数
		end
				# 窓口に空きがない場合
		if @_asb == 0
			ct_p         = Customer.new(-1, @_p_time)
			@_cus[@_nc]  = ct_p   # 客の登録(系内客数)
			@_c_lq_t    += @_que.length * (@_p_time - @_lq_t)   # 待ち行列長にその長さが継続した時間を乗じた値の累計
			@_que.push(@_nc)   # 客の登録(待ち行列)
			@_lq_t = @_p_time   # 現在の待ち行列長になった時刻
			if @_que.length > @_m_lq
				@_m_lq = @_que.length   # 最大待ち行列長
			end
				# すぐサービスを受けられる場合
		else
			k = -1
			for i1 in 0 ... @_s
				if @_sb[i1] == 0
					ct_p         = Customer.new(i1, @_p_time)
					@_cus[@_nc]  = ct_p   # 客の登録(系内客数)
					k            = i1
					@_sb[k]      = @_nc   # サービスを受けている客番号
					@_st_e[k]    = @_p_time + Next_sv()   # 窓口kのサービス終了時刻
					@_asb       -= 1   # 空いている窓口数
					break
				end
			end
			@_st_s[k] = @_p_time   # 窓口kがふさがった時刻
			if @_asb == 0
				@_asb_t = @_p_time   # すべての窓口がふさがった時刻
			end
		end
	end
	
	#################################
	# サービス終了時の処理
	#      k : サービス終了窓口番号
	#################################

	def Service(k)
				# 時間の設定
		k         -= 1
		@_p_time   = @_st_e[k]   # 現在時刻
		@_st_e[k]  = -1.0   # サービス終了時間
				# 系内客数の処理
		@_c_sc_t  += @_cus.length * (@_p_time - @_sc_t)   # 系内客数にその数が継続した時間を乗じた値の累計
		@_sc_t     = @_p_time   # 現在の系内客数になった時刻
				# 滞在時間の処理
		x1         = @_p_time - @_cus[@_sb[k]]._time
		@_c_sys   += x1   # 滞在時間の累計
		if x1 > @_m_sys
			@_m_sys = x1   # 最大滞在時間
		end
		@_cus.delete(@_sb[k])   # 客の削除(系内客数)
				# 窓口使用時間の処理
		@_asb     += 1   # 空いている窓口数
		@_sb[k]    = 0   # 窓口kを空き状態にする
		@_c_sb[k] += (@_p_time - @_st_s[k])   # 窓口kがふさがっている時間の累計
				# 待ち行列がある場合
		if @_que.length > 0
			@_asb    -= 1   # 開いている窓口数
			@_c_lq_t += @_que.length * (@_p_time - @_lq_t)   # 待ち行列長にその長さが継続した時間を乗じた値の累計
			n         = @_que.delete_at(0)
			@_lq_t    = @_p_time   # 現在の待ち行列長になった時刻
			x1        = @_p_time - @_cus[n]._time
			@_c_wt   += x1   # 待ち時間の累計
			if x1 > @_m_wt
				@_m_wt = x1   # 最大待ち時間
			end
			k = -1
			for i1 in 0 ... @_s
				if @_sb[i1] == 0
					k               = i1
					@_sb[k]         = n   # 窓口kの客番号
					@_st_e[k]       = @_p_time + Next_sv()   # 窓口kのサービス終了時刻
					@_st_s[k]       = @_p_time   # 窓口kがふさがった時刻
					@_cus[n]._state = k   # 客の状態変更
					break
				end
			end
				# 待ち行列がない場合
		else
			if @_asb == 1
				@_c_asb += (@_p_time - @_asb_t)   # すべての窓口がふさがっている時間の累計
			end
		end
	end
	
	########################
	# 結果の出力
	# (カッコ内は理論値)
	########################

	def Output()
	
		rn  = Float(@_nc)
		rs  = Float(@_s)
		ram = 1.0 / @_m_a
		myu = 1.0 / @_m_s
		rou = ram / (rs * myu)
		if @_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))
		end
		lq = pi * rou / (1.0 - rou)
		l  = lq + rs * rou
		wq = lq / ram
		w  = wq + 1.0 / myu
		printf("シミュレーション終了時間=%.3f 客数=%d ρ=%.3f p0=%.3f\n", @_p_time, @_nc, rou, p0)
		printf("   すべての窓口が塞がっている割合=%.3f (%.3f)\n", @_c_asb/@_p_time, pi)
		printf("   各窓口が塞がっている割合\n")
		for i1 in 0 ... @_s
			printf("      %d   %.3f\n", (i1+1), @_c_sb[i1]/@_p_time)
		end
		printf("   平均待ち行列長=%.3f (%.3f)  最大待ち行列長=%d\n", @_c_lq_t/@_p_time, lq, @_m_lq)
		printf("   平均系内客数  =%.3f (%.3f)  最大系内客数  =%d\n", @_c_sc_t/@_p_time, l, @_m_sc)
		printf("   平均待ち時間  =%.3f (%.3f)  最大待ち時間  =%.3f\n", @_c_wt/rn, wq, @_m_wt)
		printf("   平均滞在時間  =%.3f (%.3f)  最大滞在時間  =%.3f\n", @_c_sys/rn, w, @_m_sys)
	end
end

			# 入力データ
print("窓口の数は? ")
s = Integer(gets())
print("シミュレーション終了時間? ")
stp = Float(gets())
print("   到着時間間隔の平均値は? ")
m_a = Float(gets())
print("   サービス時間の平均値は? ")
m_s = Float(gets())
			# システムの定義
base = Q_base.new(s, m_a, m_s, stp)
			# シミュレーションの実行
srand()
base.Control()
			# 出力
base.Output()