#################################### # 遺伝的アルゴリズムによるTSPの解 # coded by Y.Suganuma #################################### ###################### # クラスSpeciesの定義 ###################### class Species ######################### # コンストラクタ # name : ファイル名 ######################### def initialize(name) # データの入力 inn = open(name, "r") s = inn.gets().split(" ") @_allele_u = Integer(s[1]) # 対立遺伝子上限 @_allele_l = Integer(s[3]) # 対立遺伝子下限 s = inn.gets().split(" ") @_max_len = Integer(s[1]) # 最大遺伝子長 @_min_len = Integer(s[3]) # 最小遺伝子長(負の時は,最大遺伝子長で固定) s = inn.gets().split(" ") @_dup_a = Integer(s[1]) # 遺伝子の重複 # =0 重複を許さない # =1 重複を許す @_dup_s = Integer(s[3]) # 個体の重複(同じ染色体の個体) # =0 重複を許さない # =1 重複を許す s = inn.gets().split(" ") @_size = Integer(s[1]) # 個体総数 @_max_ch = Integer(s[3]) # 子供の数の最大値 # データのチェック if @_size <= 0 print("***error 個体総数≦0 (Constructor)\n") end if @_max_ch < 0 print("***error 子供の数<0 (Constructor)\n") end if @_max_len <= 0 or @_min_len == 0 print("***error 遺伝子長≦0 (Constructor)\n") end if @_max_len < @_min_len print("***error 最大遺伝子長<最小遺伝子長 (Constructor)\n") end if @_allele_u <= @_allele_l print("***error 対立遺伝子上限≦対立遺伝子下限 (Constructor)\n") end kind = @_allele_u - @_allele_l + 1 if @_dup_a == 0 and @_max_len > kind print("***error 遺伝子の重複を防ぐことはできない (Constructor)\n") end # 領域の確保 num = @_size + @_max_ch @_ind = Array.new(num) # 集団(個体の集まり) for i1 in 0 ... num @_ind[i1] = Array.new(@_max_len) end @_edge = Array.new(@_max_len) # エッジ組み替え交叉用ワークエリア for i1 in 0 ... @_max_len @_edge[i1] = Array.new(5) end @_pi = Array.new(@_max_len) # 適応度 for i1 in 0 ... @_max_len @_pi[i1] = Array.new(5) end @_pi = Array.new(num) # 適応度 @_ro = Array.new(num) # ルーレット板 @_len = Array.new(num) # 各個体の遺伝子長 @_kou1 = Array.new(@_max_len) # 交叉・突然変異用作業場所1 @_kou2 = Array.new(@_max_len) # 交叉・突然変異用作業場所2 @_s_w = Array.new(num) # 淘汰用指標(選択された回数) @_pi_w = Array.new(num) # 適応度計算指標 # =0 未使用 # =1 適応度計算前(突然変異はこの個体だけに適用) # =2 適応度計算済み(交叉時に親とみなす) @_max = -999999999 # 最大適応度 @_mean = 0.0 # 平均適応度 @_max_n = -1 # 最大適応度の個体番号 end ################################################## # 場所を探す # n >=0 : n番目の親を捜す # -1 : 空いている場所を探す # return : 親の場所,または,空いている場所 # (存在しないときは負の値) ################################################## def Position(n) k = -1 sw = 0 # 空いている場所を探す if n < 0 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 0 k = i1 break end end if k < 0 print("***error 空いている場所がない --Position--\n") end # n番目の親(pi_w[i]=2)を捜す else for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 2 k += 1 if k == n sw = 1 k = i1 break end end end end return k end ################################################################### # 個体の選択 # method : 選択方法 # =-1 ランダム(default) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # bias : α,または,method=2の場合は初期値(default=0) # step : β(default=1) # return : 個体番号 ################################################################### def Select(method, bias, step) sum = 0.0 # ルーレット板の用意 # ランダム if method == -1 n = 0 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 n += 1 end end sum = 1.0 / n for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 @_ro[i1] = sum end end # 評価値をそのまま利用 elsif method == 0 n = 0 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 sum += @_pi[i1] n += 1 end end if sum.abs() > 1.0e-10 sum = 1.0 / sum.abs() for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 @_ro[i1] = @_pi[i1] * sum end end else sum = 1.0 / n for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 @_ro[i1] = sum end end end # 最小値からの差 elsif method == 1 min = -1 n = 0 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 n += 1 if min < 0 or @_pi[i1] < @_pi[min] min = i1 end end end for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 @_ro[i1] = @_pi[i1] - @_pi[min] if @_ro[i1] < bias @_ro[i1] = bias end sum += @_ro[i1] end end if sum > 1.0e-10 sum = 1.0 / sum for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 @_ro[i1] *= sum end end else sum = 1.0 / n for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 @_ro[i1] = sum end end end # 線形化 elsif method == 2 n = 0 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 @_ro[i1] = -1.0 n += 1 else @_ro[i1] = 1.0 end end sw = 0 sum = bias while sw == 0 min = -1 for i1 in 0 ... @_size+@_max_ch if @_ro[i1] < 0.0 and (min < 0 or @_pi[i1] < @_pi[min]) min = i1 end end if min < 0 sw = 1 else @_ro[min] = sum sum += @_step end end sum = 1.0 / (0.5 * (2.0 * bias + step * (n - 1)) * n) for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 @_ro[i1] *= sum end end end sum = 0.0 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 sum += @_ro[i1] @_ro[i1] = sum end end # 選択 x = rand(0) sw = 0 k = 0 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 if x <= @_ro[i1] sw = 1 k = i1 break end end end return k end ################### # 標準的な初期設定 ################### def Init_std() # 初期設定 for i1 in 0 ... @_size+@_max_ch if i1 < @_size @_pi_w[i1] = 1 # 適応度の計算前 else @_pi_w[i1] = 0 # 未使用 end end # 遺伝子の決定 for i1 in 0 ... @_size sw1 = 0 length = 0 while sw1 == 0 # 遺伝子長の決定 if @_min_len < 0 length = @_max_len else length = Integer(rand(0) * (@_max_len - @_min_len + 1) + @_min_len) if length > @_max_len length = @_max_len end end @_len[i1] = length # 遺伝子の決定 for i2 in 0 ... length sw2 = 0 while sw2 == 0 lid = Integer(rand(0) * (@_allele_u - @_allele_l + 1) + @_allele_l) if lid > @_allele_u lid = @_allele_u end @_ind[i1][i2] = lid # 重複遺伝子のチェック sw2 = 1 if @_dup_a == 0 for i3 in 0 ... i2 if lid == @_ind[i1][i3] sw2 = 0 break end end end end end # 重複個体のチェック sw1 = 1 if @_dup_s == 0 for i2 in 0 ... i1 if @_len[i1] == @_len[i2] sw2 = 0 for i3 in 0 ... @_len[i1] if @_ind[i1][i3] != @_ind[i2][i3] sw2 = 1 break end end if sw2 == 0 sw1 = 0 break end end end end end end end #################################################### # 標準的な出力 # sw : 出力レベル # =0 : 最終出力だけ # n>0 : n世代毎に出力(負はファイル) # out_m : 出力方法 # =0 : すべての個体を出力 # =1 : 最大適応度の個体だけを出力 # gen : 現在の世代番号 # name : 出力ファイル名 #################################################### def Out_std(sw, out_m, gen, name) k = 0 pr = -1 if sw >= 0 print(" 出力先は(0:出力なし,n:画面にn個づつ,-1:ファイル)? ") pr = Integer($stdin.gets()) end if pr != 0 # 出力先の決定と評価値の出力 if pr > 0 out = $stdout $stdin.gets() else now = String(Time.now) out = open(name, "a") out.print("***世代 " + String(gen) + " 適応度 max " + String(@_max) + " (" + String(@_max_n) + ") mean " + String(@_mean) + " 時間 " + now + "\n") # 詳細出力 end for i1 in 0 ... @_size+@_max_ch if (@_pi_w[i1] > 1) and (out_m == 0 or (out_m == 1 and i1 == @_max_n)) out.print(String(i1) + " allele") for i2 in 0 ... @_len[i1] out.print(" " + String(@_ind[i1][i2])) end out.print(" value " + String(@_pi[i1]) + "\n") if pr > 0 k += 1 if k == pr $stdin.gets() k = 0 end end end end if pr < 0 out.close() end end end ################################################################### # 交叉(親のコピー) # method : =2 有性(2つの親から2つの子供)(default) # =1 1つの親から1つの子供 # pair : method=2 の時は親のペア数(default=max_ch/2) # method=1 の時は親の数(=子供の数) # k_method : 選択方法 # =-1 ランダム(default) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # k_bias : α,または,method=2の場合は初期値(default=0) # k_step : β(default=1) ################################################################### def C_copy(method = 2, pair = 0, k_method = -1, k_bias = 0.0, k_step = 1.0) # 初期設定とデータチェック if method != 1 method = 2 end if pair <= 0 if method == 2 pair = Integer(@_max_ch / 2) else pair = @_max_ch end else if method == 2 and 2*pair > @_max_ch or method == 1 and pair > @_max_ch print("***error 子供が多すぎる (C_copy)\n") end end # 実行 for i1 in 0 ... pair # 親の選択 p1 = Select(k_method, k_bias, k_step) p2 = p1 sw = 0 while sw == 0 p2 = Select(k_method, k_bias, k_step) if p1 != p2 sw = 1 end end # コピー for i2 in 0 ... method p = p2 if i2 == 0 p = p1 end k = Position(-1) @_len[k] = @_len[p] @_pi_w[k] = 1 for i3 in 0 ... @_len[k] @_ind[k][i3] = @_ind[p][i3] end end end end ################################################################### # 交叉(多点交叉) # kosa : 交叉確率 # k_point : 交叉点の数(default=1) # (負の時は,1から-k_point間のランダム) # k_vr : =0 両親とも同じ位置で交叉(default) # =1 両親が異なる位置で交叉(遺伝子長は可変) # k_method : 選択方法 # =-1 ランダム(default) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # k_bias : α,または,method=2の場合は初期値(default=0) # k_step : β(default=1) ################################################################### def C_point(kosa, k_point = 1, k_vr = 0, k_method = -1, k_bias = 0.0, k_step = 1.0) mn = 0 # 初期設定とデータのチェック pair = Integer(@_max_ch / 2) if @_dup_a == 0 print("***error 交叉方法が不適当 (C_point)\n") end abs_p = k_point.abs() if abs_p == 0 or abs_p > @_max_len-1 or (@_min_len > 0 and abs_p > @_min_len-1) print("***error 交叉点の数が不適当 (C_point)\n") end if k_vr > 0 and @_min_len < 0 print("***error 遺伝子長は可変でなければならない (C_point)\n") end # 交叉 num = k_point for i1 in 0 ... pair # 交叉しない場合 if rand(0) > kosa C_copy(2, 1) # 交叉する場合 else # 親の選択 p1 = Select(k_method, k_bias, k_step) p2 = p1 sw = 0 while sw == 0 p2 = Select(k_method, k_bias, k_step) if p1 != p2 sw = 1 end end # 交叉位置の数の決定 if k_point < 0 num = Integer(rand(0) * abs_p + 1) if num > abs_p num = abs_p end end # 交叉位置の決定(点の後ろで交叉) for i2 in 0 ... num # 親1の交叉位置 sw = 0 while sw == 0 sw = 1 @_kou1[i2] = Integer(rand(0) * (@_len[p1] - 1)) if @_kou1[i2] > @_len[p1]-2 @_kou1[i2] = @_len[p1] - 2 end if k_vr == 0 and @_kou1[i2] > @_len[p2]-2 @_kou1[i2] = @_len[p2] - 2 end for i3 in 0 ... i2 if @_kou1[i3] == @_kou1[i2] sw = 0 break end end end # 親2の交叉位置 if k_vr > 0 sw = 0 while sw == 0 sw = 1 @_kou2[i2] = Integer(rand(0) * (@_len[p2] - 1)) if @_kou2[i2] > @_len[p2]-2 @_kou2[i2] = @_len[p2] - 2 end for i3 in 0 ... i2 if @_kou2[i3] == @_kou2[i2] sw = 0 break end end end end end # 交叉の実行 # 親1のt11からt12を子1のc1へコピー # 親2のt21からt22を子2のc2へコピー # 次は, # 親1のt11からt12を子2のc2へコピー # 親2のt21からt22を子1のc1へコピー # ・・・・・ c1 = 0 c2 = 0 t11 = 0 t21 = 0 # 遺伝子長 k1 = Position(-1) @_pi_w[k1] = 1 @_len[k1] = @_len[p1] k2 = Position(-1) @_pi_w[k2] = 1 @_len[k2] = @_len[p2] for i2 in 0 ... num+1 # 次の交叉位置を求める if i2 == num # 最後 t12 = @_len[p1] t22 = @_len[p2] else # 親1 t12 = @_max_len for i3 in 0 ... num if @_kou1[i3] >= 0 and @_kou1[i3] <= t12 t12 = @_kou1[i3] mn = i3 end end @_kou1[mn] = -1 t12 += 1 # 親2 if k_vr == 0 t22 = t12 else t22 = @_max_len for i3 in 0 ... num if @_kou2[i3] >= 0 and @_kou2[i3] <= t22 t22 = @_kou2[i3] mn = i3 end end @_kou2[mn] = -1 t22 += 1 end end # 指定箇所のコピー for i3 in t11 ... t12 if i2%2 == 0 if c1 < @_max_len @_ind[k1][c1] = @_ind[p1][i3] c1 += 1 end else if c2 < @_max_len @_ind[k2][c2] = @_ind[p1][i3] c2 += 1 end end end for i3 in t21 ... t22 if i2%2 == 0 if c2 < @_max_len @_ind[k2][c2] = @_ind[p2][i3] c2 += 1 end else if c1 < @_max_len @_ind[k1][c1] = @_ind[p2][i3] c1 += 1 end end end # 交叉位置の移動 t11 = t12 t21 = t22 end end end end ################################################################### # 交叉(一様交叉.[0,1]を等確率で発生させ,1であれば, # 親1,0であれば親2の遺伝子を子1が受け継ぐ) # kosa : 交叉確率 # k_method : 選択方法 # =-1 ランダム(default) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # k_bias : α,または,method=2の場合は初期値(default=0) # k_step : β(default=1) ################################################################### def C_uniform(kosa, k_method = -1, k_bias = 0.0, k_step = 1.0) # 初期設定とデータのチェック pair = Integer(@_max_ch / 2) if @_dup_a == 0 print("***error 交叉方法が不適当 (C_uniform)\n") end if @_min_len > 0 print("***error 遺伝子長は固定長でなければならない (C_uniform)\n") end # 交叉 for i1 in 0 ... pair # 交叉しない場合 if rand(0) > kosa C_copy(2, 1) # 交叉する場合 else # 親の選択 p1 = Select(k_method, k_bias, k_step) p2 = p1 sw = 0 while sw == 0 p2 = Select(k_method, k_bias, k_step) if p1 != p2 sw = 1 end end # 遺伝子長 k1 = Position(-1) @_pi_w[k1] = 1 @_len[k1] = @_len[p1] k2 = Position(-1) @_pi_w[k2] = 1 @_len[k2] = @_len[p2] # 交叉 for i2 in 0 ... @_len[p1] if rand(0) > 0.5 @_ind[k1][i2] = @_ind[p1][i2] @_ind[k2][i2] = @_ind[p2][i2] else @_ind[k1][i2] = @_ind[p2][i2] @_ind[k2][i2] = @_ind[p1][i2] end end end end end ################################################################### # 交叉(平均化交叉.2つの親の平均値を受け継ぐ) # kosa : 交叉確率 # k_method : 選択方法 # =-1 ランダム(default) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # k_bias : α,または,method=2の場合は初期値(default=0) # k_step : β(default=1) ################################################################### def C_mean(kosa, k_method = -1, k_bias = 0.0, k_step = 1.0) # 初期設定とデータのチェック if @_min_len > 0 print("***error 遺伝子長は固定長でなければならない (C_mean)\n") end # 交叉 for i1 in 0 ... @_max_ch # 交叉しない場合 if rand(0) > kosa C_copy(1, 1) # 交叉する場合 else # 親の選択 p1 = Select(k_method, k_bias, k_step) p2 = p1 sw = 0 while sw == 0 p2 = Select(k_method, k_bias, k_step) if p1 != p2 sw = 1 end end # 遺伝子長 k = Position(-1) @_len[k] = @_len[p1] @_pi_w[k] = 1 # 交叉 for i2 in 0 ... @_len[k] @_ind[k][i2] = Integer((@_ind[p1][i2] + @_ind[p2][i2]) / 2) end end end end ################################################################### # 交叉(循環交叉.ランダムに1点を選択し,その位置にある遺伝子を # そのまま各子供が選択する.その位置にある親2(1)の遺伝 # 子を,その遺伝子の親1(2)の場所に,子1(2)が受け継 # ぐ(ただし,doubleの場合は,この手続きをのぞく).この手 # 続きを,すでに受け継いだ遺伝子の位置が選択されるまで繰り # 返し,残りの遺伝子については,子1(2)は,親2(1)の # 遺伝子をその順番通りに受け継ぐ) # 2 4 1 3 6 5 + + 1 + + 5 3 2 1 4 6 5 # * → → # 3 2 5 4 1 6 + + 5 + 1 + 2 4 5 3 1 6 # kosa : 交叉確率 # k_method : 選択方法 # =-1 ランダム(default) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # k_bias : α,または,method=2の場合は初期値(default=0) # k_step : β(default=1) ################################################################### def C_cycle(kosa, k_method = -1, k_bias = 0.0, k_step = 1.0) # 初期設定とデータのチェック pair = Integer(@_max_ch / 2) if @_dup_a != 0 print("***error 交叉方法が不適当 (C_cycle)\n") end if @_min_len > 0 print("***error 遺伝子長は固定長でなければならない (C_cycle)\n") end # 交叉 for i1 in 0 ... pair # 交叉しない場合 if rand(0) > kosa C_copy(2, 1) # 交叉する場合 else # 親の選択 p1 = Select(k_method, k_bias, k_step) p2 = p1 sw = 0 while sw == 0 p2 = Select(k_method, k_bias, k_step) if p1 != p2 sw = 1 end end # 初期設定 for i2 in 0 ... @_len[p1] @_kou1[i2] = 0 @_kou2[i2] = 0 end # 遺伝子長 k1 = Position(-1) @_pi_w[k1] = 1 @_len[k1] = @_len[p1] k2 = Position(-1) @_pi_w[k2] = 1 @_len[k2] = @_len[p2] # 交叉 sw = 0 while sw == 0 sw = 1 p = Integer(rand(0) * @_len[p1]) if p >= @_len[p1] p = @_len[p1] - 1 end if @_kou1[p] == 0 and @_kou2[p] == 0 @_kou1[p] = 1 @_kou2[p] = 1 @_ind[k1][p] = @_ind[p1][p] @_ind[k2][p] = @_ind[p2][p] for i2 in 0 ... @_len[p1] if @_ind[p2][p] == @_ind[p1][i2] @_ind[k1][i2] = @_ind[p1][i2] @_kou1[i2] = 1 sw = 0 break end end sw = 1 for i2 in 0 ... @_len[p2] if @_ind[p1][p] == @_ind[p2][i2] @_ind[k2][i2] = @_ind[p2][i2] @_kou2[i2] = 1 sw = 0 break end end end end sw = 0 i2 = 0 i3 = 0 while sw == 0 while sw == 0 and i2 < @_len[p1] if @_kou1[i2] == 0 sw = 1 else i2 += 1 end end sw = 0 while sw == 0 and i3 < @_len[p2] if @_kou2[i3] == 0 sw = 1 else i3 += 1 end end if i2 < @_len[p1] and i3 < @_len[p2] @_ind[k1][i2] = @_ind[p2][i3] @_ind[k2][i3] = @_ind[p1][i2] sw = 0 i2 += 1 i3 += 1 else sw = 1 end end end end end ################################################################### # 交叉(部分的交叉.ランダムに1点を選択し,その位置にある親1と # 親2の遺伝子を取り出す.次に,親1と親2の染色体上で,こ # の2つの遺伝子の位置を交換する.この操作を,選択した点よ # り右にあるすべての遺伝子に対して実施する # 2 4 1 3 6 5 2 4 5 3 6 1 # * → → ・・・・・ # 3 2 5 4 1 6 3 2 1 4 5 6 # kosa : 交叉確率 # k_method : 選択方法 # =-1 ランダム(default) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # k_bias : α,または,method=2の場合は初期値(default=0) # k_step : β(default=1) #******************************************************************/ def C_part(kosa, k_method = -1, k_bias = 0.0, k_step = 1.0) # 初期設定とデータのチェック pair = Integer(@_max_ch / 2) if @_dup_a != 0 print("***error 交叉方法が不適当 (C_part)\n") end if @_min_len > 0 print("***error 遺伝子長は固定長でなければならない (C_part)\n") end # 交叉 for i1 in 0 ... pair # 交叉しない場合 if rand(0) > kosa C_copy(2, 1) # 交叉する場合 else # 親の選択 p1 = Select(k_method, k_bias, k_step) p2 = p1 sw = 0 while sw == 0 p2 = Select(k_method, k_bias, k_step) if p1 != p2 sw = 1 end end # 遺伝子長 k1 = Position(-1) @_pi_w[k1] = 1 @_len[k1] = @_len[p1] k2 = Position(-1) @_pi_w[k2] = 1 @_len[k2] = @_len[p2] # 交叉 p = Integer(rand(0) * @_len[p1]) if p >= @_len[p1] p = @_len[p1] - 1 end for i2 in 0 ... @_len[p1] @_ind[k1][i2] = @_ind[p1][i2] @_ind[k2][i2] = @_ind[p2][i2] end for i2 in p ... @_len[p1] sw = 0 lv = @_ind[k1][i2] for i3 in 0 ... @_len[p1] if @_ind[k2][i2] == @_ind[k1][i3] @_ind[k1][i2] = @_ind[k1][i3] @_ind[k1][i3] = lv sw = 1 break end end sw = 0 for i3 in 0 ... @_len[p1] if lv == @_ind[k2][i3] @_ind[k2][i3] = @_ind[k2][i2] @_ind[k2][i2] = lv sw = 1 break end end end end end end ################################################################### # 交叉(順序交叉.ランダムに切れ目を決定し,子1に対し,切れ目の # 左側では,親1の遺伝子をそのまま受け継ぎ,右側では,親1 # の遺伝子を親2の遺伝子の出現順序に並べ替える. # 2 4 1 3 6 5 2 4 1 3 5 6 # * → # 3 2 5 4 1 6 3 2 5 4 1 6 # kosa : 交叉確率 # k_method : 選択方法 # =-1 ランダム(default) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # k_bias : α,または,method=2の場合は初期値(default=0) # k_step : β(default=1) ################################################################### def C_seq(kosa, k_method = -1, k_bias = 0.0, k_step = 1.0) # 初期設定とデータのチェック pair = Integer(@_max_ch / 2) if @_dup_a != 0 print("***error 交叉方法が不適当 (C_seq)") end if @_min_len > 0 print("***error 遺伝子長は固定長でなければならない (C_seq)") end # 交叉 for i1 in 0 ... pair # 交叉しない場合 if rand(0) > kosa C_copy(2, 1) # 交叉する場合 else # 親の選択 p1 = Select(k_method, k_bias, k_step) p2 = p1 sw = 0 while sw == 0 p2 = Select(k_method, k_bias, k_step) if p1 != p2 sw = 1 end end # 遺伝子長 k1 = Position(-1) @_pi_w[k1] = 1 @_len[k1] = @_len[p1] k2 = Position(-1) @_pi_w[k2] = 1 @_len[k2] = @_len[p2] # 交叉 p = Integer(rand(0) * (@_len[p1] - 1)) if p >= @_len[p1]-1 p = @_len[p1] - 2 end for i2 in 0 ... p+1 @_ind[k1][i2] = @_ind[p1][i2] @_ind[k2][i2] = @_ind[p2][i2] end pp = 0 for i2 in p+1 ... @_len[p1] sw = 0 i3 = pp while i3 < @_len[p2] and sw == 0 i4 = p + 1 while i4 < @_len[p1] and sw == 0 if @_ind[p2][i3] == @_ind[p1][i4] sw = 1 pp = i3 + 1 @_ind[k1][i2] = @_ind[p1][i4] end i4 += 1 end i3 += 1 end end pp = 0 for i2 in p+1 ... @_len[p2] sw = 0 i3 = pp while i3 < @_len[p1] and sw == 0 i4 = p + 1 while i4 < @_len[p2] and sw == 0 if @_ind[p1][i3] == @_ind[p2][i4] sw = 1 pp = i3 + 1 @_ind[k2][i2] = @_ind[p2][i4] end i4 += 1 end i3 += 1 end end end end end ################################################################### # 交叉(一様順序交叉.位置の集合をランダムに選択し,一方の親の選 # 択された位置における遺伝子の順序に従って,他の親の遺伝子 # を並べ替える # 2 4 1 3 6 5 2 4 1 3 6 5 # * * → # 3 2 5 4 1 6 4 2 5 3 1 6 # kosa : 交叉確率 # k_method : 選択方法 # =-1 ランダム(default) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # k_bias : α,または,method=2の場合は初期値(default=0) # k_step : β(default=1) ################################################################### def C_useq(kosa, k_method = -1, k_bias = 0.0, k_step = 1.0) # 初期設定とデータのチェック pair =Integer(@_max_ch / 2) if @_dup_a != 0 print("***error 交叉方法が不適当 (C_useq)\n") end if @_min_len > 0 print("***error 遺伝子長は固定長でなければならない (C_useq)\n") end # 交叉 for i1 in 0 ... pair # 交叉しない場合 if rand(0) > kosa C_copy(2, 1) # 交叉する場合 else # 親の選択 p1 = Select(k_method, k_bias, k_step) p2 = p1 sw = 0 while sw == 0 p2 = Select(k_method, k_bias, k_step) if p1 != p2 sw = 1 end end # 遺伝子長 k1 = Position(-1) @_pi_w[k1] = 1 @_len[k1] = @_len[p1] k2 = Position(-1) @_pi_w[k2] = 1 @_len[k2] = @_len[p2] # 交叉 for i2 in 0 ... @_len[p1] @_ind[k1][i2] = @_ind[p1][i2] @_ind[k2][i2] = @_ind[p2][i2] if rand(0) < 0.5 @_kou1[i2] = 0 else @_kou1[i2] = 1 end end p = 0 for i2 in 0 ... @_len[p1] if @_kou1[i2] > 0 sw = 0 i3 = p while i3 < @_len[p2] and sw == 0 i4 = 0 while i4 < @_len[p1] and sw == 0 if @_ind[p2][i3] == @_ind[p1][i4] and @_kou1[i4] > 0 sw = 1 p = i3 + 1 @_ind[k1][i2] = @_ind[p1][i4] end i4 += 1 end i3 += 1 end end end p = 0 for i2 in 0 ... @_len[p3] if @_kou1[i2] > 0 sw = 0 i3 = p while i3 < @_len[p1] and sw == 0 i4 = 0 while i4 < @_len[p2] and sw == 0 if @_ind[p1][i3] == @_ind[p2][i4] and @_kou1[i4] > 0 sw = 1 p = i3 + 1 @_ind[k2][i2] = @_ind[p2][i4] end i4 += 1 end i3 += 1 end end end end end end ################################################################### # 交叉(一様位置交叉.位置の集合をランダムに選択し,一方の親の選 # 択された位置における遺伝子の位置に,他の親の同じ遺伝子を # 配置する.残りの遺伝子は,親と同じ順序に配置する. # 2 4 1 3 6 5 + + 5 + 1 + 2 4 5 3 1 6 # * * → → # 3 2 5 4 1 6 + + 1 + 6 + 3 2 1 5 6 4 # kosa : 交叉確率 # k_method : 選択方法 # =-1 ランダム(default) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # k_bias : α,または,method=2の場合は初期値(default=0) # k_step : β(default=1) ################################################################### def C_upos(kosa, k_method = -1, k_bias = 0.0, k_step = 1.0) # 初期設定とデータのチェック pair = Integer(@_max_ch / 2) if @_dup_a != 0 print("***error 交叉方法が不適当 (C_upos)\n") end if @_min_len > 0 print("***error 遺伝子長は固定長でなければならない (C_upos)\n") end # 交叉 for i1 in 0 ... pair # 交叉しない場合 if rand(0) > kosa C_copy(2, 1) # 交叉する場合 else # 親の選択 p1 = Select(k_method, k_bias, k_step) p2 = p1 sw = 0 while sw == 0 p2 = Select(k_method, k_bias, k_step) if p1 != p2 sw = 1 end end # 遺伝子長 k1 = Position(-1) @_pi_w[k1] = 1 @_len[k1] = @_len[p1] k2 = Position(-1) @_pi_w[k2] = 1 @_len[k2] = @_len[p2] # 交叉 for i2 in 0 ... @_len[p1] @_kou1[i2] = 1 if rand(0) < 0.5 @_kou1[i2] = 1 end if @_kou1[i2] > 0 @_ind[k1][i2] = @_ind[p2][i2] @_ind[k2][i2] = @_ind[p1][i2] end end p = 0 for i2 in 0 ... @_len[p1] sw = 0 for i3 in 0 ... @_len[p1] if @_kou1[i3] > 0 and @_ind[p1][i2] == @_ind[k1][i3] sw = 1 break end end if sw == 0 for i3 in p ... @_len[p1] if @_kou1[i3] == 0 @_ind[k1][i3] = @_ind[p1][i2] p = i3 + 1 sw = 1 break end end end end p = 0 for i2 in 0 ... @_len[p2] sw = 0 for i3 in 0 ... @_len[p2] if @_kou1[i3] > 0 and @_ind[p2][i2] == @_ind[k2][i3] sw = 1 break end end if sw == 0 for i3 in p ... @_len[p2] if @_kou1[i3] == 0 @_ind[k2][i3] = @_ind[p2][i2] p = i3 + 1 sw = 1 break end end end end end end end ################################################################### # 交叉(エッジ組み替え交叉.以下の手順に従って行う.対立遺伝子は # 0~(max_len-1)である必要がある) # (0) エッジマップを作成する.エッジマップとは,2つの親 # を見て,ノードがどこに接続されているのかを表すもの # であり,例えば,2つの親が, # [A B C D E F] # [B D C A E F] # である場合は, # A B F C E # B A C D F # C B D A # D C E B # E D F A # F A E B # となる. # (1) 両親の2つの出発点の内1つで初期化する.ランダムま # たはステップ(4)の基準に従って選ぶ(現在のノード) # (2) エッジマップから,現在のノードを除く # (3) 現在のノードが接続先のノードを持っていたら,(4)に # 進む.さもなければ,(5)に進む # (4) 現在のノードが持っている接続先ノードの内,最も少な # い接続先ノードを持ったノードを選択し(同じ条件の場 # 合は,ランダム),それを現在のノードとし,(2)に進む # (5) 未接続のノードが残っていればランダムに選択し,(2)に # 戻る.さもなければ,終了する # kosa : 交叉確率 # k_method : 選択方法 # =-1 ランダム(default) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # k_bias : α,または,method=2の場合は初期値(default=0) # k_step : β(default=1) ################################################################### def C_edge(kosa, k_method = -1, k_bias = 0.0, k_step = 1.0) e = Array.new(2) k0 = 0 # 初期設定とデータのチェック pair = @_max_ch if @_dup_a != 0 print("***error 交叉方法が不適当 (C_edge)\n") end if @_min_len > 0 print("***error 遺伝子長は固定長でなければならない (C_edge)\n") end # 交叉 for i1 in 0 ... pair # 交叉しない場合 if rand(0) > kosa C_copy(1, 1) # 交叉する場合 else # 親の選択 p1 = Select(k_method, k_bias, k_step) p2 = p1 sw = 0 while sw == 0 p2 = Select(k_method, k_bias, k_step) if p1 != p2 sw = 1 end end # 遺伝子長 k = Position(-1) @_pi_w[k] = 1 @_len[k] = @_len[p1] # エッジマップの初期化 for i2 in 0 ... @_len[k] @_edge[i2][0] = 0 for i3 in 1 ... 5 @_edge[i2][i3] = -1 end end # 交叉 # エッジマップの作成 for i2 in 0 ... @_len[k] sw = 0 for i3 in 0 ... @_len[k] if i2 == @_ind[p1][i3] sw = 1 if i3 == 0 e[0] = @_ind[p1][@_len[k]-1] e[1] = @_ind[p1][1] else if i3 == @_len[k]-1 e[0] = @_ind[p1][i3-1] e[1] = @_ind[p1][0] else e[0] = @_ind[p1][i3-1] e[1] = @_ind[p1][i3+1] end end for i4 in 0 ... 2 @_edge[i2][0] += 1 @_edge[i2][@_edge[i2][0]] = e[i4] end break end end sw = 0 for i3 in 0 ... @_len[k] if i2 == @_ind[p2][i3] sw = 1 if i3 == 0 e[0] = @_ind[p2][@_len[k]-1] e[1] = @_ind[p2][1] else if i3 == @_len[k]-1 e[0] = @_ind[p2][i3-1] e[1] = @_ind[p2][0] else e[0] = @_ind[p2][i3-1] e[1] = @_ind[p2][i3+1] end end for i4 in 0 ... 2 sw = 1 for i5 in 1 ... @_edge[i2][0]+1 if @_edge[i2][i5] == e[i4] sw = 2 break end end if sw == 1 @_edge[i2][0] += 1 @_edge[i2][@_edge[i2][0]] = e[i4] end end break end end end # 交叉の実行 # 出発点の決定 k1 = @_ind[p1][0] k2 = @_ind[p2][0] if @_edge[k1][0] == @_edge[k2][0] kk = k1 if rand(0) > 0.5 kk = k2 end else kk = k2 if @_edge[k1][0] < @_edge[k2][0] kk = k2 end end @_ind[k][0] = kk p = 1 while p < @_len[k] # ノードの除去 for i2 in 0 ... @_len[k] sw = 0 if @_edge[i2][0] > 0 for i3 in 1 ... 5 if @_edge[i2][i3] == kk sw = 1 @_edge[i2][i3] = -1 @_edge[i2][0] -= 1 break end end end end # 次の現在ノードの選択 min = 10 num = 0 for i2 in 1 ... 5 if @_edge[kk][i2] >= 0 k1 = @_edge[kk][i2] if @_edge[k1][0] >= 0 and @_edge[k1][0] < min num = 1 min = @_edge[k1][0] k0 = k1 else if @_edge[k1][0] == min num += 1 end end end end if num > 1 k1 = Integer(rand(0) * num) + 1 if k1 > num k1 = num end k2 = 0 k0 = -1 i2 = 1 while i2 <= 4 and k0 < 0 if @_edge[kk][i2] >= 0 if @_edge[@_edge[kk][i2]][0] == min k2 += 1 if k1 == k2 k0 = @_edge[kk][i2] end end end i2 += 1 end else if num <= 0 num = 0 for i2 in 0 ... @_len[k] if i2 != kk and @_edge[i2][0] >= 0 num += 1 end end if num <= 0 print("***error invalid data (C_edge)\n") else k1 = Integer(rand(0) * num) + 1 if k1 > num k1 = num end k2 = 0 k0 = -1 i2 = 0 while i2 < @_len[k] and k0 < 0 if i2 != kk and @_edge[i2][0] >= 0 k2 += 1 if k1 == k2 k0 = i2 end end i2 += 1 end end end end @_edge[kk][0] = -1 @_ind[k][p] = k0 kk = k0 p += 1 end end end end ############################################################## # 交叉(サブツアー交叉.2点交叉の拡張である.ただし,相手に # 同じ遺伝子のグループがない限り実行されない.たとえば # ***abcd** # *cdab**** # のような両親の時実行され,以下の4つの子供が生成され # る) # ***cdab** # *abcd**** # ***badc** # *dcba**** # 最大,4*交叉回数*個体総数*(個体総数-1) 個の子 # 供が生成される可能性があるので,子供の数としてこの値 # 以上のデータを入力しておく必要がある. # kosa : 交叉確率 # count : 1つのペアーに対する交差回数(default=10) ############################################################## def C_sub(kosa, count = 10) t22 = 0 # 初期設定とデータのチェック if (4*count*@_size*(@_size-1)) > @_max_ch print("***error 子供が多すぎる (C_sub)\n") end # 交叉 for i1 in 0 ... @_size-1 # 親1 p1 = Position(i1) if p1 >= 0 for i2 in i1 ... @_size # 親2 p2 = Position(i2) if p2 >= 0 # 交叉しない場合 if rand(0) > kosa C_copy(2, 1) # 交叉する場合 else # 交叉回数の制御 for i3 in 0 ... count # 交叉位置の決定(点の後ろで交叉) # 親1の交叉位置 t11 = Integer(rand(0) * @_len[p1]) if t11 > (@_len[p1]-1) t11 = @_len[p1] - 1 end sw = 0 while sw == 0 t12 = Integer(rand(0) * @_len[p1]) if t12 > (@_len[p1]-1) t12 = @_len[p1] - 1 end if t12 != t11 sw = 1 end end if t11 > t12 k1 = t11 t11 = t12 t12 = k1 end # 親2の交叉位置 sw = 0 t21 = -1 i4 = 0 while i4 < @_len[p2] and t21 < 0 i5 = t11 while i5 <= t12 and t21 < 0 if @_ind[p2][i4] == @_ind[p1][i5] t21 = i4 end i5 += 1 end i4 += 1 end if t21 >= 0 t22 = t21 + t12 - t11 if t22 < @_len[p2] sw = 1 i4 = t21 + 1 while i4 <= t22 and sw > 0 sw = 0 i5 = t11 while i5 <= t12 and sw == 0 if @_ind[p2][i4] == @_ind[p1][i5] sw = 1 end i5 += 1 end i4 += 1 end end end # 交叉の実行 if sw > 0 k1 = Position(-1) @_pi_w[k1] = 1 @_len[k1] = @_len[p1] k2 = Position(-1) @_pi_w[k2] = 1 @_len[k2] = @_len[p1] k3 = Position(-1) @_pi_w[k3] = 1 @_len[k3] = @_len[p2] k4 = Position(-1) @_pi_w[k4] = 1 @_len[k4] = @_len[p2] for i4 in 0 ... t11 @_ind[k1][i4] = @_ind[p1][i4] @_ind[k2][i4] = @_ind[p1][i4] end for i4 in t11 ... t12+1 @_ind[k1][i4] = @_ind[p2][t21+i4-t11] @_ind[k2][i4] = @_ind[p2][t22-i4+t11] end for i4 in t12+1 ... @_len[p1] @_ind[k1][i4] = @_ind[p1][i4] @_ind[k2][i4] = @_ind[p1][i4] end for i4 in 0 ... t21 @_ind[k3][i4] = @_ind[p2][i4] @_ind[k4][i4] = @_ind[p2][i4] end for i4 in t21 ... t22+1 @_ind[k3][i4] = @_ind[p1][t11+i4-t21] @_ind[k4][i4] = @_ind[p1][t12-i4+t21] end for i4 in t22+1 ... @_len[p2] @_ind[k3][i4] = @_ind[p2][i4] @_ind[k4][i4] = @_ind[p2][i4] end end end end end end end end end ####################################### # 突然変異(対立遺伝子との置き換え) # pr : 突然変異率 ####################################### def M_alle(pr) # データのチェックと初期設定 if @_dup_a == 0 print("***error 突然変異方法が不適当 (M_alle)\n") end # 実行 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 1 for i2 in 0 ... @_len[i1] if rand(0) <= pr lid = Integer(rand(0) * (@_allele_u - @_allele_l + 1) + @_allele_l) if lid > @_allele_u lid = @_allele_u end if lid != @_ind[i1][i2] @_ind[i1][i2] = lid end end end end end end ###################################################################### # 突然変異(移動.2点を選択し,2番目の遺伝子を1番目の遺伝子の前に # 移動する) # pr : 突然変異率 ###################################################################### def M_move(pr) for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 1 and rand(0) <= pr # 位置の決定 # p1 p1 = Integer(rand(0) * @_len[i1]) if p1 >= @_len[i1] p1 = @_len[i1] - 1 end # p2 p2 = p1 sw = 0 while sw == 0 p2 = Integer(rand(0) * @_len[i1]) if p2 >= @_len[i1] p2 = @_len[i1] - 1 end if p2 != p1 sw = 1 end end # 実行 if p2 > p1 ld = @_ind[i1][p2] i2 = p2 while i2 > p1 @_ind[i1][i2] = @_ind[i1][i2-1] i2 -= -1 end @_ind[i1][p1] = ld else ld = @_ind[i1][p2] for i2 in p2 ... p1-1 @_ind[i1][i2] = @_ind[i1][i2+1] end @_ind[i1][p1-1] = ld end end end end ######################################################## # 突然変異(逆位.2点間の遺伝子順序を逆に並べ替える) # pr : 突然変異率 # wd : >0 : 幅を固定 # =0 : 幅をランダム(default) ######################################################## def M_inv(pr, wd = 0) for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 1 and rand(0) <= pr # 区間の決定 if wd == 0 p1 = Integer(rand(0) * @_len[i1]) if p1 >= @_len[i1] p1 = @_len[i1] - 1 end sw = 0 p2 = p1 while sw == 0 p2 = Integer(rand(0) * @_len[i1]) if p2 >= @_len[i1] p2 = @_len[i1] - 1 end if p2 != p1 sw = 1 end end if p1 > p2 p = p1 p1 = p2 p2 = p end else p1 = @_len[i1] while p1 > @_len[i1]-2 p1 = Integer(rand(0) * @_len[i1]) end p2 = p1 + wd - 1 if p2 >= @_len[i1] p2 = @_len[i1] - 1 end end # 実行 sw = 0 while sw == 0 lid = @_ind[i1][p1] @_ind[i1][p1] = @_ind[i1][p2] @_ind[i1][p2] = lid p1 += 1 p2 -= 1 if p1 >= p2 sw = 1 end end end end end ###################################################################### # 突然変異(スクランブル.2点間の遺伝子順序をランダムに並べ替える) # pr : 突然変異率 # wd : >0 : 幅を固定 # =0 : 幅をランダム(default) ###################################################################### def M_scram(pr, wd = 0) for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 1 and rand(0) <= pr # 区間の決定 if wd == 0 p1 = Integer(rand(0) * @_len[i1]) if p1 >= @_len[i1] p1 = @_len[i1] - 1 end sw = 0 p2 = p1 while sw == 0 p2 = Integer(rand(0) * @_len[i1]) if p2 >= @_len[i1] p2 = @_len[i1] - 1 end if p2 != p1 sw = 1 end end if p1 > p2 p = p1 p1 = p2 p2 = p end else p1 = @_len[i1] while p1 > @_len[i1]-2 p1 = Integer(rand(0) * @_len[i1]) end p2 = p1 + wd - 1 if p2 >= @_len[i1] p2 = @_len[i1] - 1 end end # 実行 for i2 in p1 ... p2+1 p = Integer(rand(0) * (p2 - p1 + 1) + p1) if p > p2 p = p2 end ld = @_ind[i1][i2] @_ind[i1][i2] = @_ind[i1][p] @_ind[i1][p] = ld end end end end ###################################################################### # 突然変異(転座.2点間の遺伝子を他の位置のものと置き換える.ただし # 重複部分はそのままとする) # pr : 突然変異率 # wd : >0 : 幅を固定 # =0 : 幅をランダム(default) ###################################################################### def M_chg(pr, wd = 0) for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 1 and rand(0) <= pr # 区間等の決定([p1,p2]と[p3,p4]の入れ替え) # p1 p1 = Integer(rand(0) * @_len[i1]) if p1 >= @_len[i1] p1 = @_len[i1] - 1 end # p3 sw = 0 p3 = p1 while sw == 0 p3 = Integer(rand(0) * @_len[i1]) if p3 >= @_len[i1] p3 = @_len[i1] - 1 end if p3 != p1 sw = 1 end end # 小さい方をp1,p2にする if p1 > p3 p = p1 p1 = p3 p3 = p end # p4, p2 p4 = p1 + wd - 1 if wd == 0 p4 = Integer(rand(0) * (@_len[i1] - p3)) + p3 end if p4 >= @_len[i1] p4 = @_len[i1] - 1 end p2 = p1 + (p4 - p3) # 重複部分のチェック if p2 >= p3 p = p3 - 1 p3 = p2 + 1 p2 = p p4 = p3 + (p2 - p1) end # 実行 p = p3 for i2 in p1 ... p2+1 ld = @_ind[i1][i2] @_ind[i1][i2] = @_ind[i1][p] @_ind[i1][p] = ld p += 1 end end end end ###################################################################### # 突然変異(重複.2点間の遺伝子を他の位置にコピーする # pr : 突然変異率 # wd : >0 : 幅を固定 # =0 : 幅をランダム(deafult) ###################################################################### def M_dup(pr, wd = 0) # データのチェック if @_dup_a == 0 print("***error 突然変異方法が不適当 (M_dup)\n") end # 実行 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 1 and rand(0) <= pr # 区間の決定([p1,p2]を[p3,p4]にコピー) # p1 p1 = Integer(rand(0) * @_len[i1]) if p1 >= @_len[i1] p1 = @_len[i1] - 1 end # p3 sw = 0 p3 = p1 while sw == 0 p3 = Integer(rand(0) * @_len[i1]) if p3 >= @_len[i1] p3 = @_len[i1] - 1 end if p3 != p1 sw = 1 end end # 区間を決める p2 = p1 p4 = p1 if p3 > p1 p4 = p3 + wd - 1 if wd == 0 p4 = Integer(rand(0) * (@_len[i1] - p3)) + p3 end if p4 >= @_len[i1] p4 = @_len[i1] - 1 end p2 = p1 + (p4 - p3) else p2 = p1 + wd - 1 if wd == 0 p2 = Integer(rand(0) * (@_len[i1] - p1)) + p1 end if p2 >= @_len[i1] p2 = @_len[i1] - 1 end p4 = p3 + (p2 - p1) # 実行 end p = p4 i2 = p2 while i2 > p1-1 @_ind[i1][p] = @_ind[i1][i2] p -= 1 i2 -= 1 end end end end ###################################################### # 突然変異(摂動.値をある量だけ変化させる) # pr : 突然変異率 # method : =0 正規分布(default) # =1 一様分布 # m : 平均または一様分布の下限(default=0.0) # s : 標準偏差または一様分布の上限(default=1.0) ###################################################### def M_per(pr, method = 0, m = 0.0, s = 1.0) wd = 0.0 # データのチェックと初期設定 if @_dup_a == 0 print("***error 突然変異方法が不適当 (M_per)\n") end if method > 0 wd = s - m end # 実行 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 1 for i2 in 0 ... @_len[i1] if rand(0) <= pr if method == 0 w = normalvariate(m, s) else w = rand(0) * wd if rand(0) < 0.5 w = -w end end x1 = float(@_ind[i1][i2]) + w if x1 > @_allele_u x1 = @_allele_u else if x1 < @_allele_l x1 = @_allele_l end end @_ind[i1][i2] = Integer(x1) end end end end end ############################################## # 突然変異(挿入.ある長さの遺伝子を挿入する) # pr : 突然変異率 # wd : >0 幅を固定 # =0 幅をランダム(default) ############################################## def M_ins(pr, wd = 0) # データのチェック if @_dup_a == 0 or @_min_len < 0 print("***error 突然変異方法が不適当 (M_ins)\n") end # 実行 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 1 and rand(0) <= pr # 挿入位置の決定 p = Integer(rand(0) * (@_len[i1] + 1)) if p > @_len[i1] p = @_len[i1] end # 挿入する遺伝子長の決定 l = wd if wd == 0 l = Integer(rand(0) * (@_max_len - @_len[i1] + 1)) end if l > @_max_len-@_len[i1] l = @_max_len - @_len[i1] else if l <= 0 l = 1 end end # 実行 # 挿入場所の確保 if p < @_len[i1] i2 = @_len[i1] + l - 1 while i2 > p-1 @_ind[i1][i2] = @_ind[i1][i2-l] i2 -= 1 end end # 挿入場所の遺伝子の決定 for i2 in p ... p+l ld = Integer(rand(0) * (@_allele_u - @_allele_l + 1) + @_allele_l) if ld > @_allele_u ld = @_allele_u end @_ind[i1][i2] = ld end @_len[i1] += l end end end ############################################## # 突然変異(削除.ある長さの遺伝子を削除する) # pr : 突然変異率 # wd : >0 幅を固定 # =0 幅をランダム(default) ############################################## def M_del(pr, wd = 0) # データのチェック if @_dup_a == 0 or @_min_len < 0 print("***error 突然変異方法が不適当 (M_del)\n") end # 実行 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 1 and rand(0) <= pr # 削除位置の決定 p = Integer(rand(0) * @_len[i1]) if p >= @_len[i1] p = @_len[i1] - 1 end # 削除する遺伝子長の決定 max = @_len[i1] - p if @_len[i1]-@_min_len < @_len[i1]-p max = @_len[i1] - @_min_len end l = wd if wd == 0 l = Integer(rand(0) * max + 1) end if l > max l = max end # 実行 for i2 in 0 ... @_len[i1]-p-l @_ind[i1][p+i2] = @_ind[i1][p+i2+l] end @_len[i1] -= l end end end ###################################################################### # 淘汰(エリート・ルーレット選択) # elite : エリートで残す個体数(default=0) # s_method : ルーレット板の作成方法(default=1) # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 # s_bias : α,または,method=2の場合は初期値(default=0) # s_step : β(default=1) ###################################################################### def S_roul(elite = 0, s_method = 1, s_bias = 0.0, s_step = 1.0) count = 0 k = 0 n = 0 # 値のチェックと初期設定 if s_method != 0 and s_method != 2 s_method = 1 end if elite > @_size print("***error エリートで残す数が多すぎる (S_roul)\n") end if s_method == 2 and s_step <= 0.0 s_step = 1.0 end for i1 in 0 ... @_size+@_max_ch @_s_w[i1] = 0 end # 重複個体を削除 if @_dup_s == 0 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 0 for i2 in i1+1 ... @_size+@_max_ch if @_pi_w[i2] > 0 and @_len[i1] == @_len[i2] sw = 0 for i3 in 0 ... @_len[i1] if @_ind[i1][i3] != @_ind[i2][i3] sw = 1 break end end if sw == 0 @_pi_w[i2] = 0 end end end end end end for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 n += 1 end end if n < 0 or @_dup_s == 0 and n < @_size print("***error 残す個体がない (S_roul)\n") end # 淘汰して残す個体を選ぶ # エリートの選択 sw = 0 while k < elite and k < n and sw == 0 max = -1 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] > 1 and @_s_w[i1] == 0 if max < 0 or @_pi[i1] > @_pi[max] max = i1 end end end if max < 0 sw = 1 else @_s_w[max] = 1 k += 1 end end # ルーレット選択 while count < @_size+@_max_ch and k < @_size p = Select(s_method, s_bias, s_step) if @_dup_s == 0 and @_s_w[p] > 0 count += 1 else count = 0 @_s_w[p] += 1 k += 1 end end # 選択に失敗した場合の処理 if @_dup_s == 0 and k < @_size i1 = 0 while i1 < @_size+@_max_ch and k < @_size if @_pi_w[i1] > 1 and @_s_w[i1] == 0 @_s_w[i1] = 1 k += 1 end i1 += 1 end end # 複数回選択されたものの処理 for i1 in 0 ... @_size+@_max_ch if @_s_w[i1] == 0 @_pi_w[i1] = 0 end end for i1 in 0 ... @_size+@_max_ch if @_s_w[i1] > 0 if @_s_w[i1] > 1 for i2 in 2 ... @_s_w[i1]+1 k = Position(-1) @_len[k] = @_len[i1] @_pi_w[k] = 2 @_pi[k] = @_pi[i1] for i3 in 0 ... @_len[i1] @_ind[k][i3] = @_ind[i1][i3] end end end end end end end #################### # クラスTSPの定義 #################### class TSP < Species ###################################### # コンストラクタ # name1 : Species定義ファイル名 # name2 : TSP定義ファイル名 ###################################### def initialize(name1, name2) super(name1) # 親のコンストラクタ # 基本データの入力 inn = open(name2, "r") s = inn.gets().split(" ") @_out_lvl = Integer(s[1]) # 出力レベル # =0 最終出力だけ # n>0 n世代毎に出力(負の時はファイル) @_out_m = Integer(s[3]) # 出力方法 # =0 すべてを出力 # =1 最大適応度の個体だけを出力 s = inn.gets().split(" ") @_o_file = s[1] # 出力ファイル名 @_out_d = Integer(s[3]) # 表示間隔 s = inn.gets().split(" ") @_kosa_m = Integer(s[1]) # 交叉方法 # =-1 交叉を使用しない # =0 親のコピー # =1 循環交叉 # =2 部分的交叉 # =3 順序交叉 # =4 一様順序交叉 # =5 一様位置交叉 # =6 エッジ組み替え交叉 # =7 サブツアー交叉 @_kosa = Float(s[3]) # 交叉確率 @_k_point = Integer(s[5]) # 交差点の数(負の時は,1から-k_point間のランダム) @_k_vr = Integer(s[7]) # =0 両親とも同じ位置で交叉 # =1 両親が異なる位置で交叉(遺伝子長は可変) @_k_method = Integer(s[9]) # 交叉の時の親の選択方法 # =-1 ランダム # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 @_k_bias = Float(s[11]) # α,または,method=2の場合は初期値 @_k_step = Float(s[13]) # β s = inn.gets().split(" ") @_mute_m = Integer(s[1]) # 突然変異方法 # =-1 突然変異を使用しない # =0 移動 # =1 逆位 # =2 スクランブル # =3 転座 @_mute = Float(s[3]) # 突然変異率 @_wd = Integer(s[5]) # 突然変異に使用する部分遺伝子長 @_m_mean = Float(s[7]) # 摂動の平均値 @_m_std = Float(s[9]) # 摂動の標準偏差 s = inn.gets().split(" ") @_elite = Integer(s[1]) # エリート選択で残す数 @_s_method = Integer(s[3]) # ルーレット板の作成方法 # =0 適応度をそのまま使用 # =1 最小値からの差(ただし,α以下の場合はα) # =2 評価値に順位をつけ,減少率βで線形化 @_s_bias = Float(s[5]) # α,または,s_method=2の場合は初期値 @_s_step = Float(s[7]) # β s = inn.gets().split(" ") @_n_city = Integer(s[1]) # 都市の数 @_max_gen = Integer(s[3]) # 最大世代交代数 s = inn.gets().split(" ") @_kinbo = Integer(s[1]) # 近傍探索(0:行わない,1:行う) @_neib = Integer(s[3]) # 近傍(2 or 3) s = inn.gets().split(" ") @_sel = Integer(s[1]) # エッジの選択方法 # =0 最良のものを選択 # =1 最初のものを選択 if @_kinbo > 0 and @_neib != 2 and @_neib != 3 print("***error 近傍の値が不適当\n") end if @_n_city != @_max_len print("***error 都市数が不適当\n") end # 都市の位置データ @_city = Array.new(@_n_city) for i1 in 0 ... @_n_city @_city[i1] = Array.new(2) s = inn.gets().split(" ") @_city[i1][0] = Integer(s[0]) @_city[i1][1] = Integer(s[1]) end # 距離テーブル @_rg = Array.new(@_n_city) for i1 in 0 ... @_max_len @_rg[i1] = Array.new(@_n_city) end for i1 in 0 ... @_n_city for i2 in i1+1 ... @_n_city x = @_city[i2][0] - @_city[i1][0] y = @_city[i2][1] - @_city[i1][1] @_rg[i1][i2] = Math.sqrt(x * x + y * y).round() end end for i1 in 1 ... @_n_city for i2 in 0 ... i1 @_rg[i1][i2] = @_rg[i2][i1] end end inn.close() end ############### # 全体の制御 ############### def Control() gen = 1 # 初期集団の発生 Init_std() # 評価 if @_kinbo > 0 Kinbo() else Adap() end # 出力 print("***世代 " + String(gen) + " 適応度 max " + String(@_max) + " (" + String(@_max_n) + ") mean " + String(@_mean) + "\n") if @_out_lvl.abs() > 0 Output(gen) end # 世代交代 for gen in 2 ... @_max_gen+1 # 交叉 if @_kosa_m == 0 C_copy() # 親のコピー elsif @_kosa_m == 1 C_cycle(@_kosa) # 循環交叉 elsif @_kosa_m == 2 C_part(@_kosa) # 部分的交叉 elsif @_kosa_m == 3 C_seq(@_kosa) # 順序交叉 elsif @_kosa_m == 4 C_useq(@_kosa) # 一様順序交叉 elsif @_kosa_m == 5 C_upos(@_kosa) # 一様位置交叉 elsif @_kosa_m == 6 C_edge(@_kosa) # エッジ組み替え交叉 elsif @_kosa_m == 7 C_sub(@_kosa, @_k_point) # サブツアー交叉 end # 突然変異 if @_mute_m == 0 M_move(@_mute) # 移動 elsif @_mute_m == 1 M_inv(@_mute) # 逆位 elsif @_mute_m == 2 M_scram(@_mute) # スクランブル elsif @_mute_m == 3 M_chg(@_mute) # 転座 end # 適応度 if @_kinbo > 0 Kinbo() else Adap() end # 淘汰 S_roul(@_elite) # 出力 if gen%@_out_d == 0 print("***世代 " + String(gen) + " 適応度 max " + String(@_max) + " (" + String(@_max_n) + ") mean " + String(@_mean) + "\n") end if @_out_lvl.abs() > 0 if gen%@_out_lvl.abs() == 0 Output(gen) end end end gen -= 1 k1 = @_out_m @_out_m = 0 print("***世代 " + String(gen) + " 適応度 max " + String(@_max) + " (" + String(@_max_n) + ") mean " + String(@_mean) + "\n") Output(gen) @_out_m = k1 end ########################## # 距離の計算 # n_c : 都市の数 # p : 都市番号 # return : 距離(負) ########################## def Kyori(n_c, p) r = 0 n1 = p[0] for i1 in 1 ... n_c n2 = p[i1] r -= @_rg[n1][n2] n1 = n2 end n2 = p[0] r -= @_rg[n1][n2] return r end ################ # 適応度の計算 ################ def Adap() k = 0 @_mean = 0.0 @_max = 0.0 @_max_n = -1 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 1 @_pi_w[i1] = 2 @_pi[i1] = Kyori(@_len[i1], @_ind[i1]) end if @_pi_w[i1] > 0 k += 1 @_mean += @_pi[i1] if @_max_n < 0 or @_pi[i1] > @_max @_max = @_pi[i1] @_max_n = i1 end end end if k > 0 @_mean /= k end end ###################################### # エッジの入れ替え # n_city : 都市の数 # seq : 訪問する順番 # r_m : 距離の負値 # return : =0 改善がなかった # =1 改善があった ###################################### def Change(n_city, seq, r_m) ch = 0 sw = 0 max = r_m[0] n3 = Integer(rand(0) * (n_city - 2)) if n3 > n_city-3 n3 = n_city - 3 end # 2近傍 i1 = 0 while i1 <= n_city-3 and ch == 0 if n3 == 0 n1 = n_city - 2 else n1 = n_city - 1 end i2 = n3 + 2 while i2 <= n1 and ch == 0 # 枝の場所((n3,n3+1), (k1,k2)) k1 = i2 if i2 == n_city-1 k2 = 0 else k2 = i2 + 1 end # 枝の入れ替え @_kou1[0] = seq[n3] k = 1 i3 = k1 while i3 > n3 @_kou1[k] = seq[i3] k += 1 i3 -= 1 end nn = k2 while nn != n3 @_kou1[k] = seq[nn] k += 1 nn += 1 if nn > n_city-1 nn = 0 end end # 評価 r = Kyori(n_city, @_kou1) if r > max max = r sw = 1 for i3 in 0 ... n_city @_kou2[i3] = @_kou1[i3] end if @_sel > 0 ch = 1 end end i2 += 1 end n3 += 1 if n3 > n_city-3 n3 = 0 end i1 += 1 end # 3近傍 if @_neib == 3 and ch == 0 i1 = 0 while i1 <= n_city-3 and ch == 0 n1 = n_city - 2 n2 = n_city - 1 i2 = n3 + 1 while i2 <= n1 and ch == 0 i3 = i2 + 1 while i3 <= n2 and ch == 0 # 枝の場所((n3,n3+1), (i2,i2+1), (k1,k2)) k1 = i3 k2 = k1 if i3 == n_city-1 k2 = 0 else k2 = i3 + 1 end # 枝の入れ替えと評価 # 入れ替え(その1) @_kou1[0] = seq[n3] k = 1 i4 = i2 while i4 > n3 @_kou1[k] = seq[i4] k += 1 i4 -= 1 end i4 = k1 while i4 > i2 @_kou1[k] = seq[i4] k += 1 i4 -= 1 end nn = k2 while nn != n3 @_kou1[k] = seq[nn] k += 1 nn += 1 if nn > n_city-1 nn = 0 end end # 評価(その1) r = Kyori(n_city, @_kou1) if r > max max = r sw = 1 for i3 in 0 ... n_city @_kou2[i3] = @_kou1[i3] end if @_sel > 0 ch = 1 end end # 入れ替え(その2) @_kou1[0] = seq[n3] k = 1 i4 = k1 while i4 > i2 @_kou1[k] = seq[i4] k += 1 i4 -= 1 end for i4 in n3+1 ... i2+1 @_kou1[k] = seq[i4] k += 1 end nn = k2 while nn != n3 @_kou1[k] = seq[nn] k += 1 nn += 1 if nn > n_city-1 nn = 0 end end # 評価(その2) r = Kyori(n_city, @_kou1) if r > max max = r sw = 1 for i3 in 0 ... n_city @_kou2[i3] = @_kou1[i3] end if @_sel > 0 ch = 1 end end # 入れ替え(その3) @_kou1[0] = seq[n3] k = 1 for i4 in i2+1 ... k1+1 @_kou1[k] = seq[i4] k += 1 end i4 = i2 while i4 > n3 @_kou1[k] = seq[i4] k += 1 i4 -= 1 end nn = k2 while nn != n3 @_kou1[k] = seq[nn] k += 1 nn += 1 if nn > n_city-1 nn = 0 end end # 評価(その3) r = Kyori(n_city, @_kou1) if r > max max = r sw = 1 for i3 in 0 ... n_city @_kou2[i3] = @_kou1[i3] end if @_sel > 0 ch = 1 end end # 入れ替え(その4) @_kou1[0] = seq[n3] k = 1 for i4 in i2+1 ... k1+1 @_kou1[k] = seq[i4] k += 1 end for i4 in n3+1 ... i2+1 @_kou1[k] = seq[i4] k += 1 end nn = k2 while nn != n3 @_kou1[k] = seq[nn] k += 1 nn += 1 if nn > n_city-1 nn = 0 end end # 評価(その4) r = Kyori(n_city, @_kou1) if r > max max = r sw = 1 for i3 in 0 ... n_city @_kou2[i3] = @_kou1[i3] end if @_sel > 0 ch = 1 end end i3 += 1 end i2 += 1 end n3 += 1 if n3 > n_city-3 n3 = 0 end i1 += 1 end end # 設定 if sw > 0 r_m[0] = max for i1 in 0 ... n_city seq[i1] = @_kou2[i1] end end return sw end ############## # 近傍の探索 ############## def Kinbo() k = 0 @_max = 0.0 @_max_n = -1 @_mean = 0.0 for i1 in 0 ... @_size+@_max_ch if @_pi_w[i1] == 1 @_pi_w[i1] = 2 sw = 1 r = Kyori(@_len[i1], @_ind[i1]) while sw > 0 r = Array.new(1) sw = Change(@_len[i1], @_ind[i1], r) end @_pi[i1] = r[0] end if @_pi_w[i1] > 0 k += 1 @_mean += @_pi[i1] if @_max_n < 0 or @_pi[i1] > @_max @_max = @_pi[i1] @_max_n = i1 end end end if k > 0 @_mean /= k end end ############################# # 結果の出力 # gen : 現在の世代番号 ############################# def Output(gen) k = 0 pr = -1 if @_out_lvl >= 0 print(" 出力先は(0:出力なし,n:画面にn個づつ,-1:ファイル)? ") pr = Integer($stdin.gets()) end if pr != 0 # 出力先の決定と評価値の出力 if pr > 0 out = $stdout $stdin.gets() else now = String(Time.now) out = open(@_o_file, "a") out.print("***世代 " + String(gen) + " 適応度 max " + String(@_max) + " (" + String(@_max_n) + ") mean " + String(@_mean) + " 時間 " + now + "\n") end # 巡回順序の出力 if @_out_m == 0 for i1 in 0 ... @_len[@_max_n] n = @_ind[@_max_n][i1] out.print(String(n) + " " + String(@_city[n][0]) + " " + String(@_city[n][1]) + "\n") if pr > 0 k += 1 if k == pr $stdin.gets() k = 0 end end end end if pr < 0 out.close() end end end end # 入力ミス if ARGV[0] == nil print("***error ファイル名を入力して下さい\n") # 入力OK else # データの数と入力データファイル名の入力 line = gets() n = Integer(line) # データの数 i_file1 = Array.new(n) i_file2 = Array.new(n) for i1 in 0 ... n line = gets() a = line.split(" ") i_file1[i1] = a[0] i_file2[i1] = a[1] end # 実行(乱数の初期値を変える) for i1 in 0 ... n print("\n+++++ケース " + String(i1+1) + "+++++\n") srand(1000 * i1 + 1234567); tsp = TSP.new(i_file1[i1], i_file2[i1]) tsp.Control() end end =begin ----------------ケーススタディデータ(data_ct.txt)------ 3 data1_t.txt data2_t.txt data1_t.txt data2_t.txt data1_t.txt data2_t.txt ---------------Species記述データ(data1_t.txt)--------- 対立遺伝子上限 9 対立遺伝子下限 0 最大遺伝子長 10 最小遺伝子長(負の時は,最大遺伝子長で固定) -1 遺伝子の重複 0 個体の重複(同じ染色体の個体) 0 集団サイズ 10 子供 10 ---------------TSP記述データ(data2_t.txt)-------- 出力レベル(負はファイル) 10 出力方法(0:適応度+順番,1:適応度) 0 出力ファイル名 out1.txt 表示間隔 10 交叉方法 1 交叉確率 1.0 点 5 位置 0 方法 1 バイアス 0 ステップ 1 突然変異方法 1 突然変異率 0.03 幅 1 平均 0.0 標準偏差 1.0 エリート 2 方法 1 バイアス 0 ステップ 1 都市数 10 最大世代交代数 2000 近傍探索(0:行わない,1:行う) 0 近傍(2or3) 2 選択方法(0:最良,1:最初) 1 -58 37 55 -19 6 -79 27 -30 44 -94 33 -58 -94 87 -9 3 33 69 43 -57 =end