├── .idea └── .gitignore ├── ACO.py ├── Branch_and_Bound.py ├── DP.py ├── GA.py ├── PSO.py ├── README.md ├── SA.py ├── SOM.py ├── TS.py ├── data └── st70.tsp └── img ├── ACO.png ├── DP.png ├── GA.png ├── PSO.png ├── SA.png ├── SOM.png └── TS.png /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml 3 | -------------------------------------------------------------------------------- /ACO.py: -------------------------------------------------------------------------------- 1 | import random 2 | import math 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | 6 | 7 | class ACO(object): 8 | def __init__(self, num_city, data): 9 | self.m = 50 # 蚂蚁数量 10 | self.alpha = 1 # 信息素重要程度因子 11 | self.beta = 5 # 启发函数重要因子 12 | self.rho = 0.1 # 信息素挥发因子 13 | self.Q = 1 # 常量系数 14 | self.num_city = num_city # 城市规模 15 | self.location = data # 城市坐标 16 | self.Tau = np.zeros([num_city, num_city]) # 信息素矩阵 17 | self.Table = [[0 for _ in range(num_city)] for _ in range(self.m)] # 生成的蚁群 18 | self.iter = 1 19 | self.iter_max = 500 20 | self.dis_mat = self.compute_dis_mat(num_city, self.location) # 计算城市之间的距离矩阵 21 | self.Eta = 10. / self.dis_mat # 启发式函数 22 | self.paths = None # 蚁群中每个个体的长度 23 | # 存储存储每个温度下的最终路径,画出收敛图 24 | self.iter_x = [] 25 | self.iter_y = [] 26 | # self.greedy_init(self.dis_mat,100,num_city) 27 | def greedy_init(self, dis_mat, num_total, num_city): 28 | start_index = 0 29 | result = [] 30 | for i in range(num_total): 31 | rest = [x for x in range(0, num_city)] 32 | # 所有起始点都已经生成了 33 | if start_index >= num_city: 34 | start_index = np.random.randint(0, num_city) 35 | result.append(result[start_index].copy()) 36 | continue 37 | current = start_index 38 | rest.remove(current) 39 | # 找到一条最近邻路径 40 | result_one = [current] 41 | while len(rest) != 0: 42 | tmp_min = math.inf 43 | tmp_choose = -1 44 | for x in rest: 45 | if dis_mat[current][x] < tmp_min: 46 | tmp_min = dis_mat[current][x] 47 | tmp_choose = x 48 | 49 | current = tmp_choose 50 | result_one.append(tmp_choose) 51 | rest.remove(tmp_choose) 52 | result.append(result_one) 53 | start_index += 1 54 | pathlens = self.compute_paths(result) 55 | sortindex = np.argsort(pathlens) 56 | index = sortindex[0] 57 | result = result[index] 58 | for i in range(len(result)-1): 59 | s = result[i] 60 | s2 = result[i+1] 61 | self.Tau[s][s2]=1 62 | self.Tau[result[-1]][result[0]] = 1 63 | # for i in range(num_city): 64 | # for j in range(num_city): 65 | # return result 66 | 67 | # 轮盘赌选择 68 | def rand_choose(self, p): 69 | x = np.random.rand() 70 | for i, t in enumerate(p): 71 | x -= t 72 | if x <= 0: 73 | break 74 | return i 75 | 76 | # 生成蚁群 77 | def get_ants(self, num_city): 78 | for i in range(self.m): 79 | start = np.random.randint(num_city - 1) 80 | self.Table[i][0] = start 81 | unvisit = list([x for x in range(num_city) if x != start]) 82 | current = start 83 | j = 1 84 | while len(unvisit) != 0: 85 | P = [] 86 | # 通过信息素计算城市之间的转移概率 87 | for v in unvisit: 88 | P.append(self.Tau[current][v] ** self.alpha * self.Eta[current][v] ** self.beta) 89 | P_sum = sum(P) 90 | P = [x / P_sum for x in P] 91 | # 轮盘赌选择一个一个城市 92 | index = self.rand_choose(P) 93 | current = unvisit[index] 94 | self.Table[i][j] = current 95 | unvisit.remove(current) 96 | j += 1 97 | 98 | # 计算不同城市之间的距离 99 | def compute_dis_mat(self, num_city, location): 100 | dis_mat = np.zeros((num_city, num_city)) 101 | for i in range(num_city): 102 | for j in range(num_city): 103 | if i == j: 104 | dis_mat[i][j] = np.inf 105 | continue 106 | a = location[i] 107 | b = location[j] 108 | tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)])) 109 | dis_mat[i][j] = tmp 110 | return dis_mat 111 | 112 | # 计算一条路径的长度 113 | def compute_pathlen(self, path, dis_mat): 114 | a = path[0] 115 | b = path[-1] 116 | result = dis_mat[a][b] 117 | for i in range(len(path) - 1): 118 | a = path[i] 119 | b = path[i + 1] 120 | result += dis_mat[a][b] 121 | return result 122 | 123 | # 计算一个群体的长度 124 | def compute_paths(self, paths): 125 | result = [] 126 | for one in paths: 127 | length = self.compute_pathlen(one, self.dis_mat) 128 | result.append(length) 129 | return result 130 | 131 | # 更新信息素 132 | def update_Tau(self): 133 | delta_tau = np.zeros([self.num_city, self.num_city]) 134 | paths = self.compute_paths(self.Table) 135 | for i in range(self.m): 136 | for j in range(self.num_city - 1): 137 | a = self.Table[i][j] 138 | b = self.Table[i][j + 1] 139 | delta_tau[a][b] = delta_tau[a][b] + self.Q / paths[i] 140 | a = self.Table[i][0] 141 | b = self.Table[i][-1] 142 | delta_tau[a][b] = delta_tau[a][b] + self.Q / paths[i] 143 | self.Tau = (1 - self.rho) * self.Tau + delta_tau 144 | 145 | def aco(self): 146 | best_lenth = math.inf 147 | best_path = None 148 | for cnt in range(self.iter_max): 149 | # 生成新的蚁群 150 | self.get_ants(self.num_city) # out>>self.Table 151 | self.paths = self.compute_paths(self.Table) 152 | # 取该蚁群的最优解 153 | tmp_lenth = min(self.paths) 154 | tmp_path = self.Table[self.paths.index(tmp_lenth)] 155 | # 可视化初始的路径 156 | if cnt == 0: 157 | init_show = self.location[tmp_path] 158 | init_show = np.vstack([init_show, init_show[0]]) 159 | # 更新最优解 160 | if tmp_lenth < best_lenth: 161 | best_lenth = tmp_lenth 162 | best_path = tmp_path 163 | # 更新信息素 164 | self.update_Tau() 165 | 166 | # 保存结果 167 | self.iter_x.append(cnt) 168 | self.iter_y.append(best_lenth) 169 | print(cnt,best_lenth) 170 | return best_lenth, best_path 171 | 172 | def run(self): 173 | best_length, best_path = self.aco() 174 | return self.location[best_path], best_length 175 | 176 | 177 | # 读取数据 178 | def read_tsp(path): 179 | lines = open(path, 'r').readlines() 180 | assert 'NODE_COORD_SECTION\n' in lines 181 | index = lines.index('NODE_COORD_SECTION\n') 182 | data = lines[index + 1:-1] 183 | tmp = [] 184 | for line in data: 185 | line = line.strip().split(' ') 186 | if line[0] == 'EOF': 187 | continue 188 | tmpline = [] 189 | for x in line: 190 | if x == '': 191 | continue 192 | else: 193 | tmpline.append(float(x)) 194 | if tmpline == []: 195 | continue 196 | tmp.append(tmpline) 197 | data = tmp 198 | return data 199 | 200 | 201 | data = read_tsp('data/st70.tsp') 202 | 203 | data = np.array(data) 204 | data = data[:, 1:] 205 | # 加上一行因为会回到起点 206 | show_data = np.vstack([data, data[0]]) 207 | 208 | aco = ACO(num_city=data.shape[0], data=data.copy()) 209 | Best_path, Best = aco.run() 210 | print(Best) 211 | Best_path = np.vstack([Best_path, Best_path[0]]) 212 | plt.plot(Best_path[:, 0], Best_path[:, 1]) 213 | plt.title('st70:蚁群算法规划结果') 214 | plt.show() 215 | -------------------------------------------------------------------------------- /Branch_and_Bound.py: -------------------------------------------------------------------------------- 1 | import math 2 | from queue import PriorityQueue 3 | 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | 7 | pq = PriorityQueue() 8 | 9 | 10 | class Node(object): 11 | def __init__(self, level=None, path=None, bound=None): 12 | self.level = level 13 | self.path = path 14 | self.bound = bound 15 | 16 | def __cmp__(self, other): 17 | return cmp(self.bound, other.bound) 18 | def __lt__(self, other): # operator < 19 | return self.bound < other.bound 20 | def __str__(self): 21 | return str(tuple([self.level, self.path, self.bound])) 22 | 23 | 24 | ## 动态规划法 25 | class DP(object): 26 | def __init__(self, num_city, num_total, iteration, data): 27 | self.num_city = num_city 28 | self.location = data 29 | self.dis_mat = self.compute_dis_mat(num_city, data) 30 | 31 | # 计算不同城市之间的距离 32 | def compute_dis_mat(self, num_city, location): 33 | dis_mat = np.zeros((num_city, num_city)) 34 | for i in range(num_city): 35 | for j in range(num_city): 36 | if i == j: 37 | dis_mat[i][j] = np.inf 38 | continue 39 | a = location[i] 40 | b = location[j] 41 | tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)])) 42 | dis_mat[i][j] = tmp 43 | return dis_mat 44 | 45 | # 计算路径长度, goback:是否计算回到起始点的路径 46 | def compute_pathlen(self, path, dis_mat, goback=True): 47 | try: 48 | a = path[0] 49 | b = path[-1] 50 | except: 51 | import pdb 52 | pdb.set_trace() 53 | if goback: 54 | result = dis_mat[a][b] 55 | else: 56 | result = 0.0 57 | for i in range(len(path) - 1): 58 | a = path[i] 59 | b = path[i + 1] 60 | result += dis_mat[a][b] 61 | return result 62 | 63 | def run(self,src=0): 64 | optimal_tour = [] 65 | n = len(self.dis_mat) 66 | if not n: 67 | raise ValueError("Invalid adj Matrix") 68 | u = Node() 69 | PQ = PriorityQueue() 70 | optimal_length = 0 71 | v = Node(level=0, path=[0]) 72 | min_length = float('inf') # infinity 73 | v.bound = self.bound(self.dis_mat, v) 74 | PQ.put(v) 75 | while not PQ.empty(): 76 | print(PQ.qsize()) 77 | v = PQ.get() 78 | if v.bound < min_length: 79 | u.level = v.level + 1 80 | for i in filter(lambda x: x not in v.path, range(1, n)): 81 | u.path = v.path[:] 82 | u.path.append(i) 83 | if u.level == n - 2: 84 | l = set(range(1, n)) - set(u.path) 85 | u.path.append(list(l)[0]) 86 | # putting the first vertex at last 87 | u.path.append(0) 88 | 89 | _len = self.length(self.dis_mat, u) 90 | if _len < min_length: 91 | min_length = _len 92 | optimal_length = _len 93 | optimal_tour = u.path[:] 94 | 95 | else: 96 | u.bound = self.bound(self.dis_mat, u) 97 | if u.bound < min_length: 98 | PQ.put(u) 99 | # make a new node at each iteration! python it is!! 100 | u = Node(level=u.level) 101 | 102 | # shifting to proper source(start of path) 103 | optimal_tour_src = optimal_tour 104 | if src is not 1: 105 | optimal_tour_src = optimal_tour[:-1] 106 | y = optimal_tour_src.index(src) 107 | optimal_tour_src = optimal_tour_src[y:] + optimal_tour_src[:y] 108 | optimal_tour_src.append(optimal_tour_src[0]) 109 | 110 | return optimal_tour_src, optimal_length 111 | 112 | def length(self, adj_mat, node): 113 | tour = node.path 114 | # returns the sum of two consecutive elements of tour in adj[i][j] 115 | return sum([adj_mat[tour[i]][tour[i + 1]] for i in range(len(tour) - 1)]) 116 | 117 | def bound(self, adj_mat, node): 118 | path = node.path 119 | _bound = 0 120 | 121 | n = len(adj_mat) 122 | determined, last = path[:-1], path[-1] 123 | # remain is index based 124 | remain = filter(lambda x: x not in path, range(n)) 125 | 126 | # for the edges that are certain 127 | for i in range(len(path) - 1): 128 | _bound += adj_mat[path[i]][path[i + 1]] 129 | 130 | # for the last item 131 | _bound += min([adj_mat[last][i] for i in remain]) 132 | 133 | p = [path[0]] + list(remain) 134 | # for the undetermined nodes 135 | for r in remain: 136 | _bound += min([adj_mat[r][i] for i in filter(lambda x: x != r, p)]) 137 | return _bound 138 | 139 | # 读取数据 140 | def read_tsp(path): 141 | lines = open(path, 'r').readlines() 142 | assert 'NODE_COORD_SECTION\n' in lines 143 | index = lines.index('NODE_COORD_SECTION\n') 144 | data = lines[index + 1:-1] 145 | tmp = [] 146 | for line in data: 147 | line = line.strip().split(' ') 148 | if line[0] == 'EOF': 149 | continue 150 | tmpline = [] 151 | for x in line: 152 | if x == '': 153 | continue 154 | else: 155 | tmpline.append(float(x)) 156 | if tmpline == []: 157 | continue 158 | tmp.append(tmpline) 159 | data = tmp 160 | return data 161 | 162 | 163 | data = read_tsp('data/bayg29.tsp') 164 | data = np.array(data) 165 | data = data[:, 1:] 166 | 167 | foa = DP(num_city=data.shape[0], num_total=25, iteration=500, data=data.copy()) 168 | Best_path, Best = foa.run() 169 | print('规划的路径长度:{}'.format(Best)) 170 | # 显示规划结果 171 | # plt.scatter(Best_path[:, 0], Best_path[:, 1]) 172 | # Best_path = np.vstack([Best_path, Best_path[0]]) 173 | # plt.plot(Best_path[:, 0], Best_path[:, 1]) 174 | # plt.title('规划结果') 175 | # plt.show() 176 | 177 | -------------------------------------------------------------------------------- /DP.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | 6 | 7 | ## 动态规划法 8 | class DP(object): 9 | def __init__(self, num_city, num_total, iteration, data): 10 | self.num_city = num_city 11 | self.location = data 12 | self.dis_mat = self.compute_dis_mat(num_city, data) 13 | 14 | # 计算不同城市之间的距离 15 | def compute_dis_mat(self, num_city, location): 16 | dis_mat = np.zeros((num_city, num_city)) 17 | for i in range(num_city): 18 | for j in range(num_city): 19 | if i == j: 20 | dis_mat[i][j] = np.inf 21 | continue 22 | a = location[i] 23 | b = location[j] 24 | tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)])) 25 | dis_mat[i][j] = tmp 26 | return dis_mat 27 | 28 | # 计算路径长度, goback:是否计算回到起始点的路径 29 | def compute_pathlen(self, path, dis_mat, goback=True): 30 | try: 31 | a = path[0] 32 | b = path[-1] 33 | except: 34 | import pdb 35 | pdb.set_trace() 36 | if goback: 37 | result = dis_mat[a][b] 38 | else: 39 | result = 0.0 40 | for i in range(len(path) - 1): 41 | a = path[i] 42 | b = path[i + 1] 43 | result += dis_mat[a][b] 44 | return result 45 | 46 | # 动态规划过程 47 | def run(self): 48 | restnum = [x for x in range(1, self.num_city)] 49 | tmppath = [0] 50 | tmplen = 0 51 | while len(restnum) > 0: 52 | c = restnum[0] 53 | restnum = restnum[1:] 54 | if len(tmppath) <= 1: 55 | tmppath.append(c) 56 | tmplen = self.compute_pathlen(tmppath, self.dis_mat) 57 | continue 58 | 59 | insert = 0 60 | minlen = math.inf 61 | for i, num in enumerate(tmppath): 62 | a = tmppath[-1] if i == 0 else tmppath[i - 1] 63 | b = tmppath[i] 64 | tmp1 = self.dis_mat[c][a] 65 | tmp2 = self.dis_mat[c][b] 66 | curlen = tmplen + tmp1 + tmp2 - self.dis_mat[a][b] 67 | if curlen < minlen: 68 | minlen = curlen 69 | insert = i 70 | 71 | tmppath = tmppath[0:insert] + [c] + tmppath[insert:] 72 | tmplen = minlen 73 | return self.location[tmppath], tmplen 74 | 75 | 76 | # 读取数据 77 | def read_tsp(path): 78 | lines = open(path, 'r').readlines() 79 | assert 'NODE_COORD_SECTION\n' in lines 80 | index = lines.index('NODE_COORD_SECTION\n') 81 | data = lines[index + 1:-1] 82 | tmp = [] 83 | for line in data: 84 | line = line.strip().split(' ') 85 | if line[0] == 'EOF': 86 | continue 87 | tmpline = [] 88 | for x in line: 89 | if x == '': 90 | continue 91 | else: 92 | tmpline.append(float(x)) 93 | if tmpline == []: 94 | continue 95 | tmp.append(tmpline) 96 | data = tmp 97 | return data 98 | 99 | 100 | data = read_tsp('data/st70.tsp') 101 | data = np.array(data) 102 | data = data[:, 1:] 103 | 104 | model = DP(num_city=data.shape[0], num_total=25, iteration=500, data=data.copy()) 105 | Best_path, Best = model.run() 106 | print('规划的路径长度:{}'.format(Best)) 107 | # 显示规划结果 108 | plt.scatter(Best_path[:, 0], Best_path[:, 1]) 109 | Best_path = np.vstack([Best_path, Best_path[0]]) 110 | plt.plot(Best_path[:, 0], Best_path[:, 1]) 111 | plt.title('st70:动态规划规划结果') 112 | plt.show() 113 | -------------------------------------------------------------------------------- /GA.py: -------------------------------------------------------------------------------- 1 | import random 2 | import math 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | 6 | 7 | class GA(object): 8 | def __init__(self, num_city, num_total, iteration, data): 9 | self.num_city = num_city 10 | self.num_total = num_total 11 | self.scores = [] 12 | self.iteration = iteration 13 | self.location = data 14 | self.ga_choose_ratio = 0.2 15 | self.mutate_ratio = 0.05 16 | # fruits中存每一个个体是下标的list 17 | self.dis_mat = self.compute_dis_mat(num_city, data) 18 | self.fruits = self.greedy_init(self.dis_mat,num_total,num_city) 19 | # 显示初始化后的最佳路径 20 | scores = self.compute_adp(self.fruits) 21 | sort_index = np.argsort(-scores) 22 | init_best = self.fruits[sort_index[0]] 23 | init_best = self.location[init_best] 24 | 25 | # 存储每个iteration的结果,画出收敛图 26 | self.iter_x = [0] 27 | self.iter_y = [1. / scores[sort_index[0]]] 28 | 29 | def random_init(self, num_total, num_city): 30 | tmp = [x for x in range(num_city)] 31 | result = [] 32 | for i in range(num_total): 33 | random.shuffle(tmp) 34 | result.append(tmp.copy()) 35 | return result 36 | 37 | def greedy_init(self, dis_mat, num_total, num_city): 38 | start_index = 0 39 | result = [] 40 | for i in range(num_total): 41 | rest = [x for x in range(0, num_city)] 42 | # 所有起始点都已经生成了 43 | if start_index >= num_city: 44 | start_index = np.random.randint(0, num_city) 45 | result.append(result[start_index].copy()) 46 | continue 47 | current = start_index 48 | rest.remove(current) 49 | # 找到一条最近邻路径 50 | result_one = [current] 51 | while len(rest) != 0: 52 | tmp_min = math.inf 53 | tmp_choose = -1 54 | for x in rest: 55 | if dis_mat[current][x] < tmp_min: 56 | tmp_min = dis_mat[current][x] 57 | tmp_choose = x 58 | 59 | current = tmp_choose 60 | result_one.append(tmp_choose) 61 | rest.remove(tmp_choose) 62 | result.append(result_one) 63 | start_index += 1 64 | return result 65 | # 计算不同城市之间的距离 66 | def compute_dis_mat(self, num_city, location): 67 | dis_mat = np.zeros((num_city, num_city)) 68 | for i in range(num_city): 69 | for j in range(num_city): 70 | if i == j: 71 | dis_mat[i][j] = np.inf 72 | continue 73 | a = location[i] 74 | b = location[j] 75 | tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)])) 76 | dis_mat[i][j] = tmp 77 | return dis_mat 78 | 79 | # 计算路径长度 80 | def compute_pathlen(self, path, dis_mat): 81 | try: 82 | a = path[0] 83 | b = path[-1] 84 | except: 85 | import pdb 86 | pdb.set_trace() 87 | result = dis_mat[a][b] 88 | for i in range(len(path) - 1): 89 | a = path[i] 90 | b = path[i + 1] 91 | result += dis_mat[a][b] 92 | return result 93 | 94 | # 计算种群适应度 95 | def compute_adp(self, fruits): 96 | adp = [] 97 | for fruit in fruits: 98 | if isinstance(fruit, int): 99 | import pdb 100 | pdb.set_trace() 101 | length = self.compute_pathlen(fruit, self.dis_mat) 102 | adp.append(1.0 / length) 103 | return np.array(adp) 104 | 105 | def swap_part(self, list1, list2): 106 | index = len(list1) 107 | list = list1 + list2 108 | list = list[::-1] 109 | return list[:index], list[index:] 110 | 111 | def ga_cross(self, x, y): 112 | len_ = len(x) 113 | assert len(x) == len(y) 114 | path_list = [t for t in range(len_)] 115 | order = list(random.sample(path_list, 2)) 116 | order.sort() 117 | start, end = order 118 | 119 | # 找到冲突点并存下他们的下标,x中存储的是y中的下标,y中存储x与它冲突的下标 120 | tmp = x[start:end] 121 | x_conflict_index = [] 122 | for sub in tmp: 123 | index = y.index(sub) 124 | if not (index >= start and index < end): 125 | x_conflict_index.append(index) 126 | 127 | y_confict_index = [] 128 | tmp = y[start:end] 129 | for sub in tmp: 130 | index = x.index(sub) 131 | if not (index >= start and index < end): 132 | y_confict_index.append(index) 133 | 134 | assert len(x_conflict_index) == len(y_confict_index) 135 | 136 | # 交叉 137 | tmp = x[start:end].copy() 138 | x[start:end] = y[start:end] 139 | y[start:end] = tmp 140 | 141 | # 解决冲突 142 | for index in range(len(x_conflict_index)): 143 | i = x_conflict_index[index] 144 | j = y_confict_index[index] 145 | y[i], x[j] = x[j], y[i] 146 | 147 | assert len(set(x)) == len_ and len(set(y)) == len_ 148 | return list(x), list(y) 149 | 150 | def ga_parent(self, scores, ga_choose_ratio): 151 | sort_index = np.argsort(-scores).copy() 152 | sort_index = sort_index[0:int(ga_choose_ratio * len(sort_index))] 153 | parents = [] 154 | parents_score = [] 155 | for index in sort_index: 156 | parents.append(self.fruits[index]) 157 | parents_score.append(scores[index]) 158 | return parents, parents_score 159 | 160 | def ga_choose(self, genes_score, genes_choose): 161 | sum_score = sum(genes_score) 162 | score_ratio = [sub * 1.0 / sum_score for sub in genes_score] 163 | rand1 = np.random.rand() 164 | rand2 = np.random.rand() 165 | for i, sub in enumerate(score_ratio): 166 | if rand1 >= 0: 167 | rand1 -= sub 168 | if rand1 < 0: 169 | index1 = i 170 | if rand2 >= 0: 171 | rand2 -= sub 172 | if rand2 < 0: 173 | index2 = i 174 | if rand1 < 0 and rand2 < 0: 175 | break 176 | return list(genes_choose[index1]), list(genes_choose[index2]) 177 | 178 | def ga_mutate(self, gene): 179 | path_list = [t for t in range(len(gene))] 180 | order = list(random.sample(path_list, 2)) 181 | start, end = min(order), max(order) 182 | tmp = gene[start:end] 183 | # np.random.shuffle(tmp) 184 | tmp = tmp[::-1] 185 | gene[start:end] = tmp 186 | return list(gene) 187 | 188 | def ga(self): 189 | # 获得优质父代 190 | scores = self.compute_adp(self.fruits) 191 | # 选择部分优秀个体作为父代候选集合 192 | parents, parents_score = self.ga_parent(scores, self.ga_choose_ratio) 193 | tmp_best_one = parents[0] 194 | tmp_best_score = parents_score[0] 195 | # 新的种群fruits 196 | fruits = parents.copy() 197 | # 生成新的种群 198 | while len(fruits) < self.num_total: 199 | # 轮盘赌方式对父代进行选择 200 | gene_x, gene_y = self.ga_choose(parents_score, parents) 201 | # 交叉 202 | gene_x_new, gene_y_new = self.ga_cross(gene_x, gene_y) 203 | # 变异 204 | if np.random.rand() < self.mutate_ratio: 205 | gene_x_new = self.ga_mutate(gene_x_new) 206 | if np.random.rand() < self.mutate_ratio: 207 | gene_y_new = self.ga_mutate(gene_y_new) 208 | x_adp = 1. / self.compute_pathlen(gene_x_new, self.dis_mat) 209 | y_adp = 1. / self.compute_pathlen(gene_y_new, self.dis_mat) 210 | # 将适应度高的放入种群中 211 | if x_adp > y_adp and (not gene_x_new in fruits): 212 | fruits.append(gene_x_new) 213 | elif x_adp <= y_adp and (not gene_y_new in fruits): 214 | fruits.append(gene_y_new) 215 | 216 | self.fruits = fruits 217 | 218 | return tmp_best_one, tmp_best_score 219 | 220 | def run(self): 221 | BEST_LIST = None 222 | best_score = -math.inf 223 | self.best_record = [] 224 | for i in range(1, self.iteration + 1): 225 | tmp_best_one, tmp_best_score = self.ga() 226 | self.iter_x.append(i) 227 | self.iter_y.append(1. / tmp_best_score) 228 | if tmp_best_score > best_score: 229 | best_score = tmp_best_score 230 | BEST_LIST = tmp_best_one 231 | self.best_record.append(1./best_score) 232 | print(i,1./best_score) 233 | print(1./best_score) 234 | return self.location[BEST_LIST], 1. / best_score 235 | 236 | 237 | # 读取数据 238 | def read_tsp(path): 239 | lines = open(path, 'r').readlines() 240 | assert 'NODE_COORD_SECTION\n' in lines 241 | index = lines.index('NODE_COORD_SECTION\n') 242 | data = lines[index + 1:-1] 243 | tmp = [] 244 | for line in data: 245 | line = line.strip().split(' ') 246 | if line[0] == 'EOF': 247 | continue 248 | tmpline = [] 249 | for x in line: 250 | if x == '': 251 | continue 252 | else: 253 | tmpline.append(float(x)) 254 | if tmpline == []: 255 | continue 256 | tmp.append(tmpline) 257 | data = tmp 258 | return data 259 | 260 | 261 | data = read_tsp('data/st70.tsp') 262 | 263 | data = np.array(data) 264 | data = data[:, 1:] 265 | Best, Best_path = math.inf, None 266 | 267 | model = GA(num_city=data.shape[0], num_total=25, iteration=500, data=data.copy()) 268 | path, path_len = model.run() 269 | if path_len < Best: 270 | Best = path_len 271 | Best_path = path 272 | # 加上一行因为会回到起点 273 | fig, axs = plt.subplots(2, 1, sharex=False, sharey=False) 274 | axs[0].scatter(Best_path[:, 0], Best_path[:,1]) 275 | Best_path = np.vstack([Best_path, Best_path[0]]) 276 | axs[0].plot(Best_path[:, 0], Best_path[:, 1]) 277 | axs[0].set_title('规划结果') 278 | iterations = range(model.iteration) 279 | best_record = model.best_record 280 | axs[1].plot(iterations, best_record) 281 | axs[1].set_title('收敛曲线') 282 | plt.show() 283 | 284 | 285 | -------------------------------------------------------------------------------- /PSO.py: -------------------------------------------------------------------------------- 1 | import random 2 | import math 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | 6 | 7 | class PSO(object): 8 | def __init__(self, num_city, data): 9 | self.iter_max = 500 # 迭代数目 10 | self.num = 200 # 粒子数目 11 | self.num_city = num_city # 城市数 12 | self.location = data # 城市的位置坐标 13 | # 计算距离矩阵 14 | self.dis_mat = self.compute_dis_mat(num_city, self.location) # 计算城市之间的距离矩阵 15 | # 初始化所有粒子 16 | # self.particals = self.random_init(self.num, num_city) 17 | self.particals = self.greedy_init(self.dis_mat,num_total=self.num,num_city =num_city) 18 | self.lenths = self.compute_paths(self.particals) 19 | # 得到初始化群体的最优解 20 | init_l = min(self.lenths) 21 | init_index = self.lenths.index(init_l) 22 | init_path = self.particals[init_index] 23 | # 画出初始的路径图 24 | init_show = self.location[init_path] 25 | # 记录每个个体的当前最优解 26 | self.local_best = self.particals 27 | self.local_best_len = self.lenths 28 | # 记录当前的全局最优解,长度是iteration 29 | self.global_best = init_path 30 | self.global_best_len = init_l 31 | # 输出解 32 | self.best_l = self.global_best_len 33 | self.best_path = self.global_best 34 | # 存储每次迭代的结果,画出收敛图 35 | self.iter_x = [0] 36 | self.iter_y = [init_l] 37 | def greedy_init(self, dis_mat, num_total, num_city): 38 | start_index = 0 39 | result = [] 40 | for i in range(num_total): 41 | rest = [x for x in range(0, num_city)] 42 | # 所有起始点都已经生成了 43 | if start_index >= num_city: 44 | start_index = np.random.randint(0, num_city) 45 | result.append(result[start_index].copy()) 46 | continue 47 | current = start_index 48 | rest.remove(current) 49 | # 找到一条最近邻路径 50 | result_one = [current] 51 | while len(rest) != 0: 52 | tmp_min = math.inf 53 | tmp_choose = -1 54 | for x in rest: 55 | if dis_mat[current][x] < tmp_min: 56 | tmp_min = dis_mat[current][x] 57 | tmp_choose = x 58 | 59 | current = tmp_choose 60 | result_one.append(tmp_choose) 61 | rest.remove(tmp_choose) 62 | result.append(result_one) 63 | start_index += 1 64 | return result 65 | 66 | # 随机初始化 67 | def random_init(self, num_total, num_city): 68 | tmp = [x for x in range(num_city)] 69 | result = [] 70 | for i in range(num_total): 71 | random.shuffle(tmp) 72 | result.append(tmp.copy()) 73 | return result 74 | 75 | # 计算不同城市之间的距离 76 | def compute_dis_mat(self, num_city, location): 77 | dis_mat = np.zeros((num_city, num_city)) 78 | for i in range(num_city): 79 | for j in range(num_city): 80 | if i == j: 81 | dis_mat[i][j] = np.inf 82 | continue 83 | a = location[i] 84 | b = location[j] 85 | tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)])) 86 | dis_mat[i][j] = tmp 87 | return dis_mat 88 | 89 | # 计算一条路径的长度 90 | def compute_pathlen(self, path, dis_mat): 91 | a = path[0] 92 | b = path[-1] 93 | result = dis_mat[a][b] 94 | for i in range(len(path) - 1): 95 | a = path[i] 96 | b = path[i + 1] 97 | result += dis_mat[a][b] 98 | return result 99 | 100 | # 计算一个群体的长度 101 | def compute_paths(self, paths): 102 | result = [] 103 | for one in paths: 104 | length = self.compute_pathlen(one, self.dis_mat) 105 | result.append(length) 106 | return result 107 | 108 | # 评估当前的群体 109 | def eval_particals(self): 110 | min_lenth = min(self.lenths) 111 | min_index = self.lenths.index(min_lenth) 112 | cur_path = self.particals[min_index] 113 | # 更新当前的全局最优 114 | if min_lenth < self.global_best_len: 115 | self.global_best_len = min_lenth 116 | self.global_best = cur_path 117 | # 更新当前的个体最优 118 | for i, l in enumerate(self.lenths): 119 | if l < self.local_best_len[i]: 120 | self.local_best_len[i] = l 121 | self.local_best[i] = self.particals[i] 122 | 123 | # 粒子交叉 124 | def cross(self, cur, best): 125 | one = cur.copy() 126 | l = [t for t in range(self.num_city)] 127 | t = np.random.choice(l,2) 128 | x = min(t) 129 | y = max(t) 130 | cross_part = best[x:y] 131 | tmp = [] 132 | for t in one: 133 | if t in cross_part: 134 | continue 135 | tmp.append(t) 136 | # 两种交叉方法 137 | one = tmp + cross_part 138 | l1 = self.compute_pathlen(one, self.dis_mat) 139 | one2 = cross_part + tmp 140 | l2 = self.compute_pathlen(one2, self.dis_mat) 141 | if l1= num_city: 32 | start_index = np.random.randint(0, num_city) 33 | result.append(result[start_index].copy()) 34 | continue 35 | current = start_index 36 | rest.remove(current) 37 | # 找到一条最近邻路径 38 | result_one = [current] 39 | while len(rest) != 0: 40 | tmp_min = math.inf 41 | tmp_choose = -1 42 | for x in rest: 43 | if dis_mat[current][x] < tmp_min: 44 | tmp_min = dis_mat[current][x] 45 | tmp_choose = x 46 | 47 | current = tmp_choose 48 | result_one.append(tmp_choose) 49 | rest.remove(tmp_choose) 50 | result.append(result_one) 51 | start_index += 1 52 | pathlens = self.compute_paths(result) 53 | sortindex = np.argsort(pathlens) 54 | index = sortindex[0] 55 | return result[index] 56 | 57 | # 初始化一条随机路径 58 | def random_init(self, num_city): 59 | tmp = [x for x in range(num_city)] 60 | random.shuffle(tmp) 61 | return tmp 62 | 63 | # 计算不同城市之间的距离 64 | def compute_dis_mat(self, num_city, location): 65 | dis_mat = np.zeros((num_city, num_city)) 66 | for i in range(num_city): 67 | for j in range(num_city): 68 | if i == j: 69 | dis_mat[i][j] = np.inf 70 | continue 71 | a = location[i] 72 | b = location[j] 73 | tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)])) 74 | dis_mat[i][j] = tmp 75 | return dis_mat 76 | 77 | # 计算路径长度 78 | def compute_pathlen(self, path, dis_mat): 79 | a = path[0] 80 | b = path[-1] 81 | result = dis_mat[a][b] 82 | for i in range(len(path) - 1): 83 | a = path[i] 84 | b = path[i + 1] 85 | result += dis_mat[a][b] 86 | return result 87 | 88 | # 计算一个温度下产生的一个群体的长度 89 | def compute_paths(self, paths): 90 | result = [] 91 | for one in paths: 92 | length = self.compute_pathlen(one, self.dis_mat) 93 | result.append(length) 94 | return result 95 | 96 | # 产生一个新的解:随机交换两个元素的位置 97 | def get_new_fire(self, fire): 98 | fire = fire.copy() 99 | t = [x for x in range(len(fire))] 100 | a, b = np.random.choice(t, 2) 101 | fire[a:b] = fire[a:b][::-1] 102 | return fire 103 | 104 | # 退火策略,根据温度变化有一定概率接受差的解 105 | def eval_fire(self, raw, get, temp): 106 | len1 = self.compute_pathlen(raw, self.dis_mat) 107 | len2 = self.compute_pathlen(get, self.dis_mat) 108 | dc = len2 - len1 109 | p = max(1e-1, np.exp(-dc / temp)) 110 | if len2 < len1: 111 | return get, len2 112 | elif np.random.rand() <= p: 113 | return get, len2 114 | else: 115 | return raw, len1 116 | 117 | # 模拟退火总流程 118 | def sa(self): 119 | count = 0 120 | # 记录最优解 121 | best_path = self.fire 122 | best_length = self.compute_pathlen(self.fire, self.dis_mat) 123 | 124 | while self.T0 > self.Tend: 125 | count += 1 126 | # 产生在这个温度下的随机解 127 | tmp_new = self.get_new_fire(self.fire.copy()) 128 | # 根据温度判断是否选择这个解 129 | self.fire, file_len = self.eval_fire(best_path, tmp_new, self.T0) 130 | # 更新最优解 131 | if file_len < best_length: 132 | best_length = file_len 133 | best_path = self.fire 134 | # 降低温度 135 | self.T0 *= self.rate 136 | # 记录路径收敛曲线 137 | self.iter_x.append(count) 138 | self.iter_y.append(best_length) 139 | print(count, best_length) 140 | return best_length, best_path 141 | 142 | def run(self): 143 | best_length, best_path = self.sa() 144 | return self.location[best_path], best_length 145 | 146 | 147 | # 读取数据 148 | def read_tsp(path): 149 | lines = open(path, 'r').readlines() 150 | assert 'NODE_COORD_SECTION\n' in lines 151 | index = lines.index('NODE_COORD_SECTION\n') 152 | data = lines[index + 1:-1] 153 | tmp = [] 154 | for line in data: 155 | line = line.strip().split(' ') 156 | if line[0] == 'EOF': 157 | continue 158 | tmpline = [] 159 | for x in line: 160 | if x == '': 161 | continue 162 | else: 163 | tmpline.append(float(x)) 164 | if tmpline == []: 165 | continue 166 | tmp.append(tmpline) 167 | data = tmp 168 | return data 169 | 170 | 171 | data = read_tsp('data/st70.tsp') 172 | 173 | data = np.array(data) 174 | data = data[:, 1:] 175 | show_data = np.vstack([data, data[0]]) 176 | Best, Best_path = math.inf, None 177 | 178 | model = SA(num_city=data.shape[0], data=data.copy()) 179 | path, path_len = model.run() 180 | print(path_len) 181 | if path_len < Best: 182 | Best = path_len 183 | Best_path = path 184 | # 加上一行因为会回到起点 185 | Best_path = np.vstack([Best_path, Best_path[0]]) 186 | fig, axs = plt.subplots(2, 1, sharex=False, sharey=False) 187 | axs[0].scatter(Best_path[:, 0], Best_path[:,1]) 188 | Best_path = np.vstack([Best_path, Best_path[0]]) 189 | axs[0].plot(Best_path[:, 0], Best_path[:, 1]) 190 | axs[0].set_title('规划结果') 191 | iterations = model.iter_x 192 | best_record = model.iter_y 193 | axs[1].plot(iterations, best_record) 194 | axs[1].set_title('收敛曲线') 195 | plt.show() 196 | 197 | -------------------------------------------------------------------------------- /SOM.py: -------------------------------------------------------------------------------- 1 | ''' 2 | notice: this code is referenced by other 3 | https://github.com/DiegoVicen/som-tsp 4 | ''' 5 | import random 6 | import math 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | 10 | 11 | class SOM(object): 12 | def __init__(self, num_city, data): 13 | self.num_city = num_city 14 | self.location = data.copy() 15 | self.iteraton = 8000 16 | self.learning_rate = 0.8 17 | self.dis_mat = self.compute_dis_mat(num_city, self.location) 18 | self.best_path = [] 19 | self.best_length = math.inf 20 | self.iter_x = [] 21 | self.iter_y = [] 22 | 23 | def normalize(self, points): 24 | """ 25 | Return the normalized version of a given vector of points. 26 | For a given array of n-dimensions, normalize each dimension by removing the 27 | initial offset and normalizing the points in a proportional interval: [0,1] 28 | on y, maintining the original ratio on x. 29 | """ 30 | ratio = (points[:, 0].max() - points[:, 1].min()) / (points[:, 1].max() - points[:, 1].min()), 1 31 | ratio = np.array(ratio) / max(ratio) 32 | m = lambda c: (c - c.min()) / (c.max() - c.min()) 33 | norm = m(points) 34 | # norm = points.apply(lambda c: (c - c.min()) / (c.max() - c.min())) 35 | m = lambda p: ratio * p 36 | return m(norm) 37 | # return norm.apply(lambda p: ratio * p, axis=1) 38 | 39 | def generate_network(self, size): 40 | """ 41 | Generate a neuron network of a given size. 42 | Return a vector of two dimensional points in the interval [0,1]. 43 | """ 44 | return np.random.rand(size, 2) 45 | 46 | def get_neighborhood(self, center, radix, domain): 47 | """Get the range gaussian of given radix around a center index.""" 48 | 49 | # Impose an upper bound on the radix to prevent NaN and blocks 50 | if radix < 1: 51 | radix = 1 52 | 53 | # Compute the circular network distance to the center 54 | deltas = np.absolute(center - np.arange(domain)) 55 | distances = np.minimum(deltas, domain - deltas) 56 | 57 | # Compute Gaussian distribution around the given center 58 | return np.exp(-(distances * distances) / (2 * (radix * radix))) 59 | 60 | def get_route(self, cities, network): 61 | """Return the route computed by a network.""" 62 | f = lambda c: self.select_closest(network, c) 63 | dis = [] 64 | for city in cities: 65 | dis.append(f(city)) 66 | index = np.argsort(dis) 67 | return index 68 | 69 | def select_closest(self, candidates, origin): 70 | """Return the index of the closest candidate to a given point.""" 71 | return self.euclidean_distance(candidates, origin).argmin() 72 | 73 | def euclidean_distance(self, a, b): 74 | """Return the array of distances of two numpy arrays of points.""" 75 | return np.linalg.norm(a - b, axis=1) 76 | 77 | def route_distance(self, cities): 78 | """Return the cost of traversing a route of cities in a certain order.""" 79 | points = cities[['x', 'y']] 80 | distances = self.euclidean_distance(points, np.roll(points, 1, axis=0)) 81 | return np.sum(distances) 82 | 83 | # 随机初始化 84 | def random_init(self, num_total, num_city): 85 | tmp = [x for x in range(num_city)] 86 | result = [] 87 | for i in range(num_total): 88 | random.shuffle(tmp) 89 | result.append(tmp.copy()) 90 | return result 91 | 92 | # 计算不同城市之间的距离 93 | def compute_dis_mat(self, num_city, location): 94 | dis_mat = np.zeros((num_city, num_city)) 95 | for i in range(num_city): 96 | for j in range(num_city): 97 | if i == j: 98 | dis_mat[i][j] = np.inf 99 | continue 100 | a = location[i] 101 | b = location[j] 102 | tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)])) 103 | dis_mat[i][j] = tmp 104 | return dis_mat 105 | 106 | # 计算一条路径的长度 107 | def compute_pathlen(self, path, dis_mat): 108 | a = path[0] 109 | b = path[-1] 110 | result = dis_mat[a][b] 111 | for i in range(len(path) - 1): 112 | a = path[i] 113 | b = path[i + 1] 114 | result += dis_mat[a][b] 115 | return result 116 | 117 | def smo(self): 118 | citys = self.normalize(self.location) 119 | n = citys.shape[0] * 8 120 | network = self.generate_network(n) 121 | 122 | for i in range(self.iteraton): 123 | index = np.random.randint(self.num_city - 1) 124 | city = citys[index] 125 | winner_idx = self.select_closest(network, city) 126 | 127 | gaussian = self.get_neighborhood(winner_idx, n // 10, network.shape[0]) 128 | 129 | network += gaussian[:, np.newaxis] * self.learning_rate * (city - network) 130 | 131 | self.learning_rate = self.learning_rate * 0.99997 132 | n = n * 0.9997 133 | if n < 1: 134 | break 135 | route = self.get_route(citys, network) 136 | route_l = self.compute_pathlen(route, self.dis_mat) 137 | if route_l < self.best_length: 138 | self.best_length = route_l 139 | self.best_path = route 140 | self.iter_x.append(i) 141 | self.iter_y.append(self.best_length) 142 | print(i, self.iteraton, self.best_length) 143 | # 画出初始化的路径 144 | if i == 0: 145 | plt.subplot(2, 2, 2) 146 | plt.title('convergence curve') 147 | show_data = self.location[self.best_path] 148 | show_data = np.vstack([show_data, show_data[0]]) 149 | plt.plot(show_data[:, 0], show_data[:, 1]) 150 | 151 | return self.best_length, self.best_path 152 | 153 | def run(self): 154 | self.best_length, self.best_path = self.smo() 155 | return self.location[self.best_path], self.best_length 156 | 157 | 158 | # 读取数据 159 | def read_tsp(path): 160 | lines = open(path, 'r').readlines() 161 | assert 'NODE_COORD_SECTION\n' in lines 162 | index = lines.index('NODE_COORD_SECTION\n') 163 | data = lines[index + 1:-1] 164 | tmp = [] 165 | for line in data: 166 | line = line.strip().split(' ') 167 | if line[0] == 'EOF': 168 | continue 169 | tmpline = [] 170 | for x in line: 171 | if x == '': 172 | continue 173 | else: 174 | tmpline.append(float(x)) 175 | if tmpline == []: 176 | continue 177 | tmp.append(tmpline) 178 | data = tmp 179 | return data 180 | 181 | 182 | data = read_tsp('data/st70.tsp') 183 | 184 | data = np.array(data) 185 | plt.suptitle('PSO in st70.tsp') 186 | data = data[:, 1:] 187 | plt.subplot(2, 2, 1) 188 | plt.title('raw data') 189 | # 加上一行因为会回到起点 190 | show_data = np.vstack([data, data[0]]) 191 | plt.plot(data[:, 0], data[:, 1]) 192 | 193 | model = SOM(num_city=data.shape[0], data=data.copy()) 194 | Best_path, Best_length = model.run() 195 | 196 | 197 | Best_path = np.vstack([Best_path, Best_path[0]]) 198 | fig, axs = plt.subplots(2, 1, sharex=False, sharey=False) 199 | axs[0].scatter(Best_path[:, 0], Best_path[:,1]) 200 | Best_path = np.vstack([Best_path, Best_path[0]]) 201 | axs[0].plot(Best_path[:, 0], Best_path[:, 1]) 202 | axs[0].set_title('规划结果') 203 | iterations = model.iter_x 204 | best_record = model.iter_y 205 | axs[1].plot(iterations, best_record) 206 | axs[1].set_title('收敛曲线') 207 | plt.show() 208 | -------------------------------------------------------------------------------- /TS.py: -------------------------------------------------------------------------------- 1 | import random 2 | import math 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | 6 | 7 | class TS(object): 8 | def __init__(self, num_city, data): 9 | self.taboo_size = 5 10 | self.iteration = 500 11 | self.num_city = num_city 12 | self.location = data 13 | self.taboo = [] 14 | 15 | self.dis_mat = self.compute_dis_mat(num_city, data) 16 | self.path = self.greedy_init(self.dis_mat,100,num_city) 17 | self.best_path = self.path 18 | self.cur_path = self.path 19 | self.best_length = self.compute_pathlen(self.path, self.dis_mat) 20 | 21 | # 显示初始化后的路径 22 | init_pathlen = 1. / self.compute_pathlen(self.path, self.dis_mat) 23 | # 存储结果,画出收敛图 24 | self.iter_x = [0] 25 | self.iter_y = [1. / init_pathlen] 26 | def greedy_init(self, dis_mat, num_total, num_city): 27 | start_index = 0 28 | result = [] 29 | for i in range(num_total): 30 | rest = [x for x in range(0, num_city)] 31 | # 所有起始点都已经生成了 32 | if start_index >= num_city: 33 | start_index = np.random.randint(0, num_city) 34 | result.append(result[start_index].copy()) 35 | continue 36 | current = start_index 37 | rest.remove(current) 38 | # 找到一条最近邻路径 39 | result_one = [current] 40 | while len(rest) != 0: 41 | tmp_min = math.inf 42 | tmp_choose = -1 43 | for x in rest: 44 | if dis_mat[current][x] < tmp_min: 45 | tmp_min = dis_mat[current][x] 46 | tmp_choose = x 47 | 48 | current = tmp_choose 49 | result_one.append(tmp_choose) 50 | rest.remove(tmp_choose) 51 | result.append(result_one) 52 | start_index += 1 53 | pathlens = self.compute_paths(result) 54 | sortindex = np.argsort(pathlens) 55 | index = sortindex[0] 56 | return result[index] 57 | # return result[0] 58 | 59 | # 初始化一条随机路径 60 | def random_init(self, num_city): 61 | tmp = [x for x in range(num_city)] 62 | random.shuffle(tmp) 63 | return tmp 64 | 65 | # 计算不同城市之间的距离 66 | def compute_dis_mat(self, num_city, location): 67 | dis_mat = np.zeros((num_city, num_city)) 68 | for i in range(num_city): 69 | for j in range(num_city): 70 | if i == j: 71 | dis_mat[i][j] = np.inf 72 | continue 73 | a = location[i] 74 | b = location[j] 75 | tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)])) 76 | dis_mat[i][j] = tmp 77 | return dis_mat 78 | 79 | # 计算路径长度 80 | def compute_pathlen(self, path, dis_mat): 81 | a = path[0] 82 | b = path[-1] 83 | result = dis_mat[a][b] 84 | for i in range(len(path) - 1): 85 | a = path[i] 86 | b = path[i + 1] 87 | result += dis_mat[a][b] 88 | return result 89 | 90 | # 计算一个群体的长度 91 | def compute_paths(self, paths): 92 | result = [] 93 | for one in paths: 94 | length = self.compute_pathlen(one, self.dis_mat) 95 | result.append(length) 96 | return result 97 | 98 | # 产生随机解 99 | def ts_search(self, x): 100 | moves = [] 101 | new_paths = [] 102 | while len(new_paths)<400: 103 | i = np.random.randint(len(x)) 104 | j = np.random.randint(len(x)) 105 | tmp = x.copy() 106 | tmp[i:j] = tmp[i:j][::-1] 107 | new_paths.append(tmp) 108 | moves.append([i, j]) 109 | return new_paths, moves 110 | 111 | # 禁忌搜索 112 | def ts(self): 113 | for cnt in range(self.iteration): 114 | new_paths, moves = self.ts_search(self.cur_path) 115 | new_lengths = self.compute_paths(new_paths) 116 | sort_index = np.argsort(new_lengths) 117 | min_l = new_lengths[sort_index[0]] 118 | min_path = new_paths[sort_index[0]] 119 | min_move = moves[sort_index[0]] 120 | 121 | # 更新当前的最优路径 122 | if min_l < self.best_length: 123 | self.best_length = min_l 124 | self.best_path = min_path 125 | self.cur_path = min_path 126 | # 更新禁忌表 127 | if min_move in self.taboo: 128 | self.taboo.remove(min_move) 129 | 130 | self.taboo.append(min_move) 131 | else: 132 | # 找到不在禁忌表中的操作 133 | while min_move in self.taboo: 134 | sort_index = sort_index[1:] 135 | min_path = new_paths[sort_index[0]] 136 | min_move = moves[sort_index[0]] 137 | self.cur_path = min_path 138 | self.taboo.append(min_move) 139 | # 禁忌表超长了 140 | if len(self.taboo) > self.taboo_size: 141 | self.taboo = self.taboo[1:] 142 | self.iter_x.append(cnt) 143 | self.iter_y.append(self.best_length) 144 | print(cnt, self.best_length) 145 | print(self.best_length) 146 | 147 | def run(self): 148 | self.ts() 149 | return self.location[self.best_path], self.best_length 150 | 151 | 152 | # 读取数据 153 | def read_tsp(path): 154 | lines = open(path, 'r').readlines() 155 | assert 'NODE_COORD_SECTION\n' in lines 156 | index = lines.index('NODE_COORD_SECTION\n') 157 | data = lines[index + 1:-1] 158 | tmp = [] 159 | for line in data: 160 | line = line.strip().split(' ') 161 | if line[0] == 'EOF': 162 | continue 163 | tmpline = [] 164 | for x in line: 165 | if x == '': 166 | continue 167 | else: 168 | tmpline.append(float(x)) 169 | if tmpline == []: 170 | continue 171 | tmp.append(tmpline) 172 | data = tmp 173 | return data 174 | 175 | 176 | data = read_tsp('data/st70.tsp') 177 | 178 | data = np.array(data) 179 | plt.suptitle('TS in st70.tsp') 180 | data = data[:, 1:] 181 | plt.subplot(2, 2, 1) 182 | plt.title('raw data') 183 | show_data = np.vstack([data, data[0]]) 184 | plt.plot(data[:, 0], data[:, 1]) 185 | 186 | model = TS(num_city=data.shape[0], data=data.copy()) 187 | Best_path, Best_length = model.run() 188 | 189 | Best_path = np.vstack([Best_path, Best_path[0]]) 190 | fig, axs = plt.subplots(2, 1, sharex=False, sharey=False) 191 | axs[0].scatter(Best_path[:, 0], Best_path[:,1]) 192 | Best_path = np.vstack([Best_path, Best_path[0]]) 193 | axs[0].plot(Best_path[:, 0], Best_path[:, 1]) 194 | axs[0].set_title('规划结果') 195 | iterations = model.iter_x 196 | best_record = model.iter_y 197 | axs[1].plot(iterations, best_record) 198 | axs[1].set_title('收敛曲线') 199 | plt.show() 200 | 201 | -------------------------------------------------------------------------------- /data/st70.tsp: -------------------------------------------------------------------------------- 1 | NAME: st70 2 | TYPE: TSP 3 | COMMENT: 70-city problem (Smith/Thompson) 4 | DIMENSION: 70 5 | EDGE_WEIGHT_TYPE : EUC_2D 6 | NODE_COORD_SECTION 7 | 1 64 96 8 | 2 80 39 9 | 3 69 23 10 | 4 72 42 11 | 5 48 67 12 | 6 58 43 13 | 7 81 34 14 | 8 79 17 15 | 9 30 23 16 | 10 42 67 17 | 11 7 76 18 | 12 29 51 19 | 13 78 92 20 | 14 64 8 21 | 15 95 57 22 | 16 57 91 23 | 17 40 35 24 | 18 68 40 25 | 19 92 34 26 | 20 62 1 27 | 21 28 43 28 | 22 76 73 29 | 23 67 88 30 | 24 93 54 31 | 25 6 8 32 | 26 87 18 33 | 27 30 9 34 | 28 77 13 35 | 29 78 94 36 | 30 55 3 37 | 31 82 88 38 | 32 73 28 39 | 33 20 55 40 | 34 27 43 41 | 35 95 86 42 | 36 67 99 43 | 37 48 83 44 | 38 75 81 45 | 39 8 19 46 | 40 20 18 47 | 41 54 38 48 | 42 63 36 49 | 43 44 33 50 | 44 52 18 51 | 45 12 13 52 | 46 25 5 53 | 47 58 85 54 | 48 5 67 55 | 49 90 9 56 | 50 41 76 57 | 51 25 76 58 | 52 37 64 59 | 53 56 63 60 | 54 10 55 61 | 55 98 7 62 | 56 16 74 63 | 57 89 60 64 | 58 48 82 65 | 59 81 76 66 | 60 29 60 67 | 61 17 22 68 | 62 5 45 69 | 63 79 70 70 | 64 9 100 71 | 65 17 82 72 | 66 74 67 73 | 67 10 68 74 | 68 48 19 75 | 69 83 86 76 | 70 84 94 77 | EOF 78 | -------------------------------------------------------------------------------- /img/ACO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kellenf/TSP_collection/77b41dc51b1053c3eafef1f3971e8994e8fce7bd/img/ACO.png -------------------------------------------------------------------------------- /img/DP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kellenf/TSP_collection/77b41dc51b1053c3eafef1f3971e8994e8fce7bd/img/DP.png -------------------------------------------------------------------------------- /img/GA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kellenf/TSP_collection/77b41dc51b1053c3eafef1f3971e8994e8fce7bd/img/GA.png -------------------------------------------------------------------------------- /img/PSO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kellenf/TSP_collection/77b41dc51b1053c3eafef1f3971e8994e8fce7bd/img/PSO.png -------------------------------------------------------------------------------- /img/SA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kellenf/TSP_collection/77b41dc51b1053c3eafef1f3971e8994e8fce7bd/img/SA.png -------------------------------------------------------------------------------- /img/SOM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kellenf/TSP_collection/77b41dc51b1053c3eafef1f3971e8994e8fce7bd/img/SOM.png -------------------------------------------------------------------------------- /img/TS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kellenf/TSP_collection/77b41dc51b1053c3eafef1f3971e8994e8fce7bd/img/TS.png --------------------------------------------------------------------------------