当前位置: 首页 > ai >正文

【建模算法】基于遗传算法求解TSP问题(Python实现)

【建模算法】基于遗传算法求解TSP问题(Python实现)

TSP (traveling salesman problem,旅行商问题)是典型的NP完全问题,即其最坏情况下的时间复杂度随着问题规模的增大按指数方式增长,到目前为止还未找到一个多项式时间的有效算法。本文探讨了基于遗传算法求解TSP问题的Python实现。

一、问题描述

​ 本案例以31个城市为例,假定31个城市的位置坐标如表1所列。寻找出一条最短的遍历31个城市的路径。

城市编号X坐标Y坐标城市编号X坐标Y坐标
11.3042.312173.9182.179
23.6391.315184.0612.37
34.1772.244193.782.212
43.7121.399203.6762.578
53.4881.535214.0292.838
63.3261.556224.2632.931
73.2381.229233.4291.908
84.1961.044243.5072.376
94.3120.79253.3942.643
104.3860.57263.4393.201
113.0071.97272.9353.24
122.5621.756283.143.55
132.7881.491292.5452.357
142.3811.676302.7782.826
151.3320.695312.372.975
163.7151.678

二、解题思路及步骤

1.遗传算法步骤:

第一步:初始化 t←0进化代数计数器;T是最大进化代数(也可以没有);随机生成M个个体作为初始群体P(t);

第二步:个体评价 计算P(t)中各个个体的适应度;

第三步:选择运算 将选择算子作用于群体;

第四步:交叉运算 将交叉算子作用于群体;

第五步:变异运算 将变异算子作用于群体,并通过以上运算得到下一代群体P(t + 1);

第六步:终止条件判断 t≦T:t← t+1 转到步骤2;t>T:终止 输出解。

2.遗传算法求解的一般过程:

1)确定决策变量及各种约束条件,即个体的表现型X和问题的解空间;

2)建立优化模型 (目标函数最大OR 最小) 数学描述形式 量化方法;

3)染色体编码方法;

4)解码方法;

5)个体适应度的量化评价方法 F(x)

6)设计遗传算子;

7)确定有关运行参数。

3.方法实现

编码方式

应用于TSP问题,选用整数编码,每个整数代表一个城市,一整条路径就是整个染色体编码;如此显式的编码,可以不用解码;

population = []# 初始化种群index = [i for i in range(w)]
for i in range(count):x = index.copy()random.shuffle(x)gailiang(x)population.append(x)

初始种群

随机生成初始种群,并计算这个初始种群的个体适应度。初始化种群时,采用改良版本,为了初始化一个较好的种群,如果随即交换两个城市的位置,如果总距离减小,那么就更新这个染色体。

# 初始种群的改良
def gailiang(x):distance = get_total_distance(x)gailiang_num = 0while gailiang_num < gailiang_N:while True:a = random.randint(0, len(x) - 1)b = random.randint(0, len(x) - 1)if a != b:breaknew_x = x.copy()temp_a = new_x[a]new_x[a] = new_x[b]new_x[b] = temp_aif get_total_distance(new_x) < distance:x = new_x.copy()gailiang_num += 1

选择算子

选择总距离作为适应度函数,距离越小适应度越高,(存活率与总距离的倒数成正比)

# 适应度
def get_total_distance(x):dista = 0for i in range(len(x)):if i == len(x) - 1:dista += distance[x[i]][x[0]]else:dista += distance[x[i]][x[i + 1]]return dista

交叉算子

1 部分映射交叉

选择交换部分,交换父代个体基因产生子代,然后建立映射表,根据映射表来消除基因冲突。

2 顺序交叉

在父代样本1中选择交换部分,根据父代1的交叉部分先生成子代1的部分基因片段,然后将父代2中未被选中的基因按顺序复制到子代1的空余部分;然后根据父代2选择交叉部分生成子代2,并将父代1中未选择的部分复制到子代2的空余;

3 基于位置的交叉

在父代1选择时随机选择需要交换的基因,根据交叉部分生成子代1;将父代2中未被选择到的基因复制到子代1中;然后根据父代2随机选择交叉部分生成子代2,并将父代1中未选择的部分复制到子代2的空余;

# 交叉繁殖
def crossover(parents):target_count = count - len(parents)children = []while len(children) < target_count:while True:male_index = random.randint(0, len(parents)-1)female_index = random.randint(0, len(parents)-1)if male_index != female_index:breakmale = parents[male_index]female = parents[female_index]left = random.randint(0, len(male) - 2)right = random.randint(left, len(male) - 1)gen_male = male[left:right]gen_female = female[left:right]child_a = []child_b = []len_ca = 0for g in male:if len_ca == left:child_a.extend(gen_female)len_ca += len(gen_female)if g not in gen_female:child_a.append(g)len_ca += 1len_cb = 0for g in female:if len_cb == left:child_b.extend(gen_male)len_cb += len(gen_male)if g not in gen_male:child_b.append(g)len_cb += 1children.append(child_a)children.append(child_b)return children

变异算子

根据变异算子的概率,变异时随机选择两个不同的位置的基因进行交换。也可以采用三点变异法,随机生成abc三点,将ac基因片段与bc做交换。

# 变异操作
def mutation(children):for i in range(len(children)):if random.random() < mutation_rate:while True:u = random.randint(0, len(children[i]) - 1)v = random.randint(0, len(children[i]) - 1)if u != v:breaktemp_a = children[i][u]children[i][u] = children[i][v]children[i][v] = temp_a

更新种群

采用杰出父代+子代的方式来更新种群。

while i < iter_time:# 自然选择parents = nature_select(population)# 繁殖children = crossover(parents)# 变异mutation(children)# 更新population = parents + childrenresult_cur_best, dist_cur_best = get_result(population)distance_list.append(dist_cur_best)i = i + 1print(result_cur_best)print(dist_cur_best)

我在求解时采用了在初始种群中进行改良的方法,收敛速度相对较快,求解结果比较满意。

三、求解结果

距离和城市序列:

在这里插入图片描述

TSP图和Loss图:

在这里插入图片描述
在这里插入图片描述

四、实现代码

#遗传算法求解TSP问题完整代码:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import math
import random# 处理数据coord = []
with open("data.txt", "r") as lines:lines = lines.readlines()
for line in lines:xy = line.split()coord.append(xy)coord = np.array(coord)
w, h = coord.shape
coordinates = np.zeros((w, h), float)
for i in range(w):for j in range(h):coordinates[i, j] = float(coord[i, j])# print(coordinates)# 得到距离矩阵distance = np.zeros((w, w))
for i in range(w):for j in range(w):distance[i, j] = distance[j, i] = np.linalg.norm(coordinates[i] - coordinates[j])# 种群数
count = 300# 进化次数
iter_time = 1000# 最优选择概率
retain_rate = 0.3  # 适应度前30%可以活下来# 弱者生存概率
random_select_rate = 0.5# 变异
mutation_rate = 0.1# 改良
gailiang_N = 3000# 适应度
def get_total_distance(x):dista = 0for i in range(len(x)):if i == len(x) - 1:dista += distance[x[i]][x[0]]else:dista += distance[x[i]][x[i + 1]]return dista# 初始种群的改良
def gailiang(x):distance = get_total_distance(x)gailiang_num = 0while gailiang_num < gailiang_N:while True:a = random.randint(0, len(x) - 1)b = random.randint(0, len(x) - 1)if a != b:breaknew_x = x.copy()temp_a = new_x[a]new_x[a] = new_x[b]new_x[b] = temp_aif get_total_distance(new_x) < distance:x = new_x.copy()gailiang_num += 1# 自然选择def nature_select(population):grad = [[x, get_total_distance(x)] for x in population]grad = [x[0] for x in sorted(grad, key=lambda x: x[1])]# 强者retain_length = int(retain_rate * len(grad))parents = grad[: retain_length]# 生存下来的弱者for ruozhe in grad[retain_length:]:if random.random() < random_select_rate:parents.append(ruozhe)return parents# 交叉繁殖
def crossover(parents):target_count = count - len(parents)children = []while len(children) < target_count:while True:male_index = random.randint(0, len(parents)-1)female_index = random.randint(0, len(parents)-1)if male_index != female_index:breakmale = parents[male_index]female = parents[female_index]left = random.randint(0, len(male) - 2)right = random.randint(left, len(male) - 1)gen_male = male[left:right]gen_female = female[left:right]child_a = []child_b = []len_ca = 0for g in male:if len_ca == left:child_a.extend(gen_female)len_ca += len(gen_female)if g not in gen_female:child_a.append(g)len_ca += 1len_cb = 0for g in female:if len_cb == left:child_b.extend(gen_male)len_cb += len(gen_male)if g not in gen_male:child_b.append(g)len_cb += 1children.append(child_a)children.append(child_b)return children# 变异操作
def mutation(children):for i in range(len(children)):if random.random() < mutation_rate:while True:u = random.randint(0, len(children[i]) - 1)v = random.randint(0, len(children[i]) - 1)if u != v:breaktemp_a = children[i][u]children[i][u] = children[i][v]children[i][v] = temp_adef get_result(population):grad = [[x, get_total_distance(x)] for x in population]grad = sorted(grad, key=lambda x: x[1])return grad[0][0], grad[0][1]population = []
# 初始化种群
index = [i for i in range(w)]
for i in range(count):x = index.copy()random.shuffle(x)gailiang(x)population.append(x)distance_list = []
result_cur_best, dist_cur_best = get_result(population)
distance_list.append(dist_cur_best)i = 0
while i < iter_time:# 自然选择parents = nature_select(population)# 繁殖children = crossover(parents)# 变异mutation(children)# 更新population = parents + childrenresult_cur_best, dist_cur_best = get_result(population)distance_list.append(dist_cur_best)i = i + 1print(result_cur_best)print(dist_cur_best)for i in range(len(result_cur_best)):result_cur_best[i] += 1result_path = result_cur_best
result_path.append(result_path[0])print(result_path)# 画图X = []
Y = []
for index in result_path:X.append(coordinates[index-1, 0])Y.append(coordinates[index-1, 1])plt.rcParams['font.sans-serif'] = 'SimHei'  # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False
plt.figure(1)
plt.plot(X, Y, '-o')
for i in range(len(X)):plt.text(X[i] + 0.05, Y[i] + 0.05, str(result_path[i]), color='red')
plt.xlabel('横坐标')
plt.ylabel('纵坐标')
plt.title('轨迹图')plt.figure(2)
plt.plot(np.array(distance_list))
plt.title('优化过程')
plt.ylabel('最优值')
plt.xlabel('代数({}->{})'.format(0, iter_time))
plt.show()
http://www.xdnf.cn/news/11518.html

相关文章:

  • iMeta封面 | 阜外医院李守军/黄源/刘禹泽-解码先天性心脏病患者肠道微生态奥秘...
  • godaddy域名 HostMonster空间,如何解析,修改NS
  • Android init.c简析
  • 为数不多的人知道的 Kotlin 技巧及解析
  • 国内各地图API坐标系统比较与转换
  • 【Linux】Linux磁盘空间扩展
  • 【历史上的今天】5 月 9 日:中国黄页上线;Red Hat 创始人出生;Scratch 2.0 发布
  • AntiARP安装时出现windows installer package错误解决方法
  • 太厉害了:雄霸美国的黑市拳王,竟然是中国人!
  • 【休闲】苏轼诗句“只恐夜深花睡去,故烧高烛照红妆”描写的是那种花卉-蚂蚁庄园-庄园小课堂
  • 华为云CodeArts Check代码检查插件(VSCode IDE版本)使用指南
  • JazzyViewPager 开源项目教程
  • Unity Shader - BRP - Soft Particle - 软粒子
  • 关于退出系统时,清除session
  • Spring Boot 构建restful web服务
  • Html-浅谈如何正确给table加边框
  • Python采集网页数据:八招全解
  • java基于go-cqhttp开发qq机器人
  • java简易制作-王者荣耀游戏
  • vim中翻页的命令 vim使用技巧之翻页 vim学习资料vi快捷键必知必会Vim的小技巧vim的查找与替换
  • 使用神卓互联配置内网访问(内网穿透)教程(超详细,简单)
  • 【雕爷学编程】Arduino动手做(78)---槽型光耦红外对射计数传感器模块2
  • 凯立德地图版本号/特征码/激活码信息查询方法
  • 电子白板是什么?
  • Blend4精选案例图解教程(一):丰富的形状(Shape)资源
  • 剑灵系统推荐加点_剑灵力士攻略:简单粗暴新版本加点推荐
  • 达梦の外部链接(dblink)
  • 4500m a8 amd_AMD A8--4500M处理器有哪些特点?
  • 《战地1942》全攻略
  • headerTemplate里面说有一些内置的属性,比如title, date,这些内置的属性应该怎么使用,可以给一个例子吗...