遗传算法解决TSP问题一(python实现)

  旅行推销员问题(英语:Travelling salesman problem, TSP)是这样一个问题:给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。

遗传算法流程图:

开始
生成初始种群
交叉变异
评价所有染色体的适应值函数
轮盘赌生成新种群
终止条件
从此较优种群根据适应值函数选择最佳染色体
找到最优路径及最优值
结束

顺序交叉OX(Order Crossover ):
顺序交叉由Davis提出,其步骤如下:
①从第一双亲随机选择一个子串:
在这里插入图片描述
②通过拷贝子串,产生一个原始后代:
在这里插入图片描述
③删去第二双亲中子串已有的城市,得到原始后代需要的其它城市的顺序:
在这里插入图片描述
④从左到右将这些城市定位到后代的空缺位置上。

互换变异(Swap Mutation):
互换变异的主要思想为:随机地选择两个位置,并将这两个位置上的城市相互交换。
示例:
在这里插入图片描述
下面给出一个TSP问题完整代码:

import numpy as np
import random as rd

"""计算染色体对应的路线总长"""
def countlen(chromo, distance, cityNum):
    len = 0
    for iii in range(cityNum - 1):
        len += distance[chromo[iii]][chromo[iii + 1]]
    len += distance[chromo[cityNum - 1]][chromo[0]]  # 加上返回起点的距离
    return len

"""生成初始种群"""
def crepopula(cityNum, M):
    popula = []  # 种群
    for ii in range(M):
        chromo = np.random.permutation(cityNum)  # 染色体
        popula.append(chromo)
    return popula

"""计算种群中每个个体的累积概率"""
def countprobabily(popula,distance,cityNum):
    evall = []
    for chromo in popula:
        eval = 10 - countlen(chromo, distance,cityNum)  # 适应值函数
        evall.append(eval)
    seval = sum(evall)
    probabil = [x/seval for x in evall]   #单个概率
    probabily = probabil.copy()
    for i in range(1,len(popula)):
        probabily[i]=probabily[i]+probabily[i-1]  #累积概率
    return probabily

"""轮盘赌挑选子代个体"""
def lpd(popula,probabily,M):
    newpopula=[]
    for i in range(M):
        proba = rd.random()
        for ii in range(len(probabily)):
            if probabily[ii] >= proba:
                selechromo = popula[ii]
                break
        newpopula.append(selechromo)
    return newpopula

"""顺序交叉"""
def crossover(father1, father2,cityNum):
    ord1 = rd.randint(0, cityNum)
    ord2 = rd.randint(0, cityNum)
    if ord1 == ord2:
        son = father1
    elif ord1 < ord2:
        son = ox(father1, father2, ord1, ord2)
    else:
        son = ox(father1, father2, ord2, ord1)
    return son

def ox(father1, father2, ord1, ord2):  # 顺序交叉
    son1 = father1[ord1:ord2]  # 继承父代1
    son2 = []
    for gene in father2:
        if gene not in son1:
            son2.append(gene)  # 继承父代2
    for i in range(len(son1)):  # 插入
        son2.insert(ord1, son1[len(son1) - i - 1])
    return son2

"""互换变异"""
def variat(father,cityNum):
    or1 = rd.randint(0, cityNum-1)
    or2 = rd.randint(0, cityNum-1)
    son = father.copy()
    son[or1] = father[or2]
    son[or2] = father[or1]
    return son


def main():
    M = 10  # 种群规模
    cityNum = 5  # 城市数量,染色体长度

    """5个城市坐标位置"""
    cities = [[0, 0], [1, 0], [1, 1], [1, 2], [0, 1]]
    """生成距离矩阵"""
    distance = np.zeros([cityNum, cityNum])
    for i in range(cityNum):
        for j in range(cityNum):
            distance[i][j] = pow((pow(cities[i][0] - cities[j][0], 2) + pow(cities[i][1] - cities[j][1], 2)), 0.5)
    #print(distance)

    """初始化种群"""
    popula = crepopula(cityNum, M)

    for n in range(2000):  #循环次数
        """种群进化"""
        pc = 0.5  # 交叉率
        pv = 0.1  # 变异率
        son = []
        ##顺序交叉
        crossgroup = []
        for i in range(M):
            cpb = rd.random()
            if cpb < pc:  # 小于交叉率的进行交叉
                crossgroup.append(popula[i])
        if len(crossgroup) % 2 == 1:  # 如果奇数个
            del crossgroup[-1]
        # print(crossgroup)
        if crossgroup != []:  # 顺序交叉
            for ii in range(0, len(crossgroup), 2):
                sonc = crossover(crossgroup[ii], crossgroup[ii + 1], cityNum)
                son.append(sonc)
        # print(son)
        ##互换变异
        variatgroup = []
        for j in range(M):
            vpb = rd.random()
            if vpb < pv:  # 小于变异率的进行变异
                variatgroup.append(popula[j])
        # print(variatgroup)
        if variatgroup != []:
            for vag in variatgroup:
                sonv = variat(vag, cityNum)
                son.append(sonv)
        # print(son)

        """计算每个染色体的累积概率"""
        populapuls = popula + son
        # print(populapuls)
        probabily = countprobabily(populapuls, distance, cityNum)

        """轮盘赌选择新种群"""
        popula = lpd(populapuls, probabily, M)

    #print(popula)
    """挑选最好的染色体"""
    opt_chr = popula[0]
    opt_len = countlen(popula[0], distance, cityNum)
    for chr in popula:
        chrlen = countlen(chr, distance, cityNum)
        if chrlen < opt_len:
            opt_chr = chr
            opt_len = chrlen
    print("最优路径: "+str(opt_chr))
    print("最优值"+str(opt_len))


if __name__ == "__main__":
    main()

输出结果:

最优路径: [2, 3, 4, 0, 1]
最优值5.414213562373095

在这里插入图片描述
  从图中可以看出,遗传算法得出的最优路径是正确的。
  但是,此遗传算法的交叉变异方式都是较简单的方式,当城市数目较大时,不能找到最优值,下次将尝试启发式地交叉变异方式,请见下篇。

  • 11
    点赞
  • 101
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值