遗传算法解决tsp问题(基于python)

目录

1.遗传算法简要介绍

2.tsp问题简要介绍

3.遗传算法解决tsp问题的几个特殊点

4.源码

1.遗传算法简要介绍

        简单来说,遗传算法是用于解决最优化问题的一种搜索算法。其核心基于自然界种群进化的规律,即初始种群进行交配,在基因层面上,其中会发生交叉互换、基因突变等变异,产生新一批的种群,在种群不断繁衍的过程中,“适者生存,不适者灭亡”,更符合环境要求的个体的基因保留下来的可能性更大,不适应环境的个体的基因往往不会延续下去。漫长的时间后,会筛选出一批最适应环境的种群。

        基于此,当我们在解决最优化问题时,采取上述思想,将问题的解看作是“个体”,这些个体组成一个抽象的“种群”,这些解被映射成为相应的编码,于是我们就能得到由各种编码组成的“种群”。这些编码可以进行片段的交叉互换,或者其中某些数字发生“基因突变”,从而进行种群的更新。那么如何筛选更合适的个体呢?根据实际的需要与限制,我们基于编码,通过特定的函数,计算出每个个体的“适应度”,适应度更大的个体的基因(编码)被选中并保留下来的概率更大。这样经过上百次迭代后,就能得到一个接近最优解的一个种群。

        详细的介绍大家可以参照这篇文章遗传算法详解 附python代码实现_重学CS的博客-CSDN博客_python遗传算法 ,文章作者将一般公式的最优化讲解到令人发指的详细与通俗易懂。如果能将这篇文章掌握,基本上可以通过遗传算法,求任何公式(n元n次方程)的最值。其中的内涵是将解从十进制数字映射成为二进制串,这其中的编码与解码过程很重要。

        在这里就不展开叙述更多细节了,上面那篇文章讲的很清楚,本篇重点在于解决tsp问题。

2.tsp问题简要介绍

        根据百度百科的介绍,t旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。

3.遗传算法解决tsp问题的几个特殊点

这也是本篇的重点

3.1如何编码

首先要明白我们解的形式是什么,我们需要得到一条距离最短的路径。因此将这些城市编码(0、1、2........n),以10座城市为例,我们希望得到的解或许是3 5 4 8 6 7 9 0 2 1,因此,在遗传算法中,每个个体的形式就应该是10个不重复数字的排列。好消息是这样一来不需要进行二进制编码解码了。

 #初始化种群
def generate_population(self):path=np.array(range(self.num))self.pop=[]for i in range(self.size_pop):x=path.copy()np.random.shuffle(x)    #随机洗牌self.pop.append(x)

3.2距离矩阵的建立

我们该如何评价一个解的适应度?显然我们希望每个个体的路径距离越小越好,所以我们需要先得到每座城市之间的距离,将其记录在一个矩阵当中,当后续需要求一条完整路径的距离时,任意两点的距离可以直接转化为坐标(比如说,(2,6))从这个矩阵中取出。

3.3交叉互换

如果直接确定一条染色体上的一个位置,将父本母本从这个位置开始直接交叉互换,这显然是不合理的,假如父本是 1 3 2 4,母本是 2 4 1 3,两者正好在中点切割进行交叉互换后的子代分别是1 3 1 3和 2 4 2 4,这显然是错误的!旅行商每座城市只能经过一次!所以在交换染色体片段的时候,必须要经过一个操作,就是去除重复碱基对。

TSP、MTSP问题遗传算法详细解读及python实现,这篇文章的博主给出了tsp问题遗传算法交叉互换的三种方式,这里我们选择第二种​

Order Crossover(顺序交叉)

​ 

3.4基因突变

在二进制编码的情况下,要想完成基因突变,只需要将选中的染色体随机替换掉一个碱基对即可。但是在tsp问题中,如果这样做,一定会导致被选中染色体碱基对的重复!因此我们需要做的是将被选中染色体的任意两碱基对进行互换,这样就避免了重复

3.5适应度计算

我们该如何评价一个个体的基因是否适合被遗传下来呢?这就需要计算个体的适应度,适应度越高的个体,被选择的概率就越大。在tsp问题中,我们希望一个个体的路径长度越短越好,即路径越短,适应度越大,在这里,采用文章基于遗传算法求解TSP问题(旅游路径规划,Python实现,超详细,可视化,结果分析中的适应度公式来计算适应度

fitness=\frac{1}{distance^{15}}
 

 适应度越大的个体被选中的可能性越大,用numpy.choice来实现

idx = np.random.choice(np.arange(self.size_pop), size=self.size_pop, replace=True,p=(self.fitness)/(fitness_sum) )

4.源码

import numpy as npclass TSP:def __init__(self, citys, maxgen=500,size_pop=200, cross_rate=0.8,muta_rate=0.005):self.maxgen = maxgen            # 最大进化次数self.size_pop = size_pop        # 种群大小(染色体个数)self.cross_rate = cross_rate    # 交叉概率self.muta_rate = muta_rate    # 变异概率self.citys = citys       # 城市的坐标数据self.num = citys.shape[0]    # 城市的个数(染色体长度)#获得距离矩阵def matrix_distance(self):self.distance_m=np.zeros((self.num,self.num))for i in range(self.num):for j in range(self.num):self.distance_m[i][j]=np.sqrt((self.citys[i][0]-self.citys[j][0])**2+(self.citys[i][1]-self.citys[j][1])**2)#计算某条路径的距离def get_total_distance(self,one_path):distance=0for i in range(self.num-1):distance +=self.distance_m[one_path[i]][one_path[i+1]]distance += self.distance_m[one_path[-1]][one_path[0]]return distance#初始化种群def generate_population(self):path=np.array(range(self.num))self.pop=[]for i in range(self.size_pop):x=path.copy()np.random.shuffle(x)    #随机洗牌self.pop.append(x)#交叉互换def crossover(self):self.new_pop=[]for father in self.pop:child=father   #初步让子代获得父本染色体if np.random.rand()<self.cross_rate:#随机选择一个染色体作为母本mother_index = np.random.randint(0, self.size_pop)mother=self.pop[mother_index]  #确定切割点     left = np.random.randint(0, self.num-2)right = np.random.randint(left + 1, self.num-1)mother=mother.tolist()father=father.tolist()#切割片段gene = mother[left:right]child1_c = father[right:]+father[:right]child1 = child1_c.copy()#去除重复基因for o in gene:child1_c.remove(o)child1[left:right] = genechild1[right:] = child1_c[0:len(child1) - right]child1[:left] = child1_c[(len(child1) - right):]child=np.array(child1)self.new_pop.append(child)self.pop=self.new_pop#变异def mutation(self):for i in range(self.size_pop):if np.random.rand() < self.muta_rate:child = self.pop[i]u = np.random.randint(0,self.num - 2)v = np.random.randint(u+1,self.num- 1)child_x = child[u+1:v]child_x=child_x[::-1]        child = np.concatenate((child[0:u+1] , child_x , child[v:]))self.pop[i]=child#自然选择,种群根据适应度进行更新def select(self):#计算每条路径的长度,放入列表self.dis=[]for i in range(self.size_pop):self.dis.append(self.get_total_distance(one_path=self.pop[i]))#根据路径长度计算每个个体的适应度self.fitness=[]for i in range(self.size_pop):self.fitness.append(1/(self.dis[i]**15))#适应度总和fitness_sum=0for i in range(self.size_pop):fitness_sum+=self.fitness[i]#根据适应度进行选择,适应度大的被选择概率大idx = np.random.choice(np.arange(self.size_pop), size=self.size_pop, replace=True,p=(self.fitness)/(fitness_sum) )self.new_pop=[]for i in idx:self.new_pop.append(self.pop[i])self.pop=self.new_pop#输出当前种群中最优路径def result_path(self):self.index=np.argmax(self.fitness)a="the shortest path is:"for i in range(self.num-1):a+=str(self.pop[self.index][i])a+="-->"a+=str(self.pop[self.index][-1])print(a)print("the total distance is",self.dis[self.index])#主函数进行迭代def main(citys):SL=TSP(citys)SL.matrix_distance()SL.generate_population()for i in range (SL.maxgen):SL.crossover()SL.mutation()SL.select()SL.result_path()if __name__ == '__main__':citys = 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]])main(citys)

结果分析

如果每一次迭代都输出结果,可以清楚地看到路径最终都收敛。事实上,每次收敛的结果与种群大小size_pop息息相关,一开始我将种群大小设置为 200,结果每次运行虽然收敛,但是得到的结果各不相同,基本上毫无关联,说明结果陷入了局部最优解,而非全局最优解。解决方法就是把size_pop设置为500,才使得结果误差较小,趋近于全局最优解。

事实上,要想使结果更直观,最好用坐标图使结果可视化,将路线显示出来。本文仅仅展示了数字结果,这样有两个问题,一是可能起始城市不同,如8 2 3 6 和 2 3 6 8,二是顺序不同,如8 2 3 6和 6 3 2 8,其实结果的路线本质是一样的,但直观看上去不利于结果统计。

参考文章:

http://t.csdn.cn/eJZmK

http://t.csdn.cn/0fYtK

http://t.csdn.cn/VFPLr

http://t.csdn.cn/2oWZt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/216736.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

elasticsearch 索引库操作和文档操作

文章目录 索引库操作mapping映射属性索引库的CRUD&#xff08;创建&#xff0c;读取&#xff0c;更新&#xff0c;删除&#xff09;创建索引库和映射基本语法&#xff1a;示例&#xff1a; 查询索引库修改索引库删除索引库 文档操作新增文档查询文档删除文档修改文档全量修改增…

数据结构 | 堆【图解】

数据结构 | 堆【图解】 文章目录 数据结构 | 堆【图解】堆的概念及结构堆的实现堆的初始化堆的插入【重点】堆的删除【重点】取堆顶的数据堆的数据个数堆的判空堆的销毁 全部代码 堆的概念及结构 堆&#xff08;heap&#xff09;&#xff1a; 一种有特殊用途的数据结构——用来…

无人智能柜:经营成本低,运维智能化

在现代商业领域中&#xff0c;无人智能柜正逐渐崭露头角&#xff0c;成为一种具有前景的商业模式。其独特之处在于经营成本的低廉性和运维过程的智能化。相较于传统的便利店等实体店铺&#xff0c;无人智能柜在运营过程中不仅能够降低成本&#xff0c;还能够实现高效的运维管理…

【腾讯云云上实验室】向量数据库+LangChain+LLM搭建智慧辅导系统实践

目录 一、搭建智慧辅导系统——向量数据库实践指南1.1、创建向量数据库并新建集合1.2、使用 TKE 快速部署 ChatGLM1.3、部署 LangChain PyPDFVectorDB等组件1.4、配置知识库语料1.5、基于 VectorDB LLM 的智能辅导助手 二、LLM时代的次世代引擎——向量数据库2.1、向量数据库L…

【多线程】-- 02 线程创建之实现Runnable初识多线程并发问题

多线程 2 线程创建 2.2 实现Runnable接口 【学习提示】查看JDK帮助文档 定义MyRunnable类实现Runnable接口实现run()方法&#xff0c;编写线程执行体创建线程对象&#xff0c;调用start()方法启动线程 package com.duo.demo01;//创建线程方式二&#xff1a;实现Runnable接…

Mac开发环境——MacOSX安装与配置Anaconda与PyCharm详细流程

一、安装与使用Anaconda 1.简介 Anaconda 是一个用于数据科学、机器学习和科学计算的开源发行版和包管理器。有许多可用于数据处理、分析和建模的工具和库&#xff0c;并提供了一个方便的环境管理系统。Anaconda 包含了 Python 解释器和许多常用的 Python 包&#xff0c;以及…

Windows环境搭建

Windows环境搭建 一. jdk1.8安装1. 资源链接2. 安装3. 配置环境变量 一. jdk1.8安装 1. 资源链接 资源链接 提取码&#xff1a;tfms 2. 安装 1.双击下载好的JDK,点击下一步。 2.修改默认目录&#xff08;可不修改&#xff09;&#xff0c;点击下一步&#xff0c; 3. 点击下…

医保线上购药系统:引领医疗新潮流

在科技的驱动下&#xff0c;医疗健康服务正经历一场数字化的革新。医保线上购药系统&#xff0c;不仅是一种医疗服务的新选择&#xff0c;更是技术代码为我们的健康管理带来的全新可能。本文将通过一些简单的技术代码示例&#xff0c;深入解析医保线上购药系统的工作原理和优势…

CleanMyMacX4.14.5macOS电脑系统免费清理工具

CleanMyMac X是一款专业的Mac清理软件&#xff0c;可智能清理mac磁盘垃圾和多余语言安装包&#xff0c;快速释放电脑内存&#xff0c;轻松管理和升级Mac上的应用。同时CleanMyMac X可以强力卸载恶意软件&#xff0c;修复系统漏洞&#xff0c;一键扫描和优化Mac系统&#xff0c;…

Altium Designer学习笔记13

0603电容封装的画法&#xff1a; 再画下三极管SOT-23的三极管的封装图&#xff1a; 画出三极管的封装图&#xff1a; 在画图的过程中&#xff0c;遇到了一个问题&#xff0c;画闭环线路的时候&#xff0c;就会被自动删除&#xff0c;查出是这个地方的配置需要进行修改。 那这个…

ConcurrentHashMap的数据结构+以及各个版本之间的区别

ConcurrentHashMap 1.7与1.8的区别 1、锁结构不同 2、put的流程不同 3、size的计算方式不同(1.8使用的使用basecell[]计算&#xff0c;有点类似于LongAdder&#xff0c;1.7使用三级通缉判断是否一样&#xff0c;不一样通过分段式加锁再求和) 4、数据结构不同&#xff0c;1.6 Re…

FANUC机器人到达某个点位时,为什么不显示@符号?

FANUC机器人到达某个点位时,为什么不显示@符号? 该功能由变量$MNDSP_POSCF = 0(不显示)/1(显示)/2(光标移动该行显示) 控制,该变量设置为不同的值,则启用对应的功能。 如下图所示,为该变量设置不同的值时的对比, 其他常用的系统变量可参考以下内容: 在R寄存器指定速度…