问题描述
一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。
对于n个城市的TSP,本文利用python分别实现遗传算法对该问题的求解。

遗传算法

原理见链接
遗传算法原理及其python实现
遗传算法的基本运算过程如下:

  1. 初始化编码:设置最大进化代数T_max、选择概率、交叉概率、变异概率、随机生成m个染色体的群体,每个染色体的编码对于一个可行的路径(如6个城市,[1,3,2,6,4,5]就是一条可行路径)
  2. 适应度函数:对每一个染色体 Xk,其个体适应度函数设置为 f(Xk)=1/Dk ,其中Dk 表示该条路径的总长度。
  3. 选择:将旧群体中的染色体以一定概率选择到新群体,每条染色体选中的概率与对应的适应度函数只相对应,本文采用随机遍历选择。
  4. 交叉:在交叉概率的控制下,对选择群体中的个体进行两两交叉。
  5. 变异:在变异概率的控制下,对单个染色体随机交换两个点的位置
  6. 进化逆转:将选择的染色体随机选择两个位置r1:r2 ,将 r1:r2 的元素翻转为 r2:r1 ,如果翻转后的适应度更高,则替换原染色体,否则不变。
  7. 重插:选择的子代与父代结合,形成新的种群,循环操作

流程图如下

或者

代码


from math import floor
import numpy as np
import matplotlib.pyplot as plt  # 导入所需要的库class Gena_TSP(object):def __init__(self ,data ,maxgen=200 ,size_pop=200 ,cross_prob=0.9 ,pmuta_prob=0.01 ,select_prob=0.8):self.maxgen = maxgen  # 最大迭代次数self.size_pop = size_pop  # 群体个数self.cross_prob = cross_prob  # 交叉概率self.pmuta_prob = pmuta_prob  # 变异概率self.select_prob = select_prob  # 选择概率self.data = data  # 城市的左边数据self.num =len(data)  # 城市个数 对应染色体长度# 距离矩阵n*n, 第[i,j]个元素表示城市i到j距离matrix_dis函数见下文self.matrix_distance = self.matrix_dis()# 通过选择概率确定子代的选择个数self.select_num = max(floor(self.size_pop * self.select_prob + 0.5), 2)# 父代和子代群体的初始化(不直接用np.zeros是为了保证单个染色体的编码为整数,np.zeros对应的数据类型为浮点型)self.chrom = np.array([0] * self.size_pop * self.num).reshape(self.size_pop, self.num)#父 print(chrom.shape)(200, 14)self.sub_sel = np.array([0] * self.select_num * self.num).reshape(self.select_num, self.num)#子 (160, 14)# 存储群体中每个染色体的路径总长度,对应单个染色体的适应度就是其倒数  #print(fitness.shape)#(200,)self.fitness = np.zeros(self.size_pop)self.best_fit = []##最优距离self.best_path = []#最优路径# 计算城市间的距离函数  n*n, 第[i,j]个元素表示城市i到j距离def matrix_dis(self):res = np.zeros((self.num, self.num))for i in range(self.num):for j in range(i + 1, self.num):res[i, j] = np.linalg.norm(self.data[i, :] - self.data[j, :])#求二阶范数 就是距离公式res[j, i] = res[i, j]return res# 随机产生初始化群体函数def rand_chrom(self):rand_ch = np.array(range(self.num)) ## num 城市个数 对应染色体长度  =14for i in range(self.size_pop):#size_pop  # 群体个数 200np.random.shuffle(rand_ch)#打乱城市染色体编码self.chrom[i, :] = rand_chself.fitness[i] = self.comp_fit(rand_ch)# 计算单个染色体的路径距离值,可利用该函数更新fittnessdef comp_fit(self, one_path):res = 0for i in range(self.num - 1):res += self.matrix_distance[one_path[i], one_path[i + 1]] #matrix_distance n*n, 第[i,j]个元素表示城市i到j距离res += self.matrix_distance[one_path[-1], one_path[0]]#最后一个城市 到起点距离return res# 路径可视化函数def out_path(self, one_path):res = str(one_path[0] + 1) + '-->'for i in range(1, self.num):res += str(one_path[i] + 1) + '-->'res += str(one_path[0] + 1) + '\n'print(res)# 子代选取,根据选中概率与对应的适应度函数,采用随机遍历选择方法def select_sub(self):fit = 1. / (self.fitness)  # 适应度函数cumsum_fit = np.cumsum(fit)#累积求和   a = np.array([1,2,3]) b = np.cumsum(a) b=1 3 6pick = cumsum_fit[-1] / self.select_num * (np.random.rand() + np.array(range(self.select_num)))#select_num  为子代选择个数 160i, j = 0, 0index = []while i < self.size_pop and j < self.select_num:if cumsum_fit[i] >= pick[j]:index.append(i)j += 1else:i += 1self.sub_sel = self.chrom[index, :]#chrom 父# 交叉,依概率对子代个体进行交叉操作def cross_sub(self):if self.select_num % 2 == 0:#select_num160num = range(0, self.select_num, 2)else:num = range(0, self.select_num - 1, 2)for i in num:if self.cross_prob >= np.random.rand():self.sub_sel[i, :], self.sub_sel[i + 1, :] = self.intercross(self.sub_sel[i, :], self.sub_sel[i + 1, :])def intercross(self, ind_a, ind_b):#ind_a,ind_b 父代染色体 shape=(1,14) 14=14个城市r1 = np.random.randint(self.num)#在num内随机生成一个整数 ,num=14.即随机生成一个小于14的数r2 = np.random.randint(self.num)while r2 == r1:#如果r1==r2r2 = np.random.randint(self.num)#r2重新生成left, right = min(r1, r2), max(r1, r2)#left 为r1,r2小值 ,r2为大值ind_a1 = ind_a.copy()#父亲ind_b1 = ind_b.copy()# 母亲for i in range(left, right + 1):ind_a2 = ind_a.copy()ind_b2 = ind_b.copy()ind_a[i] = ind_b1[i]#交叉 (即ind_a  (1,14) 中有个元素 和ind_b互换ind_b[i] = ind_a1[i]x = np.argwhere(ind_a == ind_a[i])y = np.argwhere(ind_b == ind_b[i])"""下面的代码意思是 假如 两个父辈的染色体编码为【1234】,【4321】 交叉后为【1334】,【4221】交叉后的结果是不满足条件的,重复个数为2个需要修改为【1234】【4321】(即修改会来"""if len(x) == 2:ind_a[x[x != i]] = ind_a2[i]#查找ind_a 中元素=- ind_a[i] 的索引if len(y) == 2:ind_b[y[y != i]] = ind_b2[i]return ind_a, ind_b# 变异模块  在变异概率的控制下,对单个染色体随机交换两个点的位置。def mutation_sub(self):for i in range(self.select_num):#遍历每一个 选择的子代if np.random.rand() <= self.cross_prob:#如果随机数小于变异概率r1 = np.random.randint(self.num)#随机生成小于num==14 的数r2 = np.random.randint(self.num)while r2 == r1:#如果相同r2 = np.random.randint(self.num)#r2再生成一次self.sub_sel[i, [r1, r2]] = self.sub_sel[i, [r2, r1]]#随机交换两个点的位置。# 进化逆转  将选择的染色体随机选择两个位置r1:r2 ,将 r1:r2 的元素翻转为 r2:r1 ,如果翻转后的适应度更高,则替换原染色体,否则不变def reverse_sub(self):for i in range(self.select_num):#遍历每一个 选择的子代r1 = np.random.randint(self.num)#随机生成小于num==14 的数r2 = np.random.randint(self.num)while r2 == r1:#如果相同r2 = np.random.randint(self.num)#r2再生成一次left, right = min(r1, r2), max(r1, r2)#left取r1 r2中小值,r2取大值sel = self.sub_sel[i, :].copy()#sel 为父辈染色体 shape=(1,14)sel[left:right + 1] = self.sub_sel[i, left:right + 1][::-1]#将染色体中(r1:r2)片段 翻转为(r2:r1)if self.comp_fit(sel) < self.comp_fit(self.sub_sel[i, :]):#如果翻转后的适应度小于原染色体,则不变self.sub_sel[i, :] = sel# 子代插入父代,得到相同规模的新群体def reins(self):index = np.argsort(self.fitness)[::-1]#替换最差的(倒序)self.chrom[index[:self.select_num], :] = self.sub_sel#路径坐标
data = np.array([16.47,96.10,16.47,94.44,20.09,92.54,22.39,93.37,25.23,97.24,22.00,96.05,20.47,97.02,17.20,96.29,16.30,97.38,14.05,98.12,16.53,97.38,21.52,95.59,19.41,97.13,20.09,92.55]).reshape(14,2)
def main(data):Path_short = Gena_TSP(data)  # 根据位置坐标,生成一个遗传算法类Path_short.rand_chrom()  # 初始化父类## 绘制初始化的路径图fig, ax = plt.subplots()x = data[:, 0]y = data[:, 1]ax.scatter(x, y, linewidths=0.1)for i, txt in enumerate(range(1, len(data) + 1)):ax.annotate(txt, (x[i], y[i]))res0 = Path_short.chrom[0]x0 = x[res0]y0 = y[res0]for i in range(len(data) - 1):plt.quiver(x0[i], y0[i], x0[i + 1] - x0[i], y0[i + 1] - y0[i], color='r', width=0.005, angles='xy', scale=1,scale_units='xy')plt.quiver(x0[-1], y0[-1], x0[0] - x0[-1], y0[0] - y0[-1], color='r', width=0.005, angles='xy', scale=1,scale_units='xy')plt.show()print('初始染色体的路程: ' + str(Path_short.fitness[0]))# 循环迭代遗传过程for i in range(Path_short.maxgen):Path_short.select_sub()  # 选择子代Path_short.cross_sub()  # 交叉Path_short.mutation_sub()  # 变异Path_short.reverse_sub()  # 进化逆转Path_short.reins()  # 子代插入# 重新计算新群体的距离值for j in range(Path_short.size_pop):Path_short.fitness[j] = Path_short.comp_fit(Path_short.chrom[j, :])# 每隔三十步显示当前群体的最优路径index = Path_short.fitness.argmin()if (i + 1) % 30 == 0:print('第' + str(i + 1) + '步后的最短的路程: ' + str(Path_short.fitness[index]))print('第' + str(i + 1) + '步后的最优路径:')Path_short.out_path(Path_short.chrom[index, :])  # 显示每一步的最优路径# 存储每一步的最优路径及距离Path_short.best_fit.append(Path_short.fitness[index])Path_short.best_path.append(Path_short.chrom[index, :])res1 = Path_short.chrom[0]x0 = x[res1]y0 = y[res1]for i in range(len(data) - 1):plt.quiver(x0[i], y0[i], x0[i + 1] - x0[i], y0[i + 1] - y0[i], color='r', width=0.005, angles='xy', scale=1,scale_units='xy')plt.quiver(x0[-1], y0[-1], x0[0] - x0[-1], y0[0] - y0[-1], color='r', width=0.005, angles='xy', scale=1,scale_units='xy')plt.show()return Path_short  # 返回遗传算法结果类Path_short=main(data)
print(Path_short)

初始

最终

讲解

计算城市间的距离函数

 # 计算城市间的距离函数  n*n, 第[i,j]个元素表示城市i到j距离def matrix_dis(self):res = np.zeros((self.num, self.num))for i in range(self.num):for j in range(i + 1, self.num):res[i, j] = np.linalg.norm(self.data[i, :] - self.data[j, :])#求二阶范数 就是距离公式res[j, i] = res[i, j]return res

随机产生初始化群体函数

 # 随机产生初始化群体函数def rand_chrom(self):rand_ch = np.array(range(self.num)) ## num 城市个数 对应染色体长度  =14for i in range(self.size_pop):#size_pop  # 群体个数 200np.random.shuffle(rand_ch)#打乱城市染色体编码self.chrom[i, :] = rand_chself.fitness[i] = self.comp_fit(rand_ch)

计算单个染色体的路径距离值,可利用该函数更新self.fittness

 def comp_fit(self, one_path):res = 0for i in range(self.num - 1):res += self.matrix_distance[one_path[i], one_path[i + 1]] #matrix_distance n*n, 第[i,j]个元素表示城市i到j距离res += self.matrix_distance[one_path[-1], one_path[0]]#最后一个城市 到起点距离return res

子代选取,根据选中概率与对应的适应度函数,采用随机遍历选择方法

  def select_sub(self):fit = 1. / (self.fitness)  # 适应度函数cumsum_fit = np.cumsum(fit)#累积求和   a = np.array([1,2,3]) b = np.cumsum(a) b=1 3 6pick = cumsum_fit[-1] / self.select_num * (np.random.rand() + np.array(range(self.select_num)))#select_num  为子代选择个数 160i, j = 0, 0index = []while i < self.size_pop and j < self.select_num:if cumsum_fit[i] >= pick[j]:index.append(i)j += 1else:i += 1self.sub_sel = self.chrom[index, :]#chrom 父

交叉,依概率对子代个体进行交叉操作

 def cross_sub(self):if self.select_num % 2 == 0:#select_num160num = range(0, self.select_num, 2)else:num = range(0, self.select_num - 1, 2)for i in num:if self.cross_prob >= np.random.rand():self.sub_sel[i, :], self.sub_sel[i + 1, :] = self.intercross(self.sub_sel[i, :], self.sub_sel[i + 1, :])def intercross(self, ind_a, ind_b):#ind_a,ind_b 父代染色体 shape=(1,14) 14=14个城市r1 = np.random.randint(self.num)#在num内随机生成一个整数 ,num=14.即随机生成一个小于14的数r2 = np.random.randint(self.num)while r2 == r1:#如果r1==r2r2 = np.random.randint(self.num)#r2重新生成left, right = min(r1, r2), max(r1, r2)#left 为r1,r2小值 ,r2为大值ind_a1 = ind_a.copy()#父亲ind_b1 = ind_b.copy()# 母亲for i in range(left, right + 1):ind_a2 = ind_a.copy()ind_b2 = ind_b.copy()ind_a[i] = ind_b1[i]#交叉 (即ind_a  (1,14) 中有个元素 和ind_b互换ind_b[i] = ind_a1[i]x = np.argwhere(ind_a == ind_a[i])y = np.argwhere(ind_b == ind_b[i])"""下面的代码意思是 假如 两个父辈的染色体编码为【1234】,【4321】 交叉后为【1334】,【4221】交叉后的结果是不满足条件的,重复个数为2个需要修改为【1234】【4321】(即修改会来"""if len(x) == 2:ind_a[x[x != i]] = ind_a2[i]#查找ind_a 中元素=- ind_a[i] 的索引if len(y) == 2:ind_b[y[y != i]] = ind_b2[i]return ind_a, ind_b

变异模块

 # 变异模块  在变异概率的控制下,对单个染色体随机交换两个点的位置。def mutation_sub(self):for i in range(self.select_num):#遍历每一个 选择的子代if np.random.rand() <= self.cross_prob:#如果随机数小于变异概率r1 = np.random.randint(self.num)#随机生成小于num==14 的数r2 = np.random.randint(self.num)while r2 == r1:#如果相同r2 = np.random.randint(self.num)#r2再生成一次self.sub_sel[i, [r1, r2]] = self.sub_sel[i, [r2, r1]]#随机交换两个点的位置。

进化逆转

 # 进化逆转  将选择的染色体随机选择两个位置r1:r2 ,将 r1:r2 的元素翻转为 r2:r1 ,如果翻转后的适应度更高,则替换原染色体,否则不变def reverse_sub(self):for i in range(self.select_num):#遍历每一个 选择的子代r1 = np.random.randint(self.num)#随机生成小于num==14 的数r2 = np.random.randint(self.num)while r2 == r1:#如果相同r2 = np.random.randint(self.num)#r2再生成一次left, right = min(r1, r2), max(r1, r2)#left取r1 r2中小值,r2取大值sel = self.sub_sel[i, :].copy()#sel 为父辈染色体 shape=(1,14)sel[left:right + 1] = self.sub_sel[i, left:right + 1][::-1]#将染色体中(r1:r2)片段 翻转为(r2:r1)if self.comp_fit(sel) < self.comp_fit(self.sub_sel[i, :]):#如果翻转后的适应度小于原染色体,则不变self.sub_sel[i, :] = sel

子代插入父代,得到相同规模的新群体

 # 子代插入父代,得到相同规模的新群体def reins(self):index = np.argsort(self.fitness)[::-1]self.chrom[index[:self.select_num], :] = self.sub_sel


作者:电气-余登武

遗传算法求最短路径(旅行商问题)python实现相关推荐

  1. python遗传算法最短路径问题有几种类型_遗传算法求最短路径 - osc_tn8uhjgi的个人空间 - OSCHINA - 中文开源技术交流社区...

    实例描述 配送中心数为 $1$,客户数 $k$为 $8$,车辆总数 $m$为 $2$:车辆载重皆为 $8$ 吨:各客户点需求为 $g(i = 1, 2, ... , 8)$(单位为吨),已知客户点与配 ...

  2. python求函数极值_python 遗传算法求函数极值的实现代码

    废话不多说,大家直接看代码吧! """遗传算法实现求函数极大值-Zjh""" import numpy as np import rando ...

  3. python多元函数求解_使用遗传算法求二元函数的最小值

    二元函数为y=x1^2+x2^2,x∈[-5,5] NIND=121; %初始种群的个数(Number of individuals) NVAR=2; %一个染色体(个体)有多少基因 PRECI=20 ...

  4. Python实现遗传算法求函数最值

    Python实现遗传算法求函数最值 详细源代码:GA.py 1.算法过程图解 2.详细过程举例说明 (1)待求解方程 (2)确定编码方案 主要是确定编码长度: def segment_length(s ...

  5. Python遗传算法求一元函数最大值

    Python遗传算法求一元函数最大值 前言 代码 后记 参考文献 前言 最近接触遗传算法,参考了众多例子,有些又不尽然对,所以自己边理解边修改,然后写出了下面这堆传说中的屎山... PS1:遗传算法原 ...

  6. python基于广度优先(BFS)的迪杰斯特拉(Dijkstra)算法 求最短路径

    python深度优先与广度优先的遍历算法区别 首先要理解搜索步,一个完整的搜索步包括两个处理: a) 获得当前位置上,有几条路可供选择 b) 根据选择策略,选择其中一条路,并走到下个位置 广度优先:就 ...

  7. 迪杰斯特拉算法(Dijkstra)求最短路径Python

    迪杰斯塔拉(Dijkstra)算法求最短路径 序 关于Dijkstra Dijkstra算法讲解 Dijkstra算法的弊端 第一步:进行初始化 第二步:主程序开始 又是初始化 核心的核心[^5] 最 ...

  8. 遗传算法求三元函数极值(python)-采用实数编码

    遗传算法求三元函数极值(python)-采用实数编码 想看二进制编码编码的博客地址在这 遗传算法求三元函数极值(python)-采用二进制编码 本文的遗传算法采用实数编码求三元函数极值 所求函数为 ` ...

  9. 遗传算法求三元函数极值(python)-采用二进制编码

    想看实数编码编码的 博客地址遗传算法求三元函数极值(python)-采用实数编码 * 遗传算法求三元函数极值(python)-采用二进制编码 本文的遗传算法采用二进制编码求三元函数极值 所求函数为 要 ...

最新文章

  1. Java 集合中的方法性能分析
  2. VRRP协议介绍--转
  3. SpringBoot 项目使用 SLF4J+logback 进行日志记录,来增强可维护性
  4. mysql查询语句在哪里编写_mysql编写语句:更新查询
  5. Gh0st 3.6 存在的BUG及修改方法(收集整理)
  6. SQL server2017和ssms管理工具下载
  7. linux下用ntp对时
  8. 每期一词:catastrophe
  9. JavaScript:instanceof 实现原理
  10. 前端工具类项目规范化-使用TS
  11. weblogic 10.x 上开发restful服务
  12. Html加jq实现5星好评效果,关于jquery实现五星好评的方法
  13. 闻之色变-翻译公司用翻译工具翻译?!
  14. 12款惊人的HTML5Flash视频播放器
  15. 线和面的方程区别_平面方程和直线方程的区别?
  16. CAD梦想画图中的“绘图工具——圆弧”
  17. 工程师应培养哪些基本功?听美团技术大咖怎么说
  18. 图像的高频信息和低频信息的含义
  19. tqdm的使用和例子
  20. npcap 如何获得链路层类型

热门文章

  1. 给列名称命名_批量提取文件名,然后换上新名称
  2. Traffic Simulator Applet(交通模拟器小程序)
  3. 计算机技术类社团纳新笔试题示例
  4. spec linux,linux – 在RPM包中SPEC文件中1%{?dist}的含义是什么?
  5. Redis-学习笔记03【Redis持久化】
  6. IOS成长之路-检测耳机插入/拔出
  7. DND是如何封装WinSock的?
  8. Java基础篇:四种代码块详解
  9. IDEA、Sublime、Eclipse、VSCode 常用快捷键整合
  10. Python学习:基本概念