diff --git a/CompareWithGA.py b/CompareWithGA.py new file mode 100644 index 0000000..e133473 --- /dev/null +++ b/CompareWithGA.py @@ -0,0 +1,306 @@ +import random +import math +import numpy as np +import matplotlib.pyplot as plt +import time + +from TS import TS + + +class GA(object): + def __init__(self, num_city, num_total, iteration, data,mutate_ratio=0.05): + self.num_city = num_city + self.num_total = num_total + self.scores = [] + self.iteration = iteration + self.location = data + self.ga_choose_ratio = 0.2 + self.mutate_ratio = mutate_ratio + # fruits中存每一个个体是下标的list + self.dis_mat = self.compute_dis_mat(num_city, data) + self.fruits = self.greedy_init(self.dis_mat,num_total,num_city) + # 显示初始化后的最佳路径 + scores = self.compute_adp(self.fruits) + sort_index = np.argsort(-scores) + init_best = self.fruits[sort_index[0]] + init_best = self.location[init_best] + + # 存储每个iteration的结果,画出收敛图 + self.iter_x = [0] + self.iter_y = [1. / scores[sort_index[0]]] + + def random_init(self, num_total, num_city): + tmp = [x for x in range(num_city)] + result = [] + for i in range(num_total): + random.shuffle(tmp) + result.append(tmp.copy()) + return result + + 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 + return result + # 计算不同城市之间的距离 + 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): + try: + a = path[0] + b = path[-1] + except: + import pdb + pdb.set_trace() + 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_adp(self, fruits): + adp = [] + for fruit in fruits: + if isinstance(fruit, int): + import pdb + pdb.set_trace() + length = self.compute_pathlen(fruit, self.dis_mat) + adp.append(1.0 / length) + return np.array(adp) + + def swap_part(self, list1, list2): + index = len(list1) + list = list1 + list2 + list = list[::-1] + return list[:index], list[index:] + + def ga_cross(self, x, y): + len_ = len(x) + assert len(x) == len(y) + path_list = [t for t in range(len_)] + order = list(random.sample(path_list, 2)) + order.sort() + start, end = order + + # 找到冲突点并存下他们的下标,x中存储的是y中的下标,y中存储x与它冲突的下标 + tmp = x[start:end] + x_conflict_index = [] + for sub in tmp: + index = y.index(sub) + if not (index >= start and index < end): + x_conflict_index.append(index) + + y_confict_index = [] + tmp = y[start:end] + for sub in tmp: + index = x.index(sub) + if not (index >= start and index < end): + y_confict_index.append(index) + + assert len(x_conflict_index) == len(y_confict_index) + + # 交叉 + tmp = x[start:end].copy() + x[start:end] = y[start:end] + y[start:end] = tmp + + # 解决冲突 + for index in range(len(x_conflict_index)): + i = x_conflict_index[index] + j = y_confict_index[index] + y[i], x[j] = x[j], y[i] + + assert len(set(x)) == len_ and len(set(y)) == len_ + return list(x), list(y) + + def ga_parent(self, scores, ga_choose_ratio): + sort_index = np.argsort(-scores).copy() + sort_index = sort_index[0:int(ga_choose_ratio * len(sort_index))] + parents = [] + parents_score = [] + for index in sort_index: + parents.append(self.fruits[index]) + parents_score.append(scores[index]) + return parents, parents_score + + def ga_choose(self, genes_score, genes_choose): + sum_score = sum(genes_score) + score_ratio = [sub * 1.0 / sum_score for sub in genes_score] + rand1 = np.random.rand() + rand2 = np.random.rand() + for i, sub in enumerate(score_ratio): + if rand1 >= 0: + rand1 -= sub + if rand1 < 0: + index1 = i + if rand2 >= 0: + rand2 -= sub + if rand2 < 0: + index2 = i + if rand1 < 0 and rand2 < 0: + break + return list(genes_choose[index1]), list(genes_choose[index2]) + + def ga_mutate(self, gene): + path_list = [t for t in range(len(gene))] + order = list(random.sample(path_list, 2)) + start, end = min(order), max(order) + tmp = gene[start:end] + # np.random.shuffle(tmp) + tmp = tmp[::-1] + gene[start:end] = tmp + return list(gene) + + def ga(self): + # 获得优质父代 + scores = self.compute_adp(self.fruits) + # 选择部分优秀个体作为父代候选集合 + parents, parents_score = self.ga_parent(scores, self.ga_choose_ratio) + tmp_best_one = parents[0] + tmp_best_score = parents_score[0] + # 新的种群fruits + fruits = parents.copy() + # 生成新的种群 + while len(fruits) < self.num_total: + # 轮盘赌方式对父代进行选择 + gene_x, gene_y = self.ga_choose(parents_score, parents) + # 交叉 + gene_x_new, gene_y_new = self.ga_cross(gene_x, gene_y) + # 变异 + if np.random.rand() < self.mutate_ratio: + gene_x_new = self.ga_mutate(gene_x_new) + if np.random.rand() < self.mutate_ratio: + gene_y_new = self.ga_mutate(gene_y_new) + x_adp = 1. / self.compute_pathlen(gene_x_new, self.dis_mat) + y_adp = 1. / self.compute_pathlen(gene_y_new, self.dis_mat) + # 将适应度高的放入种群中 + if x_adp > y_adp and (not gene_x_new in fruits): + fruits.append(gene_x_new) + elif x_adp <= y_adp and (not gene_y_new in fruits): + fruits.append(gene_y_new) + + self.fruits = fruits + + return tmp_best_one, tmp_best_score + + def run(self): + start_time = time.time() + BEST_LIST = None + best_score = -math.inf + self.best_record = [] + self.iter_x = [] # 初始化iter_x为空 + for i in range(1, self.iteration + 1): + tmp_best_one, tmp_best_score = self.ga() + self.iter_x.append(i) # 记录迭代次数1~500 + if tmp_best_score > best_score: + best_score = tmp_best_score + BEST_LIST = tmp_best_one + self.best_record.append(1. / best_score) + total_time = time.time() - start_time + return self.location[BEST_LIST], 1. / best_score, 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 compare_algorithms(): + # 数据集加载 + data = read_tsp('data/st70.tsp') + data = np.array(data)[:, 1:] + + # TS参数 + ts_params = {'taboo_size': 5, 'iteration': 500} + + # GA参数(优化后) + ga_params = {'num_total': 50, 'iteration': 500, 'mutate_ratio': 0.1} + + # 运行TS + ts_model = TS(num_city=data.shape[0], data=data.copy(), **ts_params) + ts_path, ts_length, ts_time = ts_model.run() + + # 运行GA + ga_model = GA(num_city=data.shape[0], data=data.copy(), **ga_params) + ga_path, ga_length, ga_time = ga_model.run() + + # 结果输出 + print(f"TS: 路径长度={ts_length:.2f}, 时间={ts_time:.2f}s") + print(f"GA: 路径长度={ga_length:.2f}, 时间={ga_time:.2f}s") + + # 收敛曲线对比 + plt.figure() + plt.plot(ts_model.iter_x, ts_model.iter_y, label='TS') + plt.plot(ga_model.iter_x, ga_model.best_record, label='GA') + plt.xlabel('迭代次数') + plt.ylabel('路径长度') + plt.legend() + plt.title('TS vs GA 收敛曲线对比') + plt.show() + +if __name__ == "__main__": + data = read_tsp('data/st70.tsp') + + data = np.array(data) + data = data[:, 1:] + Best, Best_path = math.inf, None + # 执行对比 + compare_algorithms() + +