############################# # 複雑な待ち行列問題 # coded by Y.Suganuma ############################# ##################### # クラスInletの定義 ##################### class Inlet ########################################################### # コンストラクタ # name1 : 入り口名 # a_t1 # = -n : 到着する客の人数を負の値にしたもの # # =0 : 指数分布 # # =1 : 一定時間間隔 # m_a: 到着時間間隔またはその平均 # que1 : 客の到着時刻リスト # name2 : 待ち行列名 ########################################################### def initialize(name1, a_t1, m_a, que1, name2) @_name = name1 # 入り口名 @_out = name2 # 待ち行列名 @_a_t = a_t1 # = -n : 到着する客の人数を負の値にしたもの # =0 : 指数分布 # =1 : 一定時間間隔 @_mean = m_a # 到着時間間隔またはその平均 @_que = que1 # 客の到着時刻リスト if @_a_t == 0 @_arrive_time = -@_mean * Math.log(rand(0)) # 客の到着時刻(負:客がない) elsif @_a_t == 1 @_arrive_time = 0 else @_arrive_time = @_que.delete_at(0) end @_out_n = 0 # 待ち行列番号(Q_baseのコンストラクタで設定) end attr_accessor("_out_n", "_out", "_name", "_arrive_time", "_a_t", "_mean", "_que") end ##################### # クラスQueueの定義 ##################### class Queue ############################################# # コンストラクタ # name1 : 待ち行列名 # n1 : =0 : 入り口から入る # >0 : 複数の窓口から入る(窓口数) # in1 : 入り口名,または,窓口名 # m1 : 処理する窓口数 # out1 : 窓口名 ############################################# def initialize(name1, n1, in1, m1, out1) @_name = name1 # 待ち行列名 @_n = n1 # =0 : 入り口から入る # >0 : 複数の窓口から入る(窓口数) @_inn = in1 # 入り口名,または,窓口名(入り口) @_m = m1 # 処理する窓口数 @_out = out1 # 窓口名(出口) @_nc = 0 # 待ち行列への到着客数カウンタ @_max_q_l = 0 # 最大待ち行列長 @_c_ql = 0.0 # 待ち行列長にその長さが継続した時間を乗じた値の累計 @_ql_t = 0.0 # 現在の待ち行列長になった時間 @_max_wt = 0.0 # 最大待ち時間 @_c_wt = 0.0 # 待ち時間の累計 @_in_n = Array.new() # 入り口番号,または,窓口番号(Q_baseのコンストラクタで設定) @_out_n = Array.new() # 窓口番号(Q_baseのコンストラクタで設定) @_que = Array.new() # 待ち行列 end attr_accessor("_name", "_n", "_m", "_inn", "_in_n", "_out", "_out_n", "_nc", "_c_ql", "_que", "_ql_t", "_max_q_l", "_c_wt", "_max_wt") end ###################### # クラスEntityの定義 ###################### class Entity ######################################## # コンストラクタ # name1 : 窓口名 # s_t1 # =0 : 指数分布 # # =1 : 一定時間 # m_s:サービス時間またはその平均 # name2 : 待ち行列(入力)の名前 # sw : =0 : システムの外に出る # =1 : 待ち行列に入る # name3 : 待ち行列(出力)の名前 ######################################## def initialize(name1, s_t1, m_s, name2, sw, name3) @_name = name1 # 窓口名 @_to = sw # =0 : システムの外に出る # =1 : 待ち行列に入る @_inn = name2 # 待ち行列(入力)の名前 if @_to > 0 @_out = name3 # 待ち行列(出力)の名前 end @_end_time = -1.0 # サービス終了時刻(負:何も処理していない) @_s_t = s_t1 # =0 : 指数分布 # =1 : 一定時間 @_mean = m_s # 平均サービス時間 @_service = 0 # サービス中の客番号(0のときは無し) @_busy_t = -1.0 # 窓口がふさがった時刻 @_c_busy = 0.0 # 窓口がふさがっている時間の累計 @_in_n = -1 # 待ち行列(入力)番号(Q_baseのコンストラクタで設定) @_out_n = -1 # 待ち行列(出力)番号(Q_baseのコンストラクタで設定) end attr_accessor("_service", "_name", "_inn", "_in_n", "_to", "_out", "_out_n", "_end_time", "_busy_t", "_s_t", "_c_busy", "_mean") end ######################## # クラスCustomerの定義 ######################## class Customer ##################### # コンストラクタ # s1,s2 : 状態 # t : 到着時刻 ##################### def initialize(s1, s2, t) @_time = t # 到着時刻 @_state1 = s1 # 客の状態1 # =0 : 待ち行列に入っている # =1 : サービスを受けている @_state2 = s2 # 客の状態2(待ち行列番号,または,窓口番号) end attr_accessor("_state1", "_state2", "_time") end ####################### # クラスQ_baseの定義 ####################### class Q_base ######################################## # コンストラクタ # v_i : Inletオブジェクトリスト # v_q : Queueオブジェクトリスト # v_e : Entityオブジェクトリスト # e : シミュレーション終了時刻 ######################################## def initialize(v_i, v_q, v_e, e) print("\n") # 接続関係のチェック @_inl = v_i # Inletオブジェクトリスト @_que = v_q # Queueオブジェクトリスト @_ent = v_e # Entityオブジェクトリスト # Inlet for i1 in 0 ... @_inl.length-1 for i2 in i1+1 ... @_inl.length if @_inl[i1]._name == @_inl[i2]._name print("***error 同じ名前の入り口があります " + @_inl[i1]._name + "\n") end end end for i1 in 0 ... @_inl.length k = -1 for i2 in 0 ... @_que.length if @_inl[i1]._out == @_que[i2]._name k = i2 break end end if k >= 0 @_inl[i1]._out_n = k else print("***error 入り口から入る待ち行列名が不適当です " + @_inl[i1]._out + "\n") end end # Queue for i1 in 0 ... @_que.length-1 for i2 in i1+1 ... @_que.length if @_que[i1]._name == @_que[i2]._name print("***error 同じ名前の待ち行列があります " + @_que[i1]._name + "\n") end end end for i1 in 0 ... @_que.length if @_que[i1]._n == 0 k = -1 for i2 in 0 ... @_inl.length if @_que[i1]._inn[0] == @_inl[i2]._name k = i2 break end end if k >= 0 @_que[i1]._in_n.push(k) else print("***error 待ち行列に入る入り口名が不適当です " + @_que[i1]._inn[0] + "\n") end else for i2 in 0 ... @_que[i1]._n k = -1 for i3 in 0 ... @_ent.length if @_que[i1]._inn[i2] == @_ent[i3]._name k = i3 break end end if k >= 0 @_que[i1]._in_n.push(k) else print("***error 待ち行列に入る窓口名が不適当です " + @_que[i1]._inn[i2] + "\n") end end end for i2 in 0 ... @_que[i1]._m k = -1 for i3 in 0 ... @_ent.length if @_que[i1]._out[i2] == @_ent[i3]._name k = i3 break end end if k >= 0 @_que[i1]._out_n.push(k) else print("***error 待ち行列を処理する窓口名が不適当です " + @_que[i1]._out[i2]) end end end # Entity for i1 in 0 ... @_ent.length-1 k = -1 for i2 in i1+1 ... @_ent.length if @_ent[i1]._name == @_ent[i2]._name print("***error 同じ名前の窓口があります " + @_ent[i1]._name + "\n") end end end for i1 in 0 ... @_ent.length k = -1 for i2 in 0 ... @_que.length if @_ent[i1]._inn == @_que[i2]._name k = i2 break end end if k >= 0 @_ent[i1]._in_n = k else print("***error 窓口に入る待ち行列名が不適当です " + @_ent[i1]._inn + "\n") end if @_ent[i1]._to > 0 k = -1 for i2 in 0 ... @_que.length if @_ent[i1]._out == @_que[i2]._name k = i2 break end end if k >= 0 @_ent[i1]._out_n = k else print("***error 窓口の出口にある待ち行列名が不適当です " + @_ent[i1]._out) end end end # 初期設定 @_p_time = 0.0 # 現在時刻 @_max_c = 0 # 最大系内客数 @_nc = 0 # システムへの到着客数カウンタ @_now_c_t = 0.0 # 現在の系内客数になった時間 @_c_now_c = 0.0 # 系内客数にその数が継続した時間を乗じた値の累計 @_c_sys = 0.0 # 滞在時間の累計 @_max_sys = 0.0 # 最大滞在時間 @_end = e # シミュレーション終了時間 @_cus = Hash.new() # 系内にいる客のリスト # 乱数の初期設定 srand() end ############# # 全体の制御 ############# def Control() sw = [0, 0] while sw[0] > -2 Next(sw) # 次の処理の選択 if sw[0] == -1 sw[0] = End_o_s() # シミュレーションの終了 else if sw[0] == 0 Arrive(sw[1]) # 客の到着処理 else Service(sw[1]) # サービスの終了 end end end end ############################################# # 次の処理の決定 # sw[0] : =-1 : シミュレーションの終了 # =0 : 客の到着処理 # =1 : 窓口のサービス終了 # [1] : 入り口番号 or 窓口番号 ############################################# def Next(sw) tm = @_end # 次の処理時刻 sw[0] = -1 # 次の客の到着時刻 for i1 in 0 ... @_inl.length x = @_inl[i1] if x._arrive_time >= 0.0 and x._arrive_time < tm sw[0] = 0 sw[1] = i1 tm = x._arrive_time end end # サービス終了時刻 for i1 in 0 ... @_ent.length x = @_ent[i1] if x._service > 0 and x._end_time <= tm sw[0] = 1 sw[1] = i1 tm = x._end_time end end if sw[0] < 0 @_end = @_p_time end end ################################## # 終了処理 # return : =-1 : 終了前処理 # =-2 : 実際の終了 ################################## def End_o_s() sw = -2 @_p_time = @_end # 現在時刻 for i1 in 0 ... @_ent.length x = @_ent[i1] if x._service > 0 # サービス中の客はいるか? if sw == -2 sw = -1 @_end = x._end_time # 窓口i1のサービス終了時刻 else if x._end_time > @_end @_end = x._end_time # 窓口i1のサービス終了時刻 end end end end return sw end ######################## # 客の到着処理 # kk : 入り口番号 ######################## def Arrive(kk) # 客数の増加と次の客の到着時刻の設定 @_nc += 1 # 到着客数カウンタ @_p_time = @_inl[kk]._arrive_time # 現在時刻 if @_inl[kk]._a_t == 0 # 次の客の到着時刻 @_inl[kk]._arrive_time = @_p_time - @_inl[kk]._mean * Math.log(rand(0)) elsif @_inl[kk]._a_t == 1 @_inl[kk]._arrive_time = @_p_time + @_inl[kk]._mean else if (@_inl[kk]._que).length < 1 @_inl[kk]._arrive_time = -1.0 else @_inl[kk]._arrive_time = @_inl[kk]._que.delete_at(0) end end if @_inl[kk]._arrive_time >= @_end @_inl[kk]._arrive_time = -1.0 end # 系内客数の処理 @_c_now_c += @_cus.length * (@_p_time - @_now_c_t) # 系内客数にその数が継続した時間を乗じた値の累計 @_now_c_t = @_p_time # 現在の系内客数になった時刻 if @_cus.length+1 > @_max_c @_max_c = @_cus.length + 1 # 最大系内客数 end # 空いている窓口を探す k1 = @_inl[kk]._out_n @_que[k1]._nc += 1 k = -1 for i1 in 0 ... @_que[k1]._m k2 = @_que[k1]._out_n[i1] # 処理窓口 if @_ent[k2]._service == 0 k = k2 break end end # 窓口に空きがない場合 if k < 0 ct_p = Customer.new(0, k1, @_p_time) @_cus[@_nc] = ct_p # 客の登録(系内客数) @_que[k1]._c_ql += (@_que[k1]._que).length * (@_p_time - @_que[k1]._ql_t) # 待ち行列長にその長さが継続した時間を乗じた値の累計 @_que[k1]._que.push(@_nc) # 客の登録(待ち行列) @_que[k1]._ql_t = @_p_time # 現在の待ち行列長になった時刻 if (@_que[k1]._que).length > @_que[k1]._max_q_l @_que[k1]._max_q_l = (@_que[k1]._que).length # 最大待ち行列長 end # すぐサービスをうけられる場合 else ct_p = Customer.new(1, k, @_p_time) @_cus[@_nc] = ct_p # 客の登録(系内客数) @_ent[k]._service = @_nc # サービスを受けている客番号 @_ent[k]._busy_t = @_p_time # 窓口がふさがった時刻 if @_ent[k]._s_t == 0 # 窓口のサービス終了時刻 @_ent[k]._end_time = @_p_time - @_ent[k]._mean * Math.log(rand(0)) else @_ent[k]._end_time = @_p_time + @_ent[k]._mean end end end ################################### # サービス終了時の処理 # kk : サービス終了窓口番号 ################################### def Service(kk) # 時間の設定 @_p_time = @_ent[kk]._end_time # 現在時刻 @_ent[kk]._end_time = -1.0 # サービス終了時間 # システムの外へ出る場合 if @_ent[kk]._to == 0 # 系内客数の処理 @_c_now_c += @_cus.length * (@_p_time - @_now_c_t) # 系内客数にその数が継続した時間を乗じた値の累計 @_now_c_t = @_p_time # 現在の系内客数になった時刻 # 滞在時間の処理 it = @_cus[@_ent[kk]._service] # サービス中の客 x1 = @_p_time - it._time @_c_sys += x1 # 滞在時間の累計 if x1 > @_max_sys @_max_sys = x1 # 最大滞在時間 end @_cus.delete(@_ent[kk]._service) # 客の削除(系内客数) # 他の窓口処理へ入る場合の処理 else k1 = @_ent[kk]._out_n @_que[k1]._nc += 1 sw = 1 k2 = 0 if (@_que[k1]._que).length == 0 for i1 in 0 ... @_que[k1]._m k2 = @_que[k1]._out_n[i1] # 窓口 if @_ent[k2]._service == 0 sw = 0 break end end end # 待ち行列がある場合 if sw > 0 @_que[k1]._c_ql += (@_que[k1]._que).length * (@_p_time - @_que[k1]._ql_t) # 待ち行列長にその長さが継続した時間を乗じた値の累計 @_que[k1]._que.push(@_ent[kk]._service) # 客の登録(待ち行列) @_que[k1]._ql_t = @_p_time # 現在の待ち行列長になった時刻 if (@_que[k1]._que).length > @_que[k1]._max_q_l @_que[k1]._max_q_l = (@_que[k1]._que).length # 最大待ち行列長 end it = @_cus[@_ent[kk]._service] it._state1 = 0 # 客の状態変更(待ち行列) it._state2 = @_ent[kk]._out_n # 客の状態変更(待ち行列番号) # すぐサービスをうけられる場合 else @_ent[k2]._service = @_ent[kk]._service # サービスを受けている客番号 @_ent[k2]._busy_t = @_p_time # 窓口がふさがった時刻 if @_ent[k2]._s_t == 0 # 窓口のサービス終了時刻 @_ent[k2]._end_time = @_p_time - @_ent[k2]._mean * Math.log(rand(0)) else @_ent[k2]._end_time = @_p_time + @_ent[k2]._mean end end end # 窓口使用時間の処理 @_ent[kk]._service = 0 # 窓口を空き状態にする @_ent[kk]._c_busy += (@_p_time - @_ent[kk]._busy_t) # 窓口がふさがっている時間の累計 # この窓口に対する待ち行列がある場合 k3 = @_ent[kk]._in_n if (@_que[k3]._que).length > 0 @_que[k3]._c_ql += (@_que[k3]._que).length * (@_p_time - @_que[k3]._ql_t) # 待ち行列長にその長さが継続した時間を乗じた値の累計 n = @_que[k3]._que.delete_at(0) # 待ち行列の先頭にいる客 @_que[k3]._ql_t = @_p_time # 現在の待ち行列長になった時刻 x1 = @_p_time - @_cus[n]._time @_que[k3]._c_wt += x1 # 待ち時間の累計 if x1 > @_que[k3]._max_wt @_que[k3]._max_wt = x1 # 最大待ち時間 end for i1 in 0 ... @_que[k3]._m k4 = @_que[k3]._out_n[i1] # 窓口 if @_ent[k4]._service == 0 @_ent[k4]._service = n # 窓口の客番号 @_ent[k4]._busy_t = @_p_time # 窓口がふさがった時刻 if @_ent[k4]._s_t == 0 # 窓口のサービス終了時刻 @_ent[k4]._end_time = @_p_time - @_ent[k4]._mean * Math.log(rand(0)) else @_ent[k4]._end_time = @_p_time + @_ent[k4]._mean end @_cus[n]._state1 = 1 # 客の状態変更(サービス中) @_cus[n]._state2 = k4 # 客の状態変更(窓口番号) break end end end end ########################## # 統計量の計算と最終出力 ########################## def Output() # System printf("全客数 %d", @_nc) printf(" 最大系内客数 %d 最大滞在時間 %.3f\n", @_max_c, @_max_sys) printf("平均系内客数 %.3f", (@_c_now_c / @_p_time)) printf(" 平均滞在時間 %.3f", (@_c_sys / @_nc)) printf(" 終了時間 %.3f\n", @_p_time) # Entity for i1 in 0 ... @_ent.length e = @_ent[i1] printf("Entity " + e._name) printf(" 稼働率 %.3f\n", (e._c_busy / @_p_time)) end # Queue for i1 in 0 ... @_que.length q = @_que[i1] printf("Queue " + q._name) printf(" 客数 %d", q._nc) printf(" 最大待ち行列長 %d", q._max_q_l) printf(" 最大待ち時間 %.3f\n", q._max_wt) printf(" 平均待ち行列長 %.3f", (q._c_ql / @_p_time)) printf(" 平均待ち時間 %.3f\n", (q._c_wt / q._nc)) end end end # 入り口 print("入り口(Inlet)の数は? ") n_i = Integer(gets()) inl = Array.new() for i1 in 0 ... n_i print(String(i1+1) + " 番目の入り口(Inlet)\n") print(" 名前は? ") name1 = gets().strip() qq = Array.new() print(" 到着分布(=0:指数分布,=1:一定時間間隔,<0:指定,客数の負値)? ") n = Integer(gets()) if n == 0 print(" 到着時間間隔の平均値は? ") m_a = Float(gets()) elsif n == 1 print(" 到着時間間隔は? ") m_a = Float(gets()) else for i2 in range(0, -n) print(" 到着時間は? ") x = Float(gets()) qq.push(x) end end print(" 並ぶ待ち行列の名前は? ") name2 = gets().strip() inl_e = Inlet.new(name1, n, m_a, qq, name2) inl.push(inl_e) end # 待ち行列 print("待ち行列(Queue)の数は? ") n_q = Integer(gets()) que = Array.new() for i1 in 0 ... n_q print(String(i1+1) + " 番目の待ち行列(Queue)\n") print(" 名前は? ") name1 = gets().strip() print(" 入り口(0),または,窓口(n>0,窓口の数)から? ") n = Integer(gets()) inn = Array.new() if n == 0 print(" 入り口の名前は? ") name2 = gets().strip() inn.push(name2) else for i2 in 0 ... n print(" " + String(i2+1) + " 番目の窓口の名前は? ") name3 = gets().strip() inn.push(name3) end end print(" 処理する窓口の数は? ") m = Integer(gets()) out = Array.new() for i2 in 0 ... m print(" " +String(i2+1) + " 番目の窓口の名前は? ") name4 = gets().strip() out.push(name4) end que_e = Queue.new(name1, n, inn, m, out) que.push(que_e) end # 窓口 print("窓口(Entity)の数は? ") n_e = Integer(gets()) ent = Array.new() for i1 in 0 ... n_e print(String(i1+1) + " 番目の窓口(Entity)\n") print(" 名前は? ") name1 = gets().strip() print(" サービス分布(=0:指数分布,=1:一定時間)? ") s_t = Integer(gets()) if s_t == 0 print(" サービス時間の平均値は? ") m_s = Float(gets()) else print(" サービス時間は? ") m_s = Float(gets()) end print(" 待ち行列(入力)の名前は? ") name2 = gets().strip() print(" 終了後,外部(0),または,待ち行列(1)? ") sw = Integer(gets()) name3 = "" if sw > 0 print(" 待ち行列(出力)の名前は? ") name3 = gets().strip() end ent_e = Entity.new(name1, s_t, m_s, name2, sw, name3) ent.push(ent_e) end # 全体の制御を行うクラス print("シミュレーション終了時間? ") stp = Float(gets()) base = Q_base.new(inl, que, ent, stp) # 全体の制御を行うクラス # 実行 base.Control() # 出力 base.Output() =begin ------------入力例(簡単な場合)----------- 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 =end