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

  上次尝试用简单的交叉变异方式编写了遗传算法,这次将使用启发式的交叉变异方式:
启发式交叉由Grefenstette, Gopal, Rosrnaita和Gucht首先提出。启发式交叉步骤(最近邻点法)为:
 步骤1:从一对双亲中随机地选取一个城市作为开始城市;
 步骤2:由当前城市出发,选择一条不构成循环的最短边(由双亲表达的)。若两条边都构成循环,则随机选取一个能使巡回继续的城市;
 步骤3:如巡回完成,停机;否则转第2步。
遗传算法解决TSP问题二(python实现)_第1张图片
例如,对于如上的地图,随机选取城市3作为开始的城市,得到的子代为:
遗传算法解决TSP问题二(python实现)_第2张图片

启发式变异由Cheng和Gen提出。采用邻域技术,以获得后代的改进。启发式变异过程如下:
步骤1:随机地选出λ个基因;
步骤2:按所有选出基因的可能的换位产生邻域;
步骤3:评估所有邻域点,选出最好的作为变异产生的后代。
示例:
遗传算法解决TSP问题二(python实现)_第3张图片
下面给出启发式交叉变异方式解决TSP问题的完整代码:

import numpy as np
import random as rd
import matplotlib.pyplot as plt

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

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

"""计算种群中每个个体的累积概率"""
def countprobabily(popula,distance,cityNum):
    evall = []
    for chromo in popula:
        eval = max(30000 - countlen(chromo, distance,cityNum),0)  # 适应值函数
        evall.append(eval)
    seval = sum(evall)
    probabil = evall/seval   #单个概率
    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_nn(father1, father2,cityNum,distance):
    father_1 = father1.copy()
    father_2 = father2.copy()
    city0 = rd.randint(0, cityNum-1)  #随机选择一个城市作为起始点
    son = [city0]
    while len(son) < len(father1):
        ord1 = father_1.index(city0)
        ord2 = father_2.index(city0)
        if ord1 == len(father_1)-1 :
            ord1 = -1
        if ord2 == len(father_1)-1 :
            ord2 = -1
        city1 = father_1[ord1 + 1]
        city2 = father_2[ord2 + 1]
        father_1.remove(city0)
        father_2.remove(city0)
        if distance[city0][city1] <= distance[city0][city2]:
            son.append(city1)
            city0 = city1
        else:
            son.append(city2)
            city0 = city2
    return son

"""启发式变异"""
import itertools
def variat2(father,cityNum,distance):
    or1 = rd.randint(0, cityNum - 1) #随机生成5个位置
    or2 = rd.randint(0, cityNum - 1)
    or3 = rd.randint(0, cityNum - 1)
    or4 = rd.randint(0, cityNum - 1)
    or5 = rd.randint(0, cityNum - 1)
    nosame = list(set([or1, or2, or3, or4, or5]))
    ords = list(itertools.permutations(nosame, len(nosame)))
    sons = []               #将所有子代表示出来
    sonn = father.copy()
    for ord in ords:
        for ii in range(len(nosame)):
            sonn[nosame[ii]] = father[ord[ii]]
        sons.append(sonn)
    son_leng = []       #计算所有子代的距离
    for sonn in sons:
        leng = countlen(sonn, distance, cityNum)
        son_leng.append(leng)
    n = son_leng.index(min(son_leng))   #挑选最小距离
    return sons[n]


def main():
    M = 10  # 种群规模
    cityNum = 52  # 城市数量,染色体长度
    """52个城市坐标位置"""
    cities = [[565, 575],[25, 185],[345, 750],[945, 685],[845, 655],[880, 660],[25, 230],[525, 1000],[580, 1175],
              [650, 1130],[1605, 620],[1220, 580],[1465, 200],[1530, 5],[845, 680],[725, 370],[145, 665],[415, 635],
              [510, 875],[560, 365],[300, 465],[520, 585],[480, 415],[835, 625],[975, 580],[1215, 245],[1320, 315],
              [1250, 400],[660, 180],[410, 250],[420, 555],[575, 665],[1150, 1160],[700, 580],[685, 595],[685, 610],
              [770, 610],[795, 645],[720, 635],[760, 650],[475, 960],[95, 260],[875, 920],[700, 500],[555, 815],
              [830, 485],[1170, 65],[830, 610],[605, 625],[595, 360],[1340, 725],[1740, 245]]
    """生成距离矩阵"""
    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)

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

    for n in range(600):  # 循环次数
        """种群进化"""
        pc = 0.8  # 交叉率
        pv = 0.25  # 变异率
        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]
        if crossgroup != []:  # 启发式交叉
            for ii in range(0, len(crossgroup), 2):
                sonc = crossover_nn(crossgroup[ii], crossgroup[ii + 1], cityNum, distance)
                son.append(sonc)
        ##变异
        variatgroup = []
        for j in range(M):
            vpb = rd.random()
            if vpb < pv:  # 小于变异率的进行变异
                variatgroup.append(popula[j])

        if variatgroup != []:
            for vag in variatgroup:
                sonv = variat2(vag, cityNum, distance)  #启发式变异
                son.append(sonv)

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

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

    """挑选最好的染色体"""
    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))

    """绘制地图"""
    for cor in range(len(opt_chr)-1) :
        x = [cities[opt_chr[cor]][0],cities[opt_chr[cor+1]][0]]
        y = [cities[opt_chr[cor]][1],cities[opt_chr[cor+1]][1]]
        plt.plot(x,y,"b-")
    plt.show()

if __name__ =="__main__":
    main()

输出结果:(已知最优解:7542)
此程序的最优总距离:10802.005083049522
遗传算法解决TSP问题二(python实现)_第4张图片
  由此,可见启发式算法收敛性能有所提高,并且能够跳出局部最优解,更好地找到全局最优解。收敛速度和收敛精度有了大大的提高。所以启发式遗传算法优于不加启发式的遗传算法。


备注:通过增大种群规模(程序中的M),可以使运算结果更接近已知最优解。

你可能感兴趣的:(Python)