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

# -*- coding: UTF-8 -*-
import sys
from math import *
from random import *
from datetime import *
import numpy as np

##########################
# 距離の計算
#      n_c : 都市の数
#      p : 都市番号
#      rg : 都市間の距離
#      return : 距離(負)
##########################

def kyori(n_c, p, rg) :
	
	r  = 0
	n1 = p[0]
	
	for i1 in range(1, n_c) :
		n2  = p[i1]
		r  -= rg[n1][n2]
		n1  = n2
	
	n2  = p[0]
	r  -= rg[n1][n2]
	
	return r
	
#########################
# クラスIterationの定義
#########################

class Iteration :

	############################
	# コンストラクタ
	#      name : ファイル名
	############################

	def __init__(self, name) :
						# ファイルのオープン
		inn = open(name, "r")
						# 基本データ
		s            = inn.readline().split()
		self.n_city  = int(s[1])   # 都市の数
		self.max_try = int(s[3])   # 最大試行回数
		self.sel     = int(s[5])   # エッジの選択方法
		                           #   =0 : 最良のものを選択
		                           #   =1 : 最初のものを選択
		self.neib    = int(s[7])   # 近傍(2 or 3)
		s            = inn.readline().split()
		self.out_lvl = int(s[1])   # 出力レベル
		                           #   =0 : 最終出力だけ
		                           #   n>0 : n回毎に出力(負の時はファイル)
		self.out_m   = int(s[3])   # 出力方法
		                           #   =-1 : 出力しない
		                           #   =0 : すべてを出力
		                           #   =1 : 評価値だけを出力(最終結果だけはすべてを出力)
		s            = inn.readline().split()
		self.o_file  = s[1]   # 出力ファイル名
		self.out_d   = int(s[3])   # 表示間隔
						# 都市の位置データ
		self.city    = np.empty((self.n_city, 2), np.int)
		for i1 in range(0, self.n_city) :
			s                = inn.readline().split()
			self.city[i1][0] = int(s[0])
			self.city[i1][1] = int(s[1])
						# ファイルのクローズ
		inn.close()
						# 距離テーブルの作成
		self.rg = np.empty((self.n_city, self.n_city), np.int)   # 都市間の距離

		for i1 in range(0, self.n_city) :
			for i2 in range(i1+1, self.n_city) :
				x               = self.city[i2][0] - self.city[i1][0]
				y               = self.city[i2][1] - self.city[i1][1]
				self.rg[i1][i2] = int(sqrt(x * x + y * y) + 0.5)
	
		for i1 in range(0, self.n_city) :
			for i2 in range(0, i1) :
				self.rg[i1][i2] = self.rg[i2][i1]
						# 都市を訪れる順序(初期設定)
		self.seq    = np.empty(self.n_city, np.int)   # 都市を訪れる順序
		self.seq_w1 = np.empty(self.n_city, np.int)   # 都市を訪れる順序(ワーク)
		self.seq_w2 = np.empty(self.n_city, np.int)   # 都市を訪れる順序(ワーク)
	
		for i1 in range(0, self.n_city) :
			sw = 0
			while sw == 0 :
				ct = int(random() * self.n_city)
				if ct >= self.n_city :
					ct = self.n_city - 1
				self.seq[i1] = ct
				sw           = 1
				for i2 in range(0, i1) :
					if ct == self.seq[i2] :
						sw = 0
						break

	#################
	# 最適化の実行
	#################
	
	def Optimize(self) :
	
						# 初期設定
		n_tri  = 0
		max    = np.empty(1, np.int)
		max[0] = kyori(self.n_city, self.seq, self.rg)
	
		if self.out_m >= 0 and abs(self.out_lvl) > 0 :
			print("***試行回数 " + str(n_tri) + " 距離 " + str(-max[0]))
			self.Output(self.out_lvl, n_tri, max[0])
						# 実行
		sw = 1
		for n_tri in range(1, self.max_try+1) :
							# 改善
			sw = self.Change(max)
							# 出力
			if self.out_d > 0 and n_tri%self.out_d == 0 :
				print("***試行回数 " + str(n_tri) + " 距離 " + str(-max[0]))
	
			if self.out_m >= 0 and abs(self.out_lvl) > 0 :
				if n_tri%abs(self.out_lvl) == 0 :
					self.Output(self.out_lvl, n_tri, max[0])
			if sw <= 0 :
				break
						# 最終出力
		if self.out_m >= 0 :
			n_tri      -= 1
			k1          = self.out_m
			self.out_m  = 0
			print("***試行回数 " + str(n_tri) + " 距離 " + str(-max[0]))
			self.Output(self.out_lvl, n_tri, max[0])
			self.out_m = k1
	
		return n_tri
	
	###############################
	# 出力
	#      sw : >=0 : 出力先未定
	#           <0 : ファイル
	#      n_tri : 現在の試行回数
	#      r : 距離の負値
	###############################

	def Output(self, sw, n_tri, r) :
	
		k  = 0
		pr = -1

		if sw >= 0 :
			pr = int(input("   出力先は(0:出力なし,n:画面にn個づつ,-1:ファイル)? "))
	
		if pr != 0 :
	
			if pr > 0 :
				out = sys.stdout
				input("")
			else :
				now = datetime.today().time().isoformat()
				out = open(self.o_file, "a")
				out.write("***試行回数 " + str(n_tri) + " 距離 " + str(-r) + " 時間 " + now + "\n")
	
			if self.out_m == 0 :
				for i1 in range(0, self.n_city) :
					n = self.seq[i1]
					out.write("  " + str(n) + " " + str(self.city[n][0]) + " " + str(self.city[n][1]) + "\n")
					if pr > 0 :
						k += 1
						if k == pr :
							input("")
							k = 0
	
			if pr <= 0 :
				out.close()
	
	######################################
	# エッジの入れ替え
	#      r_m : 距離の負値
	#      return : =0 : 改善がなかった
	#               =1 : 改善があった
	######################################

	def Change(self, r_m) :
	
		ch  = 0
		sw  = 0
		max = r_m[0]
	
		n3  = int(random() * (self.n_city - 2))
		if n3 > self.n_city-3 :
			n3 = self.n_city - 3
							# 2近傍
		i1 = 0
		while i1 <= self.n_city-3 and ch == 0 :
	
			n1 = self.n_city - 1
			if n3 == 0 :
				n1 = self.n_city - 2
	
			i2 = n3 + 2
			while i2 <= n1 and ch == 0 :
								# 枝の場所((n3,n3+1), (k1,k2))
				k1 = i2
				k2 = i2 + 1
				if i2 == self.n_city-1 :
					k2 = 0
								# 枝の入れ替え
				self.seq_w1[0] = self.seq[n3]
				k              = 1
				for i3 in range(k1, n3, -1) :
					self.seq_w1[k] = self.seq[i3]
					k += 1
	
				nn = k2
				while nn != n3 :
					self.seq_w1[k] = self.seq[nn]
					k  += 1
					nn += 1
					if nn > self.n_city-1 :
						nn = 0
								# 評価
				r = kyori(self.n_city, self.seq_w1, self.rg)
	
				if r > max :
					max = r
					sw  = 1
					for i3 in range(0, self.n_city) :
						self.seq_w2[i3] = self.seq_w1[i3]
					if self.sel > 0 :
						ch = 1
				i2 += 1
	
			n3 += 1
			if n3 > self.n_city-3 :
				n3 = 0
			i1 += 1
							# 3近傍
		if self.neib == 3 and ch == 0 :
	
			i1 = 0
			while i1 <= self.n_city-3 and ch == 0 :
	
				n1 = self.n_city - 2
				n2 = self.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 == self.n_city-1 :
							k2 = 0
								# 枝の入れ替えと評価
									# 入れ替え(その1)
						self.seq_w1[0] = self.seq[n3]
						k              = 1
						for i4 in range(i2, n3, -1) :
							self.seq_w1[k] = self.seq[i4]
							k += 1
	
						for i4 in range(k1, i2, -1) :
							self.seq_w1[k] = self.seq[i4]
							k += 1
	
						nn = k2
						while nn != n3 :
							self.seq_w1[k] = self.seq[nn]
							k  += 1
							nn += 1
							if nn > self.n_city-1 :
								nn = 0
									# 評価(その1)
						r = kyori(self.n_city, self.seq_w1, self.rg)
	
						if r > max :
							max = r
							sw  = 1
							for i3 in range(0, self.n_city) :
								self.seq_w2[i3] = self.seq_w1[i3]
							if self.sel > 0 :
								ch = 1
									# 入れ替え(その2)
						self.seq_w1[0] = self.seq[n3]
						k              = 1
						for i4 in range(k1, i2, -1) :
							self.seq_w1[k] = self.seq[i4]
							k += 1
	
						for i4 in range(n3+1, i2+1) :
							self.seq_w1[k] = self.seq[i4]
							k += 1
	
						nn = k2
						while nn != n3 :
							self.seq_w1[k] = self.seq[nn]
							k  += 1
							nn += 1
							if nn > self.n_city-1 :
								nn = 0
									# 評価(その2)
						r = kyori(self.n_city, self.seq_w1, self.rg)
	
						if r > max :
							max = r
							sw  = 1
							for i3 in range(0, self.n_city) :
								self.seq_w2[i3] = self.seq_w1[i3]
							if self.sel > 0 :
								ch = 1
									# 入れ替え(その3)
						self.seq_w1[0] = self.seq[n3]
						k              = 1
						for i4 in range(i2+1, k1+1) :
							self.seq_w1[k] = self.seq[i4]
							k += 1
	
						for i4 in range(i2, n3, -1) :
							self.seq_w1[k] = self.seq[i4]
							k += 1
	
						nn = k2
						while nn != n3 :
							self.seq_w1[k] = self.seq[nn]
							k  += 1
							nn += 1
							if nn > self.n_city-1 :
								nn = 0
									# 評価(その3)
						r = kyori(self.n_city, self.seq_w1, self.rg)
	
						if r > max :
							max = r
							sw  = 1
							for i3 in range(0, self.n_city) :
								self.seq_w2[i3] = self.seq_w1[i3]
							if self.sel > 0 :
								ch = 1
									# 入れ替え(その4)
						self.seq_w1[0] = self.seq[n3]
						k              = 1
						for i4 in range(i2+1, k1+1) :
							self.seq_w1[k] = self.seq[i4]
							k += 1
	
						for i4 in range(n3+1, i2+1) :
							self.seq_w1[k] = self.seq[i4]
							k += 1
	
						nn = k2
						while nn != n3 :
							self.seq_w1[k] = self.seq[nn]
							k  += 1
							nn += 1
							if nn > self.n_city-1 :
								nn = 0
									# 評価(その4)
						r = kyori(self.n_city, self.seq_w1, self.rg)
	
						if r > max :
							max = r
							sw  = 1
							for i3 in range(0, self.n_city) :
								self.seq_w2[i3] = self.seq_w1[i3]
							if self.sel > 0 :
								ch = 1
						i3 += 1
					i2 += 1
	
				n3 += 1
				if n3 > self.n_city-3 :
					n3 = 0
				i1 += 1
							# 設定
		if sw > 0 :
			r_m[0] = max
			for i1 in range(0, self.n_city) :
				self.seq[i1] = self.seq_w2[i1]
	
		return sw

----------------------------------

# -*- coding: UTF-8 -*-
import numpy as np
import sys
from math import *
from random import *
from function import Iteration

####################################
# 巡回セールスマン問題(反復改善法)
#      coded by Y.Suganuma
####################################

				# 入力ミス
if len(sys.argv) <= 1 :
	print("***error  ファイル名を入力して下さい")
				# 入力OK
else :
					# データの数と入力データファイル名の入力
	inn    = open(sys.argv[1], "r")

	ss     = inn.readline()
	n      = int(ss)   # データの数
	i_file = []

	for i1 in range(0, n) :
		i_file.append(inn.readline().strip(" \n"))

	inn.close()
					# 実行
	for i1 in range(0, n) :
		print("\n+++++ケース " + str(i1+1) + "+++++")
						# 入力と初期設定
		it = Iteration (i_file[i1])
						# 最適化
		it.Optimize()

------------------------ケーススタディデータ------
3
data2.txt
data2.txt
data3.txt

------------------データファイル(data2.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

------------------データファイル(data3.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