#############################
# 簡単な待ち行列問題(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()