遗传算法解决TSP旅行商问题(附:Python实现)

前言

我先啰嗦一下:不是很喜欢写计算智能的算法,因为一个算法就要写好久。前前后后将近有两天的时间。
好啦,现在进入正题。

巡回旅行商问题(TSP)是一个组合优化方面的问题,已经成为测试组合优化新算法的标准问题。应用遗传算法解决 TSP 问题,首先对访问城市序列进行排列组合的方法编码,这保证了每个城市经过且只经过一次。接着生成初始种群,并计算适应度函数,即计算遍历所有城市的距离。然后用最优保存法确定选择算子,以保证优秀个体直接复制到下一代。采用有序交叉和倒置变异法确定交叉算子和变异算子。

算法流程

在这里插入图片描述

旅行商问题的遗传算法实现

  1. 初始群体设定
    一般都是随机生成一个规模为 N 的初始群体。在这里,我们定义一个s行t列的pop矩阵来表示群体,t 为城市个数 + 1,即 N + 1,s 为样本中个体数目。在本文探讨了 30 个城市的 TSP 问题,此时 t 取值 31,该矩阵中每一行的前 30 个元素表示经过的城市编号,最后一个元素表示适应度函数的取值,即每个个体所求的距离。
  2. 适应度函数的设计是根据个体适应值对其优劣判定的评价函数。在该问题中用距离的总和作为适应度函数,来衡量求解结果是否最优。
    在这里插入图片描述
  3. 选择指以一定的概率从群体中选择优胜个体的操作,它是建立在群体中个体适应度评估基础上的。为了加快局部搜索的速度,在算法中采用最优保存策略的方法,即将群体中适应度最大的个体直接替换适应度最小的个体。它们不进行交叉和变异运算,而是直接复制到下一代,以免交叉和变异运算破坏种群中的优秀解答。
  4. 交叉算子是产生新个体的主要手段。它是指将个体进行两两配对,以交叉概率 Pc 将配对的父代个体的部分结构加以替换重组生成新个体的操作。本文中采用有序交叉法来实现。有序交叉法的步骤描述如下:
    在这里插入图片描述
    5.变异操作是以较小的概率 Pm 对群体中个体编码串上的某位或者某些位作变动,从而生成新的个体。本文中采用倒置变异法:假设当前个体 X为(1 3 7 4 8 0 5 9 6 2),如果当前随机概率值小
    于 Pm,则随机选择来自同一个体的两个点mutatepoint(1) 和 mutatepoint(2),然后倒置两点的中间部分,产生新的个体。例如,假设随机选择个体 X 的两个点“7”和“9”,则倒置该两个点的中间部分,即将“4805”变为“5084”,产生新的个体 X 为(1 3 7 5 0 8 4 9 6 2)。
    6.终止条件为循环一定的代数。

代码实现

from itertools import permutations
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from itertools import combinations, permutations 
%matplotlib inline
def fitnessFunction(pop,num,city_num,x_position_add_end,y_position_add_end):
    '''适应度函数,计算每个排列的适应度,并保存到pop矩阵第二维的最后一项'''
    for x1 in range(num):
        square_sum = 0
        for x2 in range(city_num):
            square_sum += (x_position_add_end[int(pop[x1][x2])] - x_position_add_end[int(pop[x1][x2+1])])**2 + (y_position_add_end[int(pop[x1][x2])] - y_position_add_end[int(pop[x1][x2+1])])**2
        # print(round(1/np.sqrt(square_sum),7))
        pop[x1][-1] = round(1/np.sqrt(square_sum),7)
def choiceFuction(pop): 
    '''
    这里的做法:比如A当前种群中的最优解,B为经过交叉、变异后的最差解,把A作为最当前代中的最优解保存下来作为这一代的最优解,同时A也参与交叉
    和变异。经过交叉、变异后的最差解为B,那么我再用A替代B。
    :argument pop矩阵
    :return 本代适应度最低的个体的索引值和本代适应度最高的个体
    '''
    yield np.argmin(pop[:, -1])
    yield pop[np.argmax(pop[:, -1])]
def choice(pop,num,city_num,x_position_add_end,y_position_add_end,b):
    fitnessFunction(pop,num,city_num,x_position_add_end,y_position_add_end)
    c,d =choiceFuction(pop)
    # 上一代的最优值替代本代中的最差值
    pop[c] = b
    return pop
def drawPic(maxFitness,x_position,y_position,i):
    index = np.array(maxFitness[:-1],dtype=np.int32)
    x_position_add_end = np.append(x_position[index],x_position[[index[0]]])
    y_position_add_end = np.append(y_position[index],y_position[[index[0]]])
    fig = plt.figure()
    plt.plot(x_position_add_end,y_position_add_end,'-o')
    plt.xlabel('x',fontsize = 16)
    plt.ylabel('y',fontsize = 16)
    plt.title('{iter}'.format(iter=i))

以下的交叉计算应该是用递归的算法实现比较合理,但本人能力有限。有大佬看得懂我乱七八糟的代码的,请给我提提建议。

def matuingFuction(pop,pc,city_num,pm,num):
    mating_matrix =np.array(1-(np.random.rand(num)>pc),dtype=np.bool)  # 交配矩阵,如果为true则进行交配
    a = list(pop[mating_matrix][:,:-1])# 进行交配的个体
    b = list(pop[np.array(1-mating_matrix,dtype=bool)][:,:-1]) # 未进行交配的个体,直接放到下一代
    b = [list(i) for i in b] # 对b进行类型转换,避免下面numpy.array 没有index属性
#     print(a)
    if len(a)%2 !=0:
        b.append(a.pop())
#     print('ab的长度:',len(a),len(b))
    for i in range(int(len(a)/2)):
        # 随机初始化两个交配点,这里写得不好,这边的两个点初始化都是一个在中间位置偏左,一个在中间位置偏右
        p1 = np.random.randint(1,int(city_num/2)+1)
        p2 = np.random.randint(int(city_num/2)+1,city_num)
        x1 = list(a.pop())
        x2 = list(a.pop())
        matuting(x1,x2,p1,p2)
        # 交配之后产生的个体进行一定概率上的变异
        variationFunction(x1,pm,city_num)
        variationFunction(x2,pm,city_num)
        b.append(x1)
        b.append(x2)
    zero = np.zeros((num,1))
#     print('b的形状:',len(b))
    temp = np.column_stack((b, zero))
    return temp
        
    
def matuting(x1,x2,p1,p2):
        # 以下进行交配
        # 左边交换位置
        temp = x1[:p1]
        x1[:p1] = x2[:p1]
        x2[:p1] = temp
        # 右边交换位置
        temp = x1[p2:]
        x1[p2:] = x2[p2:]
        x2[p2:] = temp
        # 寻找重复的元素
        center1 = x1[p1:p2]
        center2 = x2[p1:p2]
        while True: # x1左边
            for i in x1[:p1]:
                if i in center1:
                    # print(center1.index(i)) # 根据值找到索引
                    x1[x1[:p1].index(i)] = center2[center1.index(i)]
                    break
            if np.intersect1d(x1[:p1],center1).size == 0: # 如果不存在交集,则循环结束
                break
        while True: # x1右边
            for i in x1[p2:]:
                if i in center1:
                    # print(center1.index(i)) # 根据值找到索引
                    x1[x1[p2:].index(i) + p2] = center2[center1.index(i)]
                    # print(x1)
                    break
            if np.intersect1d(x1[p2:],center1).size == 0: # 如果不存在交集,则循环结束
                break
        while True: # x2左边
            for i in x2[:p1]:
                if i in center2:
#                     print(center2.index(i)) # 根据值找到索引
                    x2[x2[:p1].index(i)] = center1[center2.index(i)]
                    break
            if np.intersect1d(x2[:p1],center2).size == 0: # 如果不存在交集,则循环结束
                break
        while True: # x2右边
            for i in x2[p2:]:
                if i in center2:
                    # print(center2.index(i)) # 根据值找到索引
                    x2[x2[p2:].index(i) + p2] = center1[center2.index(i)]
                    # print(x2)
                    break
            if np.intersect1d(x2[p2:],center2).size == 0: # 如果不存在交集,则循环结束
                break   
               
def variationFunction(list_a,pm,city_num):
    '''变异函数'''
    if np.random.rand() < pm:
        p1 = np.random.randint(1,int(city_num/2)+1)
        p2 = np.random.randint(int(city_num/2)+1,city_num)
#         print(p1,p2)
        temp = list_a[p1:p2]
        temp.reverse()
        list_a[p1:p2] = temp
#         print(list_a)
def main():
    # 初始化
    pop = [] # 存放访问顺序和每个个体适应度
    num = 250 # 初始化群体的数目
    city_num = 10 # 城市数目
    pc = 0.9 # 每个个体的交配概率
    pm = 0.2 # 每个个体的变异概率 
    x_position = np.random.randint(0,100,size=city_num)
    y_position = np.random.randint(0,100,size=city_num)
    x_position_add_end = np.append(x_position,x_position[0])
    y_position_add_end = np.append(y_position,y_position[0])
    for i in range(num):
        pop.append(np.random.permutation(np.arange(0,city_num))) # 假设有5个城市,初始群体的数目为60个
        # 初始化化一个60*1的拼接矩阵,值为0
    zero = np.zeros((num,1))
    pop = np.column_stack((pop, zero)) # 矩阵的拼接
    fitnessFunction(pop,num,city_num,x_position_add_end,y_position_add_end)
    for i in range(180):
        a,b = choiceFuction(pop) # a 为当代适应度最小的个体的索引,b为当代适应度最大的个体,这边要保留的是b
#         print('索引值和适应度最大的个体:',a,b)
    #     pop[a]=b
        if (i+1)%10==0:
            drawPic(b,x_position,y_position,i+1) # 根据本代中的适应度最大的个体画图
        pop_temp = matuingFuction(pop,pc,city_num,pm,num) #交配变异
        pop = choice(pop_temp,num,city_num,x_position_add_end,y_position_add_end,b)
        
main()

分析
迭代了180次,这个算法有了一定的效果,可能是迭代的次数不够,继续迭代可能会呈现更好的结果。但是在其中的运行过程中,发现迭代次数较多的情况下,有可能还会使得路程变远,偏离了一个较好的状态是不是算法实现存在一定问题或者是变异导致,这边没有再做更加具体研究,因为本科生当前时间有限,没办法花更多的时间,待日后继续更改。

运行结果:

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

在这里插入图片描述
参考:
基于遗传算法的旅行商问题的研究

### 回答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问题的理想工具。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值