diff --git a/TS.py b/TS.py new file mode 100644 index 0000000..64ea284 --- /dev/null +++ b/TS.py @@ -0,0 +1,240 @@ +import random +import math +import numpy as np +import matplotlib.pyplot as plt +import time + +class TS(object): + def __init__(self, num_city, data,taboo_size=5, iteration=500): + self.taboo_size = taboo_size + self.iteration = iteration + self.num_city = num_city + self.location = data + self.taboo = [] + + self.dis_mat = self.compute_dis_mat(num_city, data) + self.path = self.greedy_init(self.dis_mat,100,num_city) + self.best_path = self.path + self.cur_path = self.path + self.best_length = self.compute_pathlen(self.path, self.dis_mat) + + # 显示初始化后的路径 + init_pathlen = 1. / self.compute_pathlen(self.path, self.dis_mat) + # 存储结果,画出收敛图 + self.iter_x = [0] + self.iter_y = [1. / init_pathlen] + def greedy_init(self, dis_mat, num_total, num_city): + start_index = 0 + result = [] + for i in range(num_total): + rest = [x for x in range(0, num_city)] + if start_index >= num_city: + start_index = np.random.randint(0, num_city) + result.append(result[start_index].copy()) + continue + current = start_index + rest.remove(current) + result_one = [current] # 找到一条最近邻路径 + while len(rest) != 0: + tmp_min = math.inf + tmp_choose = -1 + for x in rest: + if dis_mat[current][x] < tmp_min: + tmp_min = dis_mat[current][x] + tmp_choose = x + current = tmp_choose + result_one.append(tmp_choose) + rest.remove(tmp_choose) + result.append(result_one) + start_index += 1 + pathlens = self.compute_paths(result) + sortindex = np.argsort(pathlens) + index = sortindex[0] + return result[index] + # return result[0] + + # 初始化一条随机路径 + def random_init(self, num_city): + tmp = [x for x in range(num_city)] + random.shuffle(tmp) + return tmp + + # 计算不同城市之间的距离 + def compute_dis_mat(self, num_city, location): + dis_mat = np.zeros((num_city, num_city)) + for i in range(num_city): + for j in range(num_city): + if i == j: + dis_mat[i][j] = np.inf + continue + a = location[i] + b = location[j] + tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)])) + dis_mat[i][j] = tmp + return dis_mat + + # 计算路径长度 + def compute_pathlen(self, path, dis_mat): + a = path[0] + b = path[-1] + result = dis_mat[a][b] + for i in range(len(path) - 1): + a = path[i] + b = path[i + 1] + result += dis_mat[a][b] + return result + + # 计算一个群体的长度 + def compute_paths(self, paths): + result = [] + for one in paths: + length = self.compute_pathlen(one, self.dis_mat) + result.append(length) + return result + + # 产生随机解 + def ts_search(self, x): + moves = [] + new_paths = [] + while len(new_paths)<400: + i = np.random.randint(len(x)) + j = np.random.randint(len(x)) + tmp = x.copy() + tmp[i:j] = tmp[i:j][::-1] + new_paths.append(tmp) + moves.append([i, j]) + return new_paths, moves + + # 禁忌搜索 + def ts(self): + start_time = time.time() + for cnt in range(self.iteration): + new_paths, moves = self.ts_search(self.cur_path) + new_lengths = self.compute_paths(new_paths) + sort_index = np.argsort(new_lengths) + min_l = new_lengths[sort_index[0]] + min_path = new_paths[sort_index[0]] + min_move = moves[sort_index[0]] + + # 更新当前的最优路径 + if min_l < self.best_length: + self.best_length = min_l + self.best_path = min_path + self.cur_path = min_path + # 更新禁忌表 + if min_move in self.taboo: + self.taboo.remove(min_move) + + self.taboo.append(min_move) + else: + # 找到不在禁忌表中的操作 + while min_move in self.taboo: + sort_index = sort_index[1:] + min_path = new_paths[sort_index[0]] + min_move = moves[sort_index[0]] + self.cur_path = min_path + self.taboo.append(min_move) + # 禁忌表超长了 + if len(self.taboo) > self.taboo_size: + self.taboo = self.taboo[1:] + self.iter_x.append(cnt) + self.iter_y.append(self.best_length) + #print(cnt, self.best_length) + print(self.best_length) + total_time = time.time() - start_time + return self.best_length, total_time + + def run(self): + best_length, total_time = self.ts() + return self.location[self.best_path], best_length, total_time + +def read_tsp(path): + lines = open(path, 'r').readlines() + assert 'NODE_COORD_SECTION\n' in lines + index = lines.index('NODE_COORD_SECTION\n') + data = lines[index + 1:-1] + tmp = [] + for line in data: + line = line.strip().split(' ') + if line[0] == 'EOF': + continue + tmpline = [] + for x in line: + if x == '': + continue + else: + tmpline.append(float(x)) + if tmpline == []: + continue + tmp.append(tmpline) + data = tmp + return data + +def parameter_sensitivity_test(): + # 定义参数组合 + taboo_sizes = [3, 5, 10] + iterations = [200, 500, 1000] + results = [] + + # 遍历所有参数组合 + for taboo_size in taboo_sizes: + for iteration in iterations: + model = TS(num_city=data.shape[0], data=data.copy(), + taboo_size=taboo_size, iteration=iteration) + best_length, total_time = model.ts() # 调用ts()并获取结果 + results.append({ + "taboo_size": taboo_size, + "iteration": iteration, + "best_length": best_length, + "time": total_time + }) + print(f"taboo_size={taboo_size}, iteration={iteration}, " + f"length={best_length:.2f}, time={total_time:.2f}s") + + # 结果可视化(示例:热力图) + import pandas as pd + import seaborn as sns + df = pd.DataFrame(results) + pivot_length = df.pivot(index="taboo_size", columns="iteration", values="best_length") + pivot_time = df.pivot(index="taboo_size", columns="iteration", values="time") + + plt.figure(figsize=(12, 5)) + plt.subplot(1, 2, 1) + sns.heatmap(pivot_length, annot=True, fmt=".2f", cmap="YlGnBu") + plt.title("Path Length vs Parameters") + plt.subplot(1, 2, 2) + sns.heatmap(pivot_time, annot=True, fmt=".2f", cmap="YlGnBu") + plt.title("Time Cost vs Parameters") + plt.tight_layout() + plt.show() + +if __name__ == "__main__": + data = read_tsp('data/st70.tsp') + + data = np.array(data) + plt.suptitle('TS in st70.tsp') + data = data[:, 1:] + plt.subplot(2, 2, 1) + plt.title('raw data') + show_data = np.vstack([data, data[0]]) + plt.plot(data[:, 0], data[:, 1]) + + model = TS(num_city=data.shape[0], data=data.copy()) + Best_path, Best_length,execution_time = model.run() + print("时间:",execution_time) + + Best_path = np.vstack([Best_path, Best_path[0]]) + fig, axs = plt.subplots(2, 1, sharex=False, sharey=False) + axs[0].scatter(Best_path[:, 0], Best_path[:,1]) + Best_path = np.vstack([Best_path, Best_path[0]]) + axs[0].plot(Best_path[:, 0], Best_path[:, 1]) + axs[0].set_title('规划结果') + iterations = model.iter_x + best_record = model.iter_y + axs[1].plot(iterations, best_record) + axs[1].set_title('收敛曲线') + #plt.show() + plt.show(block=False) # 添加此行,允许后续代码继续执行 + #参数敏感性实验 + parameter_sensitivity_test() +