1 # 模拟退火算法求解三十城市TSP问题
2 # 2018-3-21开始求解
3 # yangmingustb@outlook.com
4
5
6 import math
7 import random
8 import datetime
9
10 class SaTSP(object):
11
12 def __init__(self, tf=0.01, alpha=0.9):
13 self.tf = tf # 最低温度
14 self.alpha = alpha # 降温系数
15
16 # 30城市坐标
17 self.coordinates = [[41, 94], [37, 84], [54, 67], [25, 62], [7, 64],
18 [2, 99], [68, 58], [71, 44], [54, 62], [83, 69],
19 [64, 60], [18, 54], [22, 60], [83, 46], [91, 38],
20 [25, 38], [24, 42], [58, 69], [71, 71], [74, 78],
21 [87, 76], [18, 40], [13, 40], [82, 7], [62, 32],
22 [58, 35], [45, 21], [41, 26], [44, 35], [4, 50]
23 ]
24
25 self.iteration = 200 * len(self.coordinates) # 每一个温度过程中迭代次数
26
27 def initial_temperature(self): # 温度初始化,采用t0=-delta(f)/(ln0.9)
28 # number_list = []
29 # for index, item in enumerate(self.coordinates):
30 # number_list.append([index, item])
31 # print(number_list)
32
33 # print(path)
34 # print(path[0])
35 dist_list = []
36 for i in range(100): # 生成一百条路径
37
38 path = random.sample(self.coordinates, len(self.coordinates)) # 生成一条随机序列
39 dist_list.append(self.all_dist(path))
40
41 t0 = -(max(dist_list) - min(dist_list)) / math.log(0.9) # 设置初温
42 print('初始温度是:', t0)
43 return t0
44
45 def convergence(self, t_update, iteration_update): # 收敛
46 while t_update >= 0.01:
47 while iteration_update <= self.iteration:
48 self.metropolis_rule()
49 else:
50 self.shuchu()
51 else:
52 self.shuchu()
53
54 def D_value(self, current_path, update_path): # 变换前后目标函数差值
55 # value_list = []
56 print('计算两个状态的差值...')
57 current_distance = self.all_dist(current_path)
58 # value_list.append(current_path)
59 update_distance = self.all_dist(update_path)
60 # value_list.append(update_path)
61 d_value = update_distance-current_distance
62 return d_value
63
64 def metropolis_rule(self, current_path, update_path,update_t):
65 de = self.D_value(current_path, update_path)
66 if de < 0:
67
68 current_path = update_path
69 else:
70 p = math.exp(-de/update_t)
71 if random.random() <= p:
72 current_path = update_path
73
74 else:
75 path = self.swap(update_path)
76 self.metropolis_rule(current_path, path,update_t)
77 return current_path
78
79 def first_path(self): # 生成第一条初始化的路径
80 length = len(self.coordinates)
81 # 因为对初值不敏感,生成一条随机序列, 第一条路径是随机的
82 path = random.sample(self.coordinates, length)
83 return path
84
85 def swap(self, path): # 随机交换2个城市顺序
86 print('产生新解...')
87 city_1 = random.randint(0, len(self.coordinates) - 1) # 产生第一个交换点
88 while True:
89 city_2 = random.randint(0, len(self.coordinates) - 1) # 产生第二个交换点
90 if city_2 != city_1:
91 break
92 else:
93 continue
94 path[city_1], path[city_2] = path[city_2], path[city_1]
95 print('产生新解结束')
96 return path
97
98 def update_t(self, t):
99 t_update = self.alpha*t
100 return t_update
101
102 def between_dist(self, point1, point2): # 计算两点距离
103
104 dist_x = point1[0] - point2[0]
105 dist_y = point1[1] - point2[1]
106 dist = dist_x ** 2 + dist_y ** 2
107 dist = math.sqrt(dist)
108 return dist
109
110 def all_dist(self, path): # 计算所有点距离,总共30段距离
111 sum_cities = 0
112 n = len(path)
113 for i in range(n - 1): # 先计算前29段距离
114 sum_cities += self.between_dist(path[i], path[i + 1])
115 sum_cities += self.between_dist(path[n - 1], path[0]) # 计算第30个点和第一个点的距离,形成闭环
116 return sum_cities
117
118 def shuchu(self):
119 pass
120
121 def main(self): # 函数式编程,调用其它函数,进入这个大循环
122
123 start_time = datetime.datetime.now()
124 t = self.initial_temperature() # 调用生成初温函数
125 current_path = self.first_path()
126 while t > self.tf: # 终止条件
127 iteration_number = 0
128 while iteration_number < self.iteration: # metropolis终止条件
129 update_path = self.swap(current_path)
130 de = self.D_value(current_path, update_path)
131
132 if de < 0: # 如果增量为负值,则接受
133
134 current_path = update_path
135
136 else: # 产生新解比较
137 p = math.exp(-de / t)
138 if random.random() <= p:
139 current_path = update_path
140
141 else: # 否则保留当前解解,而不是一直产生新解比较,注意误区
142 current_path = current_path
143 # else:
144 # path = self.swap(update_path)
145 # self.metropolis_rule(current_path, path, update_t)
146 # return current_path
147 iteration_number += 1
148 t = self.alpha*t
149 distance = self.all_dist(current_path)
150 print(distance)
151 end_time = datetime.datetime.now()
152 this_time = end_time - start_time
153 print('程序运行时间:', this_time)
154 return current_path
155
156
157 s1 = SaTSP()
158 s1.main()