Python动态展示遗传算法求解TSP旅行商问题

效果图:
在这里插入图片描述
程序会动态的展示迭代过程,40以内城市大概迭代300次能收敛到最优;这里是用中国城市地理坐标直接做欧式距离计算,实际上可以根据问题作出调整。
网址在这:欢迎star啊,我看中这个
Github:https://github.com/425776024/TSP-GA-py

数据:china.csv:

北京 ;116.46;39.92
天津 ;117.2;39.13
上海 ;121.48;31.22
重庆 ;106.54;29.59
拉萨 ;91.11;29.97
乌鲁木齐 ;87.68;43.77
银川 ;106.27;38.47
呼和浩特 ;111.65;40.82
南宁  ;108.33;22.84
哈尔滨  ;126.63;45.75
长春  ;125.35;43.88
沈阳  ;123.38;41.8
石家庄  ;114.48;38.03
太原  ;112.53;37.87
西宁  ;101.74;36.56
济南  ;117;36.65
郑州 ;113.6;34.76
南京;118.78;32.04
合肥;117.27;31.86
杭州;120.19;30.26
福州;119.3;26.08
南昌;115.89;28.68
长沙;113;28.21
武汉;114.31;30.52
广州;113.23;23.16
台北;121.5;25.05
海口;110.35;20.02
兰州;103.73;36.03
西安;108.95;34.27
成都;104.06;30.67
贵阳;106.71;26.57
昆明;102.73;25.04
香港;114.1;22.2
澳门;113.33;22.13

文件:TSP-GA.py

# -*- encoding: utf-8 -*-
import numpy as np
import pandas as pd
from DW import *

class TSP(object):
    citys = np.array([])	#城市数组
    citys_name = np.array([])
    pop_size = 50	#种群大小
    c_rate = 0.7	#交叉率
    m_rate = 0.05	#突变率
    pop = np.array([])	#种群数组
    fitness = np.array([])	#适应度数组
    city_size = -1		#标记城市数目
    ga_num = 200	#最大迭代次数
    best_dist = -1	#记录目前最优距离
    best_gen = []	#记录目前最优旅行方案
    dw = Draw()	#绘图类

    def __init__(self, c_rate, m_rate, pop_size, ga_num):
        self.fitness = np.zeros(self.pop_size)
        self.c_rate = c_rate
        self.m_rate = m_rate
        self.pop_size = pop_size
        self.ga_num = ga_num

    def init(self):
        tsp = self
        # tsp.load_Citys()	#加载城市数据
        tsp.load_Citys2()	#加载城市数据
        tsp.pop = tsp.creat_pop(tsp.pop_size)	#创建种群
        tsp.fitness = tsp.get_fitness(tsp.pop)	#计算初始种群适应度
        tsp.dw.bound_x = [np.min(tsp.citys[:, 0]), np.max(tsp.citys[:, 0])]	#计算绘图时的X界
        tsp.dw.bound_y = [np.min(tsp.citys[:, 1]), np.max(tsp.citys[:, 1])]	#计算绘图时的Y界
        tsp.dw.set_xybound(tsp.dw.bound_x, tsp.dw.bound_y)	#设置边界

    def creat_pop(self, size):
        pop = []
        for i in range(size):
            gene = np.arange(self.citys.shape[0])	#问题的解,基因,种群中的个体:[0,...,city_size]
            np.random.shuffle(gene)		#打乱数组[0,...,city_size]
            pop.append(gene)			#加入种群
        return np.array(pop)

    def get_fitness(self, pop):
        d = np.array([])				#适应度记录数组
        for i in range(pop.shape[0]):
            gen = pop[i]  # 取其中一条基因(编码解,个体)
            dis = self.gen_distance(gen)	#计算此基因优劣(距离长短)
            dis = self.best_dist / dis	#当前最优距离除以当前pop[i](个体)距离;越近适应度越高,最优适应度为1
            d = np.append(d, dis)  # 保存适应度pop[i]
        return d

    def get_local_fitness(self, gen, i):
        '''
        计算地i个城市的邻域
        交换基因数组中任意两个值组成的解集:称为邻域。计算领域内所有可能的适应度
        :param gen:城市路径
        :param i:第i城市
        :return:第i城市的局部适应度
        '''
        di = 0
        fi = 0
        if i == 0:
            di = self.ct_distance(self.citys[gen[0]], self.citys[gen[-1]])
        else:
            di = self.ct_distance(self.citys[gen[i]], self.citys[gen[i - 1]])
        od = []
        for j in range(self.city_size):
            if i != j:
                od.append(self.ct_distance(self.citys[gen[i]], self.citys[gen[i - 1]]))
        mind = np.min(od)
        fi = di - mind
        return fi

    def EO(self, gen):
    	#极值优化,传统遗传算法性能不好,这里混合EO
    	#其会在整个基因的领域内,寻找一个最佳变换以更新基因
        local_fitness = []
        for g in range(self.city_size):
            f = self.get_local_fitness(gen, g)
            local_fitness.append(f)
        max_city_i = np.argmax(local_fitness)
        maxgen = np.copy(gen)
        if 1 < max_city_i < self.city_size - 1:
            for j in range(max_city_i):
                maxgen = np.copy(gen)
                jj = max_city_i
                while jj < self.city_size:
                    gen1 = self.exechange_gen(maxgen, j, jj)
                    d = self.gen_distance(maxgen)
                    d1 = self.gen_distance(gen1)
                    if d > d1:
                        maxgen = gen1[:]
                    jj += 1
        gen = maxgen
        return gen

    def select_pop(self, pop):
    	#选择种群,优胜劣汰,策略1:低于平均的要替换改变
        best_f_index = np.argmax(self.fitness)
        av = np.median(self.fitness, axis=0)
        for i in range(self.pop_size):
            if i != best_f_index and self.fitness[i] < av:
                pi = self.cross(pop[best_f_index], pop[i])
                pi = self.mutate(pi)
                # d1 = self.distance(pi)
                # d2 = self.distance(pop[i])
                # if d1 < d2:
                pop[i, :] = pi[:]

        return pop

    def select_pop2(self, pop):
    	#选择种群,优胜劣汰,策略2:轮盘赌,适应度低的替换的概率大
        probility = self.fitness / self.fitness.sum()
        idx = np.random.choice(np.arange(self.pop_size), size=self.pop_size, replace=True, p=probility)
        n_pop = pop[idx, :]
        return n_pop

    def cross(self, parent1, parent2):
        """交叉p1,p2的部分基因片段"""
        if np.random.rand() > self.c_rate:
            return parent1
        index1 = np.random.randint(0, self.city_size - 1)
        index2 = np.random.randint(index1, self.city_size - 1)
        tempGene = parent2[index1:index2]  # 交叉的基因片段
        newGene = []
        p1len = 0
        for g in parent1:
            if p1len == index1:
                newGene.extend(tempGene)  # 插入基因片段
            if g not in tempGene:
                newGene.append(g)
            p1len += 1
        newGene = np.array(newGene)

        if newGene.shape[0] != self.city_size:
            print('c error')
            return self.creat_pop(1)
            # return parent1
        return newGene

    def mutate(self, gene):
        """突变"""
        if np.random.rand() > self.m_rate:
            return gene
        index1 = np.random.randint(0, self.city_size - 1)
        index2 = np.random.randint(index1, self.city_size - 1)
        newGene = self.reverse_gen(gene, index1, index2)
        if newGene.shape[0] != self.city_size:
            print('m error')
            return self.creat_pop(1)
        return newGene

    def reverse_gen(self, gen, i, j):
    	#函数:翻转基因中i到j之间的基因片段
        if i >= j:
            return gen
        if j > self.city_size - 1:
            return gen
        parent1 = np.copy(gen)
        tempGene = parent1[i:j]
        newGene = []
        p1len = 0
        for g in parent1:
            if p1len == i:
                newGene.extend(tempGene[::-1])  # 插入基因片段
            if g not in tempGene:
                newGene.append(g)
            p1len += 1
        return np.array(newGene)

    def exechange_gen(self, gen, i, j):
    	#函数:交换基因中i,j值
        c = gen[j]
        gen[j] = gen[i]
        gen[i] = c
        return gen

    def evolution(self):
    	#主程序:迭代进化种群
        tsp = self
        for i in range(self.ga_num):
            best_f_index = np.argmax(tsp.fitness)
            worst_f_index = np.argmin(tsp.fitness)
            local_best_gen = tsp.pop[best_f_index]
            local_best_dist = tsp.gen_distance(local_best_gen)
            if i == 0:
                tsp.best_gen = local_best_gen
                tsp.best_dist = tsp.gen_distance(local_best_gen)

            if local_best_dist < tsp.best_dist:
                tsp.best_dist = local_best_dist	#记录最优值
                tsp.best_gen = local_best_gen	#记录最个体基因
                #绘图
                tsp.dw.ax.cla()
                tsp.re_draw()
                tsp.dw.plt.pause(0.001)
            else:
                tsp.pop[worst_f_index] = self.best_gen
            print('gen:%d evo,best dist :%s' % (i, self.best_dist))
	
            tsp.pop = tsp.select_pop(tsp.pop)	#选择淘汰种群
            tsp.fitness = tsp.get_fitness(tsp.pop)	#计算种群适应度
            for j in range(self.pop_size):
                r = np.random.randint(0, self.pop_size - 1)
                if j != r:
                    tsp.pop[j] = tsp.cross(tsp.pop[j], tsp.pop[r])	#交叉种群中第j,r个体的基因
                    tsp.pop[j] = tsp.mutate(tsp.pop[j])	#突变种群中第j个体的基因
            #self.best_gen = self.EO(self.best_gen)	#极值优化,防止收敛局部最优
            tsp.best_dist = tsp.gen_distance(self.best_gen)	#记录最优值

    def load_Citys(self, file='china_main_citys.csv', delm=','):
        # 中国34城市经纬度
        data = pd.read_csv(file, delimiter=delm, header=None).values
        #china_main_citys.csv数据太大,只计算部分如:湖南省关键字的
        self.citys = data[data[:, 0] == '湖南省', 4:]
        self.citys_name = data[data[:, 0] == '湖南省', 2]
        self.city_size = self.citys.shape[0]

    def load_Citys2(self, file='china.csv', delm=';'):
        # 中国34城市经纬度
        data = pd.read_csv(file, delimiter=delm, header=None).values
        self.citys = data[:, 1:]
        self.citys_name = data[:, 0]
        self.city_size = data.shape[0]

    def gen_distance(self, gen):
    	#计算基因所代表的总旅行距离
        distance = 0.0
        for i in range(-1, len(self.citys) - 1):
            index1, index2 = gen[i], gen[i + 1]
            city1, city2 = self.citys[index1], self.citys[index2]
            distance += np.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2)
        return distance

    def ct_distance(self, city1, city2):
    	#计算2城市之间的欧氏距离
        d = np.sqrt((city1[0] - city2[0]) ** 2 + (city1[1] - city2[1]) ** 2)
        return d

    def draw_citys_way(self, gen):
        '''
        根据一条基因gen绘制一条旅行路线
        :param gen:
        :return:
        '''
        tsp = self
        dw = self.dw
        m = gen.shape[0]
        tsp.dw.set_xybound(tsp.dw.bound_x, tsp.dw.bound_y)
        for i in range(m):
            if i < m - 1:
                best_i = tsp.best_gen[i]
                next_best_i = tsp.best_gen[i + 1]
                best_icity = tsp.citys[best_i]
                next_best_icity = tsp.citys[next_best_i]
                dw.draw_line(best_icity, next_best_icity)
        start = tsp.citys[tsp.best_gen[0]]
        end = tsp.citys[tsp.best_gen[-1]]
        dw.draw_line(end, start)

    def draw_citys_name(self, gen, size=5):
        '''
        根据一条基因gen绘制对应城市名称
        :param gen:
        :param size: text size
        :return:
        '''
        tsp = self
        m = gen.shape[0]
        tsp.dw.set_xybound(tsp.dw.bound_x, tsp.dw.bound_y)
        for i in range(m):
            c = gen[i]
            best_icity = tsp.citys[c]
            tsp.dw.draw_text(best_icity[0], best_icity[1], tsp.citys_name[c], 10)

    def re_draw(self):
    	#重绘图;每次迭代后绘制一次,动态展示。
        tsp = self
        tsp.dw.draw_points(tsp.citys[:, 0], tsp.citys[:, 1])
        tsp.draw_citys_name(tsp.pop[0], 8)
        tsp.draw_citys_way(self.best_gen)
        
def main():
    tsp = TSP(0.5, 0.1, 100, 500)
    tsp.init()
    tsp.evolution()
    tsp.re_draw()
    tsp.dw.plt.show()

if __name__ == '__main__':
    main()

绘图文件:DW.py

import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import matplotlib.animation as animation

class Draw(object):
    bound_x = []
    bound_y = []

    def __init__(self):
        self.fig, self.ax = plt.subplots()
        self.plt = plt
        self.set_font()

    def draw_line(self, p_from, p_to):
        line1 = [(p_from[0], p_from[1]), (p_to[0], p_to[1])]
        (line1_xs, line1_ys) = zip(*line1)
        self.ax.add_line(Line2D(line1_xs, line1_ys, linewidth=1, color='blue'))

    # def draw_arrow(self, p_from, p_to):
    #     if p_from.shape[0] != 2 and p_to.shape[0] != 2:
    #         print('error,', p_from, p_to)
    #         return
    #     p_from = list(p_from)
    #     p_to = list(p_to)
    #     self.ax.arrow(p_from[0], p_from[1], p_to[0] - p_from[0], p_to[1] - p_from[1],
    #                   length_includes_head=True,
    #                   head_width=(self.bound_x[1] - self.bound_x[0]) / 100,
    #                   head_length=(self.bound_x[1] - self.bound_x[0]) / 50,
    #                   fc='blue', ec='black')

    def draw_points(self, pointx, pointy):
        self.ax.plot(pointx, pointy, 'ro')

    def set_xybound(self, x_bd, y_bd):
        self.ax.axis([x_bd[0], x_bd[1], y_bd[0], y_bd[1]])

    def draw_text(self, x, y, text, size=8):
        self.ax.text(x, y, text, fontsize=size)

    def set_font(self, ft_style='SimHei'):
        plt.rcParams['font.sans-serif'] = [ft_style]  # 用来正常显示中文标签

  • 20
    点赞
  • 147
    收藏
    觉得还不错? 一键收藏
  • 36
    评论
### 回答1: 遗传算法是一种基于生物进化原理的优化算法,可以用于求解TSP旅行商问题TSP问题是指在给定的一组城市和每对城市之间的距离下,找到一条经过每个城市一次且总距离最短的路径。 遗传算法的基本思想是将问题转化为一个个体的遗传过程,通过不断的交叉、变异和选择等操作,逐步优化种群中的个体,最终得到最优解。 具体实现过程如下: 1. 初始化种群:随机生成一定数量的个体,每个个体表示一条路径,即一组城市的访问顺序。 2. 评估适应度:计算每个个体的适应度,即路径长度。适应度越高,说明路径越短,个体越优秀。 3. 选择操作:根据适应度大小,选择一部分个体作为下一代的父代。 4. 交叉操作:对父代进行交叉操作,生成新的个体。交叉操作可以采用顺序交叉、部分映射交叉等方法。 5. 变异操作:对新生成的个体进行变异操作,引入一定的随机性。变异操作可以采用交换、插入、翻转等方法。 6. 评估适应度:计算新生成的个体的适应度。 7. 选择操作:根据适应度大小,选择一部分个体作为下一代的父代。 8. 重复步骤4-7,直到达到停止条件(如达到最大迭代次数、适应度达到一定阈值等)。 9. 输出最优解:从最终种群中选择适应度最高的个体作为最优解,即TSP问题的最短路径。 总之,遗传算法是一种有效的求解TSP问题的方法,可以通过不断的迭代优化,得到最优解。 ### 回答2: TSP问题指的是旅行商问题,即在一定的时间内,旅行商需要访问所有城市一次,最终回到起点,并且最小化行程距离。TSP问题作为优化问题,是计算机科学中的经典问题之一。传统的找到TSP问题最优解的求解方法是暴力枚举,但是对于较大的问题规模来说,这种方法变得非常不现实。因此,遗传算法成为了很好的解决方法。 遗传算法是一种优化算法,模拟自然界的进化过程,在解决问题时通过对“遗传信息”的编码进行选择、交叉、变异等操作从而达到全局最优或近似最优的解决方案。对于TSP问题,我们可以将遗传算法应用于其中,帮助我们找到全局最短路径。 具体实现时,我们将每个解看作一个种群中的个体,并对其进行随机编码,形成一个基因串。遗传算法会运用自然选择过程,筛选出适应度较高的基因串,构建适应度函数F。通过选择、交叉和种群变异操作,让基因串在不断迭代、进化的过程中,逐渐找到TSP的最优解。 具体实施步骤如下: 1. 确定优化目标和适应度函数:我们需要定义适当的算法来度量每个个体的适应度大小,例如,对于TSP问题,我们可以以旅行商需要走的总距离作为适应度函数,离初始点越近,所需距离越短,适应度就越高。 2. 生成种群:我们通过随机选择点来构建种群,每个种群中的个体表示不同的旅游路径。 3. 选择:通过在种群中选择一部分高适应度的个体,产生新的种群。 4. 交叉:在新的种群中选择一些个体进行交叉,重新生成新的种群。 5. 变异:在新的种群中选择一部分个体进行变异操作,即对某些基因序列进行随机修改,生成新的种群。 6. 迭代:重复3-5步,多次迭代后,选择适应度最高个体作为结果输出。 Python作为一种高阶编程语言,在处理遗传算法中的求解问题方面表现突出。在实现过程中,我们可以使用Python中的numpy模块来实现矩阵计算,使用matplotlib模块对结果进行可视化处理,并结合python的其它模块,如pandas、networkx等来进行数据处理和图形展示,最终得到一个完整的TSP问题求解。 ### 回答3: 旅行商问题TSP)是一个NP难问题,它假设有一位旅行商要访问n个城市,在每个城市之间都有一定的距离,要求旅行商走遍所有城市且回到起点的路径是最短的。遗传算法是一种解决TSP问题的有效方法之一,Python是一门流行的编程语言,能够方便地实现遗传算法遗传算法采用生物进化的概念,将问题的解表示为一个染色体,通过模拟基因交叉、变异等操作,逐代优化解的质量。在TSP问题中,每个染色体都表示一条路径。为方便操作,可以将每个路径用城市编号表示。 首先需要构建初始种群,方法可以采用随机生成、贪心算法等。每个染色体的适应度可以用路径长度来表示,路径长度越小,适应度越高。随后进行选择操作,选择适应度高的染色体进行繁殖。为获得更多的多样性,可以采用轮盘赌算法或锦标赛选择算法。 繁殖是遗传算法的重要过程之一,主要是模拟基因交叉和变异。基因交叉分为单点交叉、多点交叉、均匀交叉等方式,可以使用随机数生成器确定交叉点和交叉方式。变异是指染色体中的一些基因改变了其值,一般用于增加种群多样性。变异的方式包括随机突变、倒位变异等。 进行多代迭代后,可以得到最优解,其中最优解的染色体表示了旅行商走遍各个城市的最短路径。最后,将该染色体中编号转换为具体城市名称,即可得到最优路径。 总之,遗传算法求解TSP问题的一种有效方法,Python语言具有简洁、高效和易于实现的特点,是实现遗传算法求解TSP问题的理想工具。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值