利用遗传算法解决TSP问题及可视化程序

利用遗传算法解决TSP问题及可视化程序

  • 实验目的
  1. 掌握遗传算法在实际问题中的应用方法
  2. 理解遗传算法中染色体编码和适应度函数的定义方法
  3. 熟悉遗传算法中选择、交叉、变异操作在实际问题中的适配问题
  4. 熟悉遗传算法中的控制参数设置的性能的影响
  5. 将解决方案可视化并建立和用户交互前端程序
  • 实验内容

       在本次实验中,我们给定了中国34个省会城市的二维坐标,其中部分数据截图如下:

       数据总共由34行组成,每一行代表一个城市名字以及对应坐标。两个城市之间的距离可以通过对应坐标求欧氏距离得到。对于给定数据,要求选择始发城市和剩余33个城市中的全部城市或部分城市作为需要遍历的城市,通过编写相应的遗传算法代码,求解TSP问题中回到始发城市的路径,并且尽可能的使路径总长度最短。

                                                               图1.地图可视化

设计实现相应的前端可视化程序。例如,34个城市的可视化结果如图1左图所示。两个城市之间的距离可以通过两个城市的坐标来求出,假设在旅行商问题中,旅行商希望经过的城市为乌鲁木齐、银川、拉萨、重庆这四个城市并且起点为重庆,那么一个可行的解的可视化结果如图1右图所示。设计的软件程序中包含类似的地图可视化。

三、实验方法设计

       介绍实验中程序的总体设计方案、关键步骤的编程方法及思路,主要包括:

染色体编码和适应度定义的程序设计(伪代码或源代码截图)及说明解释

染色体编码定义:用一个列表表示,列表的第一个和最后一个元素为起始城市的序号,中间元素为途经城市的序号,按照路径的顺序排列

适应度定义:该路径的距离总和的倒数,用列表表示一个种群中的个体适应度(路径越长,其倒数越小,适应度越低)

种群初始化程序设计(伪代码或源代码截图)及说明解释

一个种群为一个二维列表,每一行代表一个个体,共有M(种群数量)列,每个个体的染色体用路径顺序的列表表示

rand函数参数为起始城市和途经城市,返回一个列表,其中列表的第一个元素和最后一个元素为起始城市,中间城市随机排列

选择操作程序设计(伪代码或源代码截图)及说明解释

用轮盘赌进行选择,一共选择M次(种群个体数目)

RWS算法的输入为每个个体被选择的概率(适应度越高,被选择概率越高),输出为选择的个体序号

交叉操作程序设计(伪代码或源代码截图)及说明解释

交叉操作主要是将输入的群体进行片段的交换,我的交叉算法参考了网上的次序杂交算法。

次序杂交算法首先随机地在双亲中选择两个杂交点,再交换杂交段,其他 位置根据双亲城市的相对位置确定。例如:A1[10]={0, 8,5,4,7,3,1,6,9,2},A2[10]={1,5,4,6,9,0,3,2,8,7}, 随机杂交点为 4,7。 首先交换杂交段: B1[10]={&,&,&,&|9,0,3,2|&,&} B2[10]={&,&, &,&|7,3,1,6|&,&}, 从 A1 的第二个杂交点开始 9— 2—0—8—5—4—7—3—1—6,去除杂交段中的元素得 8—5—4—7—1—6, 依次从第二个杂交点开始填入得 B1[10]={4,7,1,6,9,0,3,2,8,5},同理得 B2[10]={4,9, 0,2,7,3,1,6,8,5}。

但是即使用此交叉算法优化出的最短路线长度仍为370左右,因此我在交叉时产生了种群数M二倍的子代,再从子代中选出M个相对于较优的个体。这种方式将最短路径降低为170左右,虽然不是十分符合遗传算法的规律。

交叉中选用移除初始城市序号的列表。生成的子代再进一步选择:

子函数如下所示:

变异操作程序设计(伪代码或源代码截图)及说明解释

变异操作采用了十分简单的方法:随机选择两个城市序号(除初始城市)进行交换

子函数如下:输入为变异概率和变异种群,输出为已变异种群

四、实验结果展示

       展示程序界面设计、运行结果及相关分析等,主要包括:

可视化程序界面展示及各功能组件介绍:

首先输入起始城市的序号,然后输入途经城市序号,每一个序号中间用空格分离,如果输入为空(直接回车),则为所有城市

运行结果如下所示:

  1. 遗传算法收敛图(适应度值随迭代增加的变化趋势)

当途径所有城市时,遗传算法收敛图如下,其中横坐标为迭代轮数,纵坐标为本轮最高适应度,可以从图中看到,适应度随着迭代轮数逐渐升高并趋于平缓。

  1. 不同城市组合下的最优结果城市路径展示及分析
  1. 可以看到结果接近最短路线

可以看到结果是正确的最短路线

不同初始种群个数对结果的影响分析

初始种群个数越多,运行越慢,结果越能接近距离最小值。但是不能过大,过大会导致运行过慢,且在计算最小路径上比适当种群数目只有较小影响

不同交叉概率和变异概率对结果的影响分析

交叉的作用为保证种群的稳定性,朝着最优解的方向进化,因此交叉的概率应当适度大一些,如果交叉概率过低,可能得不到最优解

变异的作用为保证种群的多样性,避免交叉可能产生的局部收敛,变异概率应小一点,过大或过小可能会找不到最优解

test.py

import random
import math
import copy
import matplotlib.pyplot as plt


#初始化种群
def rand(n,passby): 
    seq = []
    seq.append(n)
    length = len(passby)
    i = 0
    while i<length:
        k = random.randint(0,length-1)
        try:
            seq.index(passby[k])
        except:
            seq.append(passby[k])
            i += 1
    seq.append(n)
    return seq

def culDistance(N,seq1,cities):
    distance = [0]*N

    for i in range(N): 
        for j in range(1,len(seq1[0])):

            city1 = seq1[i][j-1]
            city2 = seq1[i][j]
            distance[i] += (math.sqrt(pow(float(cities[city1][2])-float(cities[city2][2]),2)+pow(float(cities[city1][1])-float(cities[city2][1]),2)))
    return distance

#轮盘赌算法
def RWS(p): 
    m = 0
    r = random.random() #产生随机数[0,1)
    for i in range(len(p)):
        m += p[i]
        if r<=m:
            return i

#交换(用于选择运算)        
def change(seq,select):
    temp = []
    length = len(seq)
    for i in range(length):
        temp.append(seq[select[i]])
    for i in range(length):
        seq[i] = temp[i]
    return seq


#交叉繁殖
def crossover(parents):

    length = len(parents)
    #孩子列表
    children=[]
    while len(children)<2*length:
        male_index = random.randint(0, len(parents) - 1)
        female_index = random.randint(0, len(parents) - 1)

        if male_index!=female_index:
            male=parents[male_index]
            female=parents[female_index]
          
            left=random.randint(0,len(male)-2)
            right=random.randint(left+1,len(male)-1)

            #交叉片段
            gene1=male[left:right]
            gene2=female[left:right]

            child1_c=male[right:]+male[:right]
            child2_c=female[right:]+female[:right]

            child1=child1_c.copy()
            child2=child2_c.copy()
            
            for o in gene2:
                child1_c.remove(o)

            for o in gene1:
                child2_c.remove(o)

            child1[left:right]=gene2
            child2[left:right]=gene1

            child1[right:]=child1_c[0:len(child1)-right]
            child1[:left] = child1_c[len(child1) - right:]

            child2[right:] = child2_c[0:len(child1) - right]
            child2[:left] = child2_c[len(child1) - right:]
 
            children.append(child1)
            children.append(child2)

    return children


#变异
def variation(children,variation_p):
    length = len(children)
    lengthcity = len(children[0])
    for i in range(length):
        if random.random() < variation_p:
            m = random.randint(0,lengthcity-1)
            n = random.randint(0,lengthcity-1)
            while  m==n:
                m = random.randint(0,lengthcity-1)
                n = random.randint(0,lengthcity-1)
            children[i][n],children[i][m] = children[i][m],children[i][n]

 #读文件中的城市信息并存储到数组当中
def readfile():
    file = open("data.txt")
    context = file.readlines()
    cities = []
    length = len(context) #城市数目
    for i in range(length): 
        cities.append(context[i].rstrip().split(","))
        file.close()   
    return cities

def result(begin,passby): 
     
    #读文件
    cities = readfile()

    #初始化
    t = 0 #进化代数计数器
    T = 500#最大进化代数
    M = 50#种群规模
    cross_p = 0.8
    variation_p = 0.2
    parents = [] #种群个体
    fitness = [] #自适应度
    distance = []
    select_p = []#选择概率 
    plt.figure(figsize=(12, 10), dpi=80)
    X = []    
    Y = []
    
    #生成初始种群
    for i in range(M): 
        parents.append(rand(begin,passby))

    for t in range(T):
        
        # 计算总距离
        distance = []
        
        distance = culDistance(M,parents,cities) 
        distance.sort()
        
        #计算自适应度
        fitness = []
        for i in range(M):
            fitness.append(1/distance[i])
        #print("最高适应度为",max(fitness))
        X.append(t)
        Y.append(max(fitness))
        #计算选择概率
        select_p = []
        for i in range(M): 
            select_p.append(fitness[i]/sum(fitness))

        
        
        #进行轮盘赌
        select = []
        for i in range(M): 
            select.append(RWS(select_p))
            
        #交换个体
        parents = change(parents,select)
        
        #交叉
        for i in range(M):
            parents[i] = copy.deepcopy(parents[i][1:len(parents[i])-1])
        children = []
        p = random.random()
        if p <= cross_p:
            children = crossover(parents)
        else:
            children = copy.deepcopy(parents)
            children = children*2
#
        childDistance= []
        
        childDistance = culDistance(2*M,children,cities)

        for i in range(M):
            n = childDistance.index(max(childDistance))
            childDistance.pop(n)
            children.pop(n)
#            
        #变异
        variation(children,variation_p)
        #加上起点
        for i in range(M):
            children[i] = [begin]+children[i]+[begin]
  
        parents = copy.deepcopy(children)
        
        if t==T-1:
            distance = []
            plt.plot(X,Y,'-o')
            distance = culDistance(M,parents,cities)
            return distance,parents
   
#distance,seq = result(0,list(range(1,34)))
#print(seq)
#print(distance)
#city,d,seq = result(0)
#print(d)
#print(seq)


可视化界面:

# -*- coding: utf-8 -*-
import test
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号

cities = test.readfile()

for i in range(len(cities)):
    print(i,cities[i][0],end="")

begin = int(input("请输入起始城市:"))
passby = input(" 请输入途经城市:(以空格分离)")
if passby.rstrip()=="":
    passby = list(range(0,34))
    passby.remove(begin)
else:
    passby = passby.rstrip().split(" ")
    for i in range(len(passby)):
        passby[i] = int(passby[i])
print(passby)

distance,seq = test.result(begin,passby)

route = []
shortest = min(distance)
n = distance.index(shortest)
route = seq[n]

print("最短路线为:",route)
print("距离为:",shortest)

X = []    
Y = []
plt.figure(figsize=(12, 10), dpi=80)
for i in range(len(route)):
    X.append(float(cities[route[i]][1]))
    Y.append(float(cities[route[i]][2]))
    plt.text(X[i],Y[i],cities[route[i]][0])

plt.plot(X,Y,'-o')
 
    

总结:当遍历全部城市时,达不到最优解,只能靠近,最小试过的为170左右

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值