巡回セールスマン問題(反復改善法)

####################################
# 巡回セールスマン問題(反復改善法)
#      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