####################################
# 巡回セールスマン問題(反復改善法)
# coded by Y.Suganuma
####################################
#################################
# 距離の計算
# n_c : 都市の数
# p : 都市番号
# rg : 都市間の距離
# return : 距離
#################################
def kyori(n_c, p, rg)
r = 0.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
#########################
# クラスIterationの定義
#########################
class Iteration
############################
# コンストラクタ
# name ファイル名
############################
def initialize(name)
# ファイルのオープン
inn = open(name, "r")
# 基本データ
s = inn.gets().split(" ")
@_n_city = Integer(s[1]) # 都市の数
@_max_try = Integer(s[3]) # 最大試行回数
@_sel = Integer(s[5]) # エッジの選択方法
# =0 最良のものを選択
# =1 最初のものを選択
@_neib = Integer(s[7]) # 近傍(2 or 3)
s = inn.gets().split(" ")
@_out_lvl = Integer(s[1]) # 出力レベル
# =0 最終出力だけ
# n>0 n回毎に出力(負の時はファイル)
@_out_m = Integer(s[3]) # 出力方法
# =-1 出力しない
# =0 すべてを出力
# =1 評価値だけを出力(最終結果だけはすべてを出力)
s = inn.gets().split(" ")
@_o_file = s[1] # 出力ファイル名
@_out_d = Integer(s[3]) # 表示間隔
# 都市の位置データ
@_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
# ファイルのクローズ
inn.close()
# 距離テーブルの作成
@_rg = Array.new(@_n_city) # 都市間の距離
for i1 in 0 ... @_n_city
@_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 0 ... @_n_city
for i2 in 0 ... i1
@_rg[i1][i2] = @_rg[i2][i1]
end
end
# 都市を訪れる順序(初期設定)
@_seq = Array.new(@_n_city) # 都市を訪れる順序
@_seq_w1 = Array.new(@_n_city) # 都市を訪れる順序(ワーク)
@_seq_w2 = Array.new(@_n_city) # 都市を訪れる順序(ワーク)
for i1 in 0 ... @_n_city
sw = 0
while sw == 0
ct = Integer(rand(0) * @_n_city)
if ct >= @_n_city
ct = @_n_city - 1
end
@_seq[i1] = ct
sw = 1
for i2 in 0 ... i1
if ct == @_seq[i2]
sw = 0
break
end
end
end
end
end
#################
# 最適化の実行
#################
def Optimize()
# 初期設定
n_tri = 0
max = Array.new(1)
max[0] = kyori(@_n_city, @_seq, @_rg)
if @_out_m >= 0 and @_out_lvl.abs() > 0
print("***試行回数 " + String(n_tri) + " 距離 " + String(-max[0]) + "\n")
Output(@_out_lvl, n_tri, max[0])
end
# 実行
sw = 1
for n_tri in 1 ... @_max_try+1
# 改善
sw = Change(max)
# 出力
if @_out_d > 0 and n_tri%@_out_d == 0
print("***試行回数 " + String(n_tri) + " 距離 " + String(-max[0]) + "\n")
end
if @_out_m >= 0 and @_out_lvl.abs() > 0
if n_tri%@_out_lvl.abs() == 0
Output(@_out_lvl, n_tri, max[0])
end
end
if sw <= 0
break
end
end
# 最終出力
if @_out_m >= 0
n_tri -= 1
k1 = @_out_m
@_out_m = 0
print("***試行回数 " + String(n_tri) + " 距離 " + String(-max[0]) + "\n")
Output(@_out_lvl, n_tri, max[0])
@_out_m = k1
end
return n_tri
end
###############################
# 出力
# sw >= 0 出力先未定
# < 0 ファイル
# n_tri 現在の試行回数
# r 距離の負値
###############################
def Output(sw, n_tri, r)
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(@_o_file, "a")
out.print("***試行回数 " + String(n_tri) + " 距離 " + String(-r) + " 時間 " + now + "\n")
end
if @_out_m == 0
for i1 in 0 ... @_n_city
n = @_seq[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
######################################
# エッジの入れ替え
# r_m 距離の負値
# return =0 改善がなかった
# =1 改善があった
######################################
def Change(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
n1 = @_n_city - 1
if n3 == 0
n1 = @_n_city - 2
end
i2 = n3 + 2
while i2 <= n1 and ch == 0
# 枝の場所((n3,n3+1), (k1,k2))
k1 = i2
k2 = i2 + 1
if i2 == @_n_city-1
k2 = 0
end
# 枝の入れ替え
@_seq_w1[0] = @_seq[n3]
k = 1
i3 = k1
while i3 > n3
@_seq_w1[k] = @_seq[i3]
k += 1
i3 -= 1
end
nn = k2
while nn != n3
@_seq_w1[k] = @_seq[nn]
k += 1
nn += 1
if nn > @_n_city-1
nn = 0
end
end
# 評価
r = kyori(@_n_city, @_seq_w1, @_rg)
if r > max
max = r
sw = 1
for i3 in 0 ... @_n_city
@_seq_w2[i3] = @_seq_w1[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 = i3 + 1
if i3 == @_n_city-1
k2 = 0
end
# 枝の入れ替えと評価
# 入れ替え(その1)
@_seq_w1[0] = @_seq[n3]
k = 1
i4 = i2
while i4 > n3
@_seq_w1[k] = @_seq[i4]
k += 1
i4 -= 1
end
i4 = k1
while i4 > i2
@_seq_w1[k] = @_seq[i4]
k += 1
i4 -= 1
end
nn = k2
while nn != n3
@_seq_w1[k] = @_seq[nn]
k += 1
nn += 1
if nn > @_n_city-1
nn = 0
end
end
# 評価(その1)
r = kyori(@_n_city, @_seq_w1, @_rg)
if r > max
max = r
sw = 1
for i3 in 0 ... @_n_city
@_seq_w2[i3] = @_seq_w1[i3]
end
if @_sel > 0
ch = 1
end
end
# 入れ替え(その2)
@_seq_w1[0] = @_seq[n3]
k = 1
i4 = k1
while i4 > i2
@_seq_w1[k] = @_seq[i4]
k += 1
i4 -= 1
end
for i4 in n3+1 ... i2+1
@_seq_w1[k] = @_seq[i4]
k += 1
end
nn = k2
while nn != n3
@_seq_w1[k] = @_seq[nn]
k += 1
nn += 1
if nn > @_n_city-1
nn = 0
end
end
# 評価(その2)
r = kyori(@_n_city, @_seq_w1, @_rg)
if r > max
max = r
sw = 1
for i3 in 0 ... @_n_city
@_seq_w2[i3] = @_seq_w1[i3]
end
if @_sel > 0
ch = 1
end
end
# 入れ替え(その3)
@_seq_w1[0] = @_seq[n3]
k = 1
for i4 in i2+1 ... k1+1
@_seq_w1[k] = @_seq[i4]
k += 1
end
i4 = i2
while i4 >n3
@_seq_w1[k] = @_seq[i4]
k += 1
i4 -= 1
end
nn = k2
while nn != n3
@_seq_w1[k] = @_seq[nn]
k += 1
nn += 1
if nn > @_n_city-1
nn = 0
end
end
# 評価(その3)
r = kyori(@_n_city, @_seq_w1, @_rg)
if r > max
max = r
sw = 1
for i3 in 0 ... @_n_city
@_seq_w2[i3] = @_seq_w1[i3]
end
if @_sel > 0
ch = 1
end
end
# 入れ替え(その4)
@_seq_w1[0] = @_seq[n3]
k = 1
for i4 in i2+1 ... k1+1
@_seq_w1[k] = @_seq[i4]
k += 1
end
for i4 in n3+1 ... i2+1
@_seq_w1[k] = @_seq[i4]
k += 1
end
nn = k2
while nn != n3
@_seq_w1[k] = @_seq[nn]
k += 1
nn += 1
if nn > @_n_city-1
nn = 0
end
end
# 評価(その4)
r = kyori(@_n_city, @_seq_w1, @_rg)
if r > max
max = r
sw = 1
for i3 in 0 ... @_n_city
@_seq_w2[i3] = @_seq_w1[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] = @_seq_w2[i1]
end
end
return sw
end
end
# 入力ミス
if ARGV[0] == nil
print("***error ファイル名を入力して下さい\n")
# 入力OK
else
# データの数と入力データファイル名の入力
line = gets()
n = Integer(line) # データの数
i_file = Array.new(n)
for i1 in 0 ... n
line = gets()
a = line.split(" ")
i_file[i1] = a[0]
end
# 実行
for i1 in 0 ... n
print("\n+++++ケース " + String(i1+1) + "+++++\n")
srand(1000 * i1 + 1234567);
# 入力と初期設定
it = Iteration.new(i_file[i1])
# 最適化
it.Optimize()
end
end
=begin
------------------ケーススタディデータ(data.txt)------
3
data1.txt
data1.txt
data2.txt
------------------データファイル(data1.txt)------------
都市の数 10 最大試行回数 3000 選択方法(0:最良,1:最初) 1 近傍(2or3) 3
出力レベル(負はファイル) 0 出力方法(-1:なし,0:すべて,1:評価値) 1
出力ファイル名 out1.txt 表示間隔 0
-58 37
55 -19
6 -79
27 -30
44 -94
33 -58
-94 87
-9 3
33 69
43 -57
------------------データファイル(data2.txt)------------
都市の数 50 最大試行回数 3000 選択方法(0:最良,1:最初) 1 近傍(2or3) 3
出力レベル(負はファイル) 0 出力方法(-1:なし,0:すべて,1:評価値) 1
出力ファイル名 out1.txt 表示間隔 0
86 27
82 16
29 37
27 1
90 88
40 92
87 53
24 99
11 80
61 8
30 93
4 81
86 90
84 52
96 45
4 34
53 6
45 12
23 97
61 46
49 16
82 74
48 36
13 5
12 6
33 26
8 50
98 98
79 65
36 56
42 25
52 12
88 79
27 51
86 51
14 35
77 37
44 0
78 13
21 55
82 26
25 88
55 0
10 59
41 33
7 4
56 99
19 34
92 46
27 35
=end