基于遗传算法解决TSP问题

本文介绍了如何使用遗传算法解决旅行商问题,详细阐述了遗传算法的基本步骤,包括编码、选择、交叉和变异,并提供了具体的Python实现。在解决TSP问题中,通过建立地图、创建初始种群、计算非适应度等步骤,逐步优化路径,寻找最短距离。最后,文章讨论了结束条件和选择策略,以及如何处理历史最优解。
摘要由CSDN通过智能技术生成

基于遗传算法解决TSP问题


TSP 遗传算法


前言

  • 这是第一次发布文章,本人菜鸟一个。发文章主要看心情。突然想写点啥,于是就来了。其实早就想写点啥了,一直有其他的事打扰(hai,就是懒),现在有空了,就想记录下一些东西。最近忙这个大作业,突然挺有感想的。感觉人不能被生活赶着走,你得知道自己需要什么,想要什么,让自己快乐才是最重要的,而且不要短暂的快乐。有时候学习让我感到难受不是因为学习本身,而是作业不会做,不会做的原因是上课没好好听,课后没去拓展学习。总之一切都是有原因的。多思考人生,才能更好的走下去

提示:以下是本篇文章正文内容,下面案例可供参考

一、旅行商问题(Traveling Salesman Problem,TSP)

旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。
假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。TSP问题就是要我们找到那条最短的路!

二、遗传算法

1.什么是遗传算法?

遗传算法(Genetic Algorithm,GA)最早是由美国的 John holland于20世纪70年代提出,该算法是根据大自然中生物体进化规律而设计提出的。

是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。

该算法通过数学的方式,利用计算机仿真运算,将问题的求解过程转换成类似生物进化中的染色体基因的交叉、变异等过程。在求解较为复杂的组合优化问题时,相对一些常规的优化算法,通常能够较快地获得较好的优化结果。遗传算法已被人们广泛地应用于组合优化、机器学习、信号处理、自适应控制和人工生命等领域。

总结一下,遗传算法最重要的思想就是 并行
设想一下,一个人把所有城市不重复走一遍,假设要花一年时间,一个人活100岁,他一辈子才走完100趟(类似暴力解法);如果他派100个人出去,每年这100个人还能认识新的朋友以及每个人自己的子嗣加入到行列中,那就像是核裂变,他们经历城市的方式是不计其数的。

2.遗传算法基本步骤

(1) 首先对问题进行编码,产生初始种群。
(2)然后对个体进行交叉、变异等遗传操作,产生出新的个体。
(3) 再按照优胜劣汰的原则对个体进行选择。
(4)如此往复,逐代演化产生出越来越好的个体。

3.编码与解码

将问题的解变换为位串形式表示的过程叫编码;
(1)将位串形式表示变换为原问题的解的过程叫解码。
(2)把位串形式编码叫染色体(chromosome)或个体(individual),如:0100111010100011
(3) 染色体的每位称为基因(gene)
有多种编码方式:
二进制编码
实数编码
整数编码
排列编码
有限状态机编码
树编码
怎么说呢,各有各的好处吧,具体问题具体分析。

4.选择

(1)适应度
适应度用适应度函数进行标度,比如一个个体x染色体为 01010 ,我们用函数:
F = G(x) 例如 F = x^2
来刻度它,先把它化成十进制数 x = 10 ,则x的适应度为
F = 10^2 =100
当然适应度函数也是根据问题需求来的。它值的大小的意义都可以由你来标度。
(2)选择
选择按方式来分分为:
轮盘赌选择
两两竞争法选择
锦标赛选择
精英保留

5.交叉

单点交叉
两点交叉
多点交叉
部分匹配交叉
顺序交叉

6.变异

如是二进制编码,先取定变异概率Pm(一般较小,pm ≤0.05),对交叉后代集中的每个后代
x=1010101… (举个例子)
的每位基因生成一个随机数r∈[0,1],若r ≤pm则将此基因g的改为1-g,否则*保持不变,*g代表0或1。
即:若r ≤pm则将数字0变为1,1变为0;否则保持不变。例如:O中的第3位和第7位需要改变:
O =1010101010
O =1000100010

对于整数编码常用有两种变异
(1)对换变异:随机地在染色体上选取两个位
置,交换两位置的基因。
4 3 1 2 5 6 7 —》 4 5 1 2 3 6 7
(2) 移位变异:任选一位移到最前
4 3 1 2 5 6 7 —》 5 4 3 1 2 6 7

三、基于遗传算法解决TSP问题

我们首先来思考这个问题:
假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。那他可以有多少种出行规划?

如果旅行商始终居住在同一个城市的话,答案是 (n-1)! 种。因为旅行商每次出行的起点和终点是一样的,如果用全排列来解决,只需要对其他 n-1 个城市排列就行了,故为 (n-1)! 种。
但是如果这个旅行商特别有钱,每到一个城市就卖一套房,下次出行的起点就有 n 个,故为 n! 种。

有些人可能会问,这个TSP和遗传有啥关系?难道旅行商嫌自己走的路太远了,就像愚公一样让自己的后代一代一代的去寻找那条最短的路?
没错,就是这样。
旅行商家族每次派N个人按照规划好的路线出去卖东西,在路上统计一路走过的距离,最后回来汇总。每个人在路上都有可能会遇到各种各样的事情,比如,他可能会被告知按另一种方式会走更短的距离(到底可不可信,谁又知道呢),于是他回来汇报给家族;又或者他决定下次要和谁交换一半的行程等等等等,都把信息和要求都向家族汇报。家族统计所有的信息,宁可信其有,不可信其无,于是又分派下一批人出发。如此往复。。。
最终白马王子与白雪公主…额,不对,最终旅行商祖师爷穿越到几亿年后发现还是没找到那条路。。。
好了,不扯了,下面说解决问题的思路:

1.建立地图

既然要出行,那必须得有地图啊。
默认为30个城市。反正画好所有的地图旅行商不一定全走到所有城市。可以多,不能少。

def mapCreat(cities=30):
    weighMap = {}           
    for i in range(1, cities):
        for j in range(2, cities+1):
            if i < j:  # 减少内存压力
                # st = str(i) + '-' + str(j)
                st = f'{i}-{j}'    #更快
                weighMap.update({st: random.randint(1, 10)})
    return weighMap

我们都知道,两个城市之间去程和回程是一致的。这样可以减少内存压力,因为到时候地图之类的都会在内存。
还有一件事!!如果你想多次在一个地图上运行,生成一次地图之后,就给它存起来,然后每次都在这个地图上跑TSP问题。要不然每次结果都不一样的。

2.创建初始种群

这里的城市数量包括出发城市,我认识的旅行商住在城市1,故我让他每次从城市1出发,最后回到城市1。故城市1,我们可以不写入染色体x中,需要计算非适应度的时候再加进去。
编码采取整数编码,一个数代表一个城市。存储在list里。

def creatPopul(geneNum, firstNum):       # 1 建立初始种群 genNum包括城市1  默认初始种群个体数量
    firstPopul = []
    times = firstNum                     # 生成指定个数的初始种群
    while times > 0:
        temp = creatRandomList(geneNum)  # 获得一个x'
        if temp in firstPopul:           # 如果之前生成的已经在里边了,重新生成一次
            continue
        firstPopul.append(temp)
        times -= 1

    return firstPopul  					 # [firstNum*[x']] x'=[2,3,4,...,n] 


def creatRandomList(geneNum):         	 # 生成一个随机个体 x',x'里面没有1
    xList = []
    for i in range(1, geneNum):
        rond = random.randint(0, i-1) 	 # 生成值的为随机插入的位置
        xList.insert(rond, i+1)
    return xList

3.计算非适应度

非适应度 = 距离
非适应度最小 = 距离最小 = 适应度最大
留下适应度最大的个体就是我们需要的TSP距离最小的个体

定义非适应度函数:

def getFitness(perTree, indiv):           # 非适应度函数定义为总路径距离
    fitness = 0
    populTemp = copy.deepcopy(indiv)	  # 牺牲空间,不改变原来的值
    populTemp.insert(0, 1)                # 形成完整的染色体 x = [1] + x' + [1]
    populTemp.append(1)
    for j in range(len(populTemp)-1):
        fitness += getDist(populTemp[j], populTemp[j+1], perTree)
    return fitness

def getDist(cityOne, cityTwo, weighMap):  		  # 从权值地图获取两个城市间的距离
    if cityOne > cityTwo:
        cityOne, cityTwo = cityTwo, cityOne   # 保证城市标号小的在前面
    # s = str(cityOne) + '-' + str(cityTwo)
    s = f'{cityOne}-{cityTwo}'                # 更快
    return weighMap[s]  							  # 从权值地图中找到对应路径的长度

4.结束标志

两个:
(1)跑完了指定的代数
(2)连续很多代都是同一个解,可以根据指定代数来取,较大较好。

if len(theSolution) != 0:
            genery += 1       #当前的代数
            popuxMin = min(popux)   #当代最优解
            if genery >= generationMax:     #如果达到一定的代数,退出程序
                if theSolution[0] > popuxMin[0]:
                    theSolution = popuxMin  #如果当代最优解没有历史最优好,输出历史最优
                    errGe = 0       #又是白跑的一天
                theS = copy.deepcopy(theSolution)
                print(f'城市数量cities = {geneNum}')
                print(f'最短距离sumDistMin = {theS[0]}')
                theS.pop(0)
                theS.insert(0, 1)
                theS.append(1)
                print(f'最短路径minDistWay = {theS}')
                print(f'当前遗传代数已达到{generationMax}代,Done!')
                print(f'已经连续{errGe}代为同一最优解')
                return theSolution  # 返回最优解
            if theSolution[0] == popuxMin[0]:  # 连续同解
                errGe += 1          #累计连续同解
                if errGe >= errorGene:  # 相同最优解遗传代数大于给定值,则认为是全局最优解
                    theS = copy.deepcopy(theSolution)
                    print(f'城市数量cities = {geneNum}')
                    print(f'最短距离sumDistMin = {theS[0]}')
                    theS.pop(0)
                    theS.insert(0, 1)
                    theS.append(1)
                    print(f'最短路径minDistWay = {theS}')
                    print(f'已经连续{errGe}代为同一最优解,Done!')
                    return theSolution  # 返回最优解
            if theSolution[0] > popuxMin[0]:
                theSolution = min(popux)
                errGe = 0
        else:      #第一次
            theSolution = min(popux)       # 存储最优解

5.选择父代——轮盘赌

给种群全体个体建立轮盘
产生随机数,确定被选择的个体集合Pa
此时个体内染色体为x’

def rouletSelect(popux, populNum, oPopul):
    roulette = []  				# 轮盘
    sumFitness = 0
    
    for i in popux:  			# 获取总体的适应度值
        sumFitness += i[0]

    for i in popux:  			# 建立轮盘
        roulette.append(float(i[0])/float(sumFitness))
    for i in range(1, len(roulette)):
        roulette[i] += roulette[i-1]
        
    parentsSelected = []  		# 被选择的个体
    probal = 0.0           		# 存储概率
    j = 0
    fg = 0
    copaNum = populNum  		# 选择populNum/2对个体 进行遗传
    if copaNum % 2 != 0:
        copaNum += 1      		# 一定选择偶数个配对,2个一组
    while(copaNum > 0):   		# 每次选择一对
        probal = random.random()
        for j in range(len(roulette)):
            if probal < roulette[j]:
                break
        if copaNum % 2 != 0 and j == fg:
            continue  			# 同组相同重新获取
        parentsSelected.append(oPopul[j])
        copaNum -= 1
        fg = j
    return parentsSelected

6.交叉——顺序交叉

对于Pa中的每一组个体,获取一个随机数(0,1),当随机数小于给定的交叉概率Pc时进行顺序交叉。
一组两个个体,而且是连续的两个

解释一下为什么是小于,而不是大于:
比如我们给定交叉概率为Pc = 0.6,我们令产生随机数的大小区间为(0,1)。产生的随机数在(0.0.6)这个区间的概率是0.6,也即小于Pc的概率是0.6;而随机数在(0.6,1)的概率为0.4,也即大于Pc的概率是0.4。
故我们在随机数小于Pc时进行顺序交叉。Pc不宜太大,也不能过小,0.4~0.6左右即可。过小不容易收敛,过大收敛太快,可能演化为随机算法。

def overlap(pIndiv1, pIndiv2):
    lenGene = len(pIndiv1)
    # 生成两个切片点
    ovlapIndex = set()  
    while(len(ovlapIndex) < 2):
        ovlapIndex.add(random.randint(1, lenGene-1))  # 集合去重
    ovlapIndex = list(ovlapIndex)
    ovlapIndex.sort()  # 由小到大排序
    
    minIndex = int(ovlapIndex[0])
    maxIndex = int(ovlapIndex[1])
    exchangeOne = []
    exchangeTwo = []
    for i in range(lenGene):
        if minIndex <= i and i < maxIndex:
            exchangeOne.append(pIndiv1[i])  # 个体1 交换出去 的基因,即中间部分
            exchangeTwo.append(pIndiv2[i])  # 个体2 交换出去 的基因

    temp = 0
    for idx in range(lenGene - ovlapIndex[1]):  # 按第二个切点排序的新序列
        temp = pIndiv1.pop()
        pIndiv1.insert(0, temp)
        temp = pIndiv2.pop()
        pIndiv2.insert(0, temp)

    tm = []        		#剩下不是交换进来的基因
    for i in pIndiv1:
        if i not in exchangeTwo:
            tm.append(i)
    pIndiv1 = tm

    tm2 = []
    for i in pIndiv2:
        if i not in exchangeOne:
            tm2.append(i)
    pIndiv2 = tm2

    temp = len(exchangeTwo)      #在第一个切片点回填交换进来的基因
    while(temp > 0):
        pIndiv1.insert(minIndex, exchangeTwo[temp-1])
        temp -= 1

    temp = len(exchangeOne)
    while(temp > 0):
        pIndiv2.insert(minIndex, exchangeOne[temp-1])
        temp -= 1

    return pIndiv1, pIndiv2

交叉结束后将新产生的个体做个备份,后续有用:

bornKidsSetOneBackups = copy.deepcopy(bornKidsSetOne)  # 备份

7.变异——移位变异

对于每个交叉产生的新个体,获取一个随机数(0,1),当随机数小于给定的变异概率Pm时对其进行移位变异。
变异概率一般取得很低。不能超过0.5,超过就变成随机算法了。

def shiftVaria(bornKidsIndiv):                 # 移位变异
    lenGene = len(bornKidsIndiv)               # 获取基因个数
    variIndex = random.randint(0, lenGene-1)   # 生成要变换的位置
    geneTemp = bornKidsIndiv.pop(variIndex)    # 拿出选中的基因
    bornKidsIndiv.insert(0, geneTemp)          # 放到首部
    return bornKidsIndiv

8.选择子代——精英保留选择

选择子代跟选择父代差不多,都需要计算非适应度。
在总体上进行筛选,选择N个个体留到下一代。N与最初种群数相等。意义是资源有限,存活数不会增加。
总体 = 本代的初始种群 U交叉产生的新个体U变异产生的新个体
U 是并
‘x = [fitness] + x
(1)首先对总体去重 (有没有一种可能,我是说可能,虽然可能性很小,去重后总体个体数小于N,得考虑一下)
(2)由不适应度(距离)从小到大排序
(3)把前E个个体直接保留到下一代。E也不宜太大,取一半。
(4)选择其余N-E个普通个体

        # 6 选择子代 物竞天择 精英保留选择,更快到达局部最优或全局最优
        
        oPopul.extend(bornKidsSetOneBackups)  # 加入遗传产生的子代个体
        oPopul.extend(bornKidsSetTwo)  # 加入变异产生的子代个体
        popux = copy.deepcopy(oPopul)  # 复制
        
        fitTemp = 0
        for i in popux:  # 计算所有个体的非适应度
            fitTemp = getFitness(perf_tree, i)
            i.insert(0, fitTemp)  # 把每个非适应度放到’x的第一位

        popux = derepeat(popux)   # 去重
        lenPopux = len(popux)
        popux.sort()  # 根据非适应度fitness从小到大排序,非适应度就是距离
        # 将前 eliteNum 个精英保留,其他的随机保留 到下一代
        oPopul = []         #下一代个体集
        for i in range(eliteNum):  # 先把前 eliteNum 个精英留到下一代
            popux[i].pop(0)
            oPopul.append(popux[i])

        lenPaSet = set()
        while(len(lenPaSet) < populNum-eliteNum):  # 选择 N-E个普通个体
            fg = random.randint(eliteNum, lenPopux-1)
            lenPaSet.add(fg)
        lenPaSet = list(lenPaSet)
        for i in lenPaSet:
            # print("111")  #调试
            popux[i].pop(0)
            oPopul.append(popux[i])

9.历史最优解

按理来说,到上面第8步就应该结束了,可是我按照遗传算法运行了很多次之后,旅行商家族都不知道繁衍多少带了,可能城市1都比上海还大了,可能地球都爆炸了,还是会有不一样的结果出现。

比如第一次,20个城市,找了3000代,最后出来结果是:
[20, 26, 11, 7, 16, 15, 10, 6, 5, 3, 13, 20, 2, 17, 12, 8, 4, 18, 19, 9, 14]
第一个数20是代表城市个数,第二个数26是最短距离,后面的是从城市1出发后依次经过的城市,也即本次旅行商得到的最短路径是:
[1,11, 7, 16, 15, 10, 6, 5, 3, 13, 20, 2, 17, 12, 8, 4, 18, 19, 9, 14,1]

然后第二次,我觉得3000代有点少,于是来了个20000代,结果是:
[20,25, 3, 13, 20, 2, 17, 11, 7, 15, 16, 4, 8, 12, 10, 6, 5, 18, 19, 9, 14,]
果然能找到更短的,这次是25,那么我就想,额,是不是会还有更短的,24?23?于是就打算把每次运行的结果都存起来,当然,如果这一次的结果没有比上一次的更短的距离的就没必要存了,我不关心有没有其他的路径是等距离的,我只要最短。

def initOptimal(fileName, citiesMin=10, citiesMax=30):
    theSolution = []
    with open(fileName, 'r') as f:
        for i in range(citiesMin, citiesMax+1):
            linee = f.readline()
            linee = linee.strip('[')    # 去掉首尾的[] \n
            linee = linee.strip('\n')
            linee = linee.strip(']')
            splLine = linee.split(',')  # 长度为 i+1
            splLine.pop(0)              # 去掉城市数
            for j in range(len(splLine)):
                splLine[j] = int(splLine[j])
            theSolution.append(splLine)
    f.close()
    return theSolution
   
# 将历史运算最优解存储起来,自动更新,存储历史最优到文件
cities = citiesMin     #10
citiesMax = 30
tim = 5000       #代数,可以修改
theSolution = initOptimal(fileStr, citiesMin, citiesMax)     # 读取历史最优解

k = 0  # 如果没有更优解,不需要更新文件
while(cities <= citiesMax):
   solu = tspGenetic(theSolution[cities-citiesMin], cities, 40, perf_tree, 0.6, 0.01,
                     tim, 0.00000000001, 1000, 20)

   # 是否需要更新最优解
   if solu < theSolution[cities-citiesMin]:  # 保存新的更优解
       theSolution[cities-citiesMin] = solu
       k += 1
   print()
   cities += 1

# 统一写回文件
if k != 0:
   cities = citiesMin
   with open(fileStr, 'w+') as f:
       for i in theSolution:
           i.insert(0, cities)
           f.write(str(i))
           f.write('\n')
           cities += 1
   f.close()
else:
   print("\n*************本次遗传没有更新!!**************\n")

这样的话,只要有足够的时间,多运行几次,结果就会与全局最优越来越接近。
下面贴一些运行事例:
20000代结果
上面这个是20000代跑了10次的,后来 我又改成了纯精英保留选择,并且感觉每次都是闯最优解,不如多来几次,而不是在一条路上走很远,于是改成10次2000代替20000次的,发现并没啥用,出不了新结果,然后改成3000*10,果然又出了新解:
3000*10结果
我们都听过,“悟已往之不谏,知来者之可追”,就是说,如果走错了路,应该立即回头还来得及。放到我们这呢,就是说可能我们被锁死在一个局部最优解,此时我们应该立即结束,回到原点,重新出发,才有可能更快找到下一个更好的解。

四、总结

遗传算法整体代码

def tspGenetic(solution, geneNum, populNum, perf_tree, ovlapProb, variProb, generationMax, error, errorGene, eliteNum):
    # solution只会与最后的结果比较不会变
    # 1 建立初始种群 P(0) t=0 表示代数 每个个体x = [1,2,3,4,5,6,7,8,9,10,...,1]是一个排列,首尾一定是1,可省略
    oPopul = creatPopul(geneNum, populNum)

    theSolution = []
    genery = 0
    errGe = 0

    while True:  # 开始遗传算法
        # 2 定义适应度函数  计算适应度 fitness  = getFiness(x) x代表个体染色体
        popux = copy.deepcopy(oPopul)  # 复制
        fitTemp = 0
        for i in popux:
            fitTemp = getFitness(perf_tree, i)
            i.insert(0, fitTemp)  # 把每个适应度放到x的第一位

        # popux.sort(reverse=True)   #降序排列,为后续《精英选择》做准备

        # 定义遗传结束标志 2种:
        if len(theSolution) != 0:
            genery += 1  # 代数
            popuxMin = min(popux)  # 当代最优解
            if genery >= generationMax:  # 达到一定的代数,程序结束
                if theSolution[0] > popuxMin[0]:
                    theSolution = popuxMin
                    errGe = 0  # 又是没用的一次循环
                theS = copy.deepcopy(theSolution)
                print(f'城市数量cities = {geneNum}')
                print(f'最短距离sumDistMin = {theS[0]}')
                theS.pop(0)
                theS.insert(0, 1)
                theS.append(1)
                print(f'最短路径minDistWay = {theS}')
                print(f'当前遗传代数已达到{generationMax}代,Done!')
                print(f'已经连续{errGe}代为同一最优解')
                return theSolution  # 返回最优解
            if theSolution[0] == popuxMin[0]:  # 连续同解
                errGe += 1  # 累计连续同解
                if errGe >= errorGene:  # 相同最优解遗传代数大于给定值,则认为是全局最优解
                    theS = copy.deepcopy(theSolution)
                    print(f'城市数量cities = {geneNum}')
                    print(f'最短距离sumDistMin = {theS[0]}')
                    theS.pop(0)
                    theS.insert(0, 1)
                    theS.append(1)
                    print(f'最短路径minDistWay = {theS}')
                    print(f'已经连续{errGe}代为同一最优解,Done!')
                    return theSolution  # 返回最优解
            if theSolution[0] > popuxMin[0]:
                theSolution = min(popux)
                errGe = 0
        else:
            theSolution = min(popux)       # 存储最优解
        if theSolution[0] > solution[0]:   # 取等号增加/减少多样性
            theSolution = solution         # 跟上一代最优的相比,留下历史最优
        # 3 选择父代 (1)轮盘赌选择
        # 1.1创建轮盘
        roulette = []  # 轮盘
        sumFitness = 0
        for i in popux:  # 获取总体的适应度值
            sumFitness += i[0]

        for i in popux:  # 建立轮盘
            roulette.append(float(i[0])/float(sumFitness))

        for i in range(1, len(roulette)):
            roulette[i] += roulette[i-1]

        # 1.2进行选择
        parentsSelected = []  # 被选择的个体
        probal = 0.0           # 存储概率
        j = 0
        fg = 0
        copaNum = populNum  # 选择N/2对个体 进行遗传
        if copaNum % 2 != 0:
            copaNum += 1      # 一定选择偶数个配对,2个一组
        while(copaNum > 0):   # 每次选择一对
            probal = random.random()
            for j in range(len(roulette)):
                if probal < roulette[j]:
                    break
            if copaNum % 2 != 0 and j == fg:
                continue  # 同组相同重新获取
            parentsSelected.append(oPopul[j])
            copaNum -= 1
            fg = j
        # parentsSelected = rouletSelect(popux, populNum, oPopul)
        # # (2)锦标赛选择

        # 4 遗传:顺序交叉  ovlapProb交叉概率 随机选点,顺序交叉
        bornKidsSetOne = []
        lenPaSet = len(parentsSelected)
        while(lenPaSet > 0):
            ifOverlap = random.random()
            if ifOverlap <= ovlapProb:  # 进行顺序交叉
                bornKidsSetOne.extend(
                    overlap(parentsSelected[lenPaSet-1], parentsSelected[lenPaSet-2]))
            lenPaSet -= 2

        bornKidsSetOneBackups = copy.deepcopy(bornKidsSetOne)  # 备份

        # 5 变异:对换变异  移位变异 Pm变异概率

        bornKidsSetTwo = []
        lenKidsSetOne = len(bornKidsSetOne)
        for i in range(lenKidsSetOne):
            ifVari = random.random()
            if ifVari <= variProb:  # 进行移位变异
                bornKidsSetTwo.append(shiftVaria(bornKidsSetOne[i]))

        # 6 选择子代 物竞天择 精英保留选择,更快到达局部最优或全局最优
        # 这一代的初始种群P(geM) Popul
        oPopul.extend(bornKidsSetOneBackups)  # 加入遗传产生的子代个体
        oPopul.extend(bornKidsSetTwo)  # 加入变异产生的子代个体

        popux = copy.deepcopy(oPopul)  # 复制
        fitTemp = 0

        for i in popux:  # 计算所有个体的适应度
            fitTemp = getFitness(perf_tree, i)
            i.insert(0, fitTemp)  # 把每个适应度放到x的第一位

        popux = derepeat(popux)   # 去重
        # 考虑去重之后的数量可能很少,比populNum还少
        if len(popux) < populNum:
            popux.extend(popux)  # 让其翻倍
            print("1111")
        lenPopux = len(popux)
        popux.sort()  # 根据适应度fitness从小到大排序,适应度就是距离
        # 将前 eliteNum 个精英保留,其他的随机保留 到下一代
        oPopul = []
        for i in range(eliteNum):  # 先把前 eliteNum 个精英留到下一代
            popux[i].pop(0)
            oPopul.append(popux[i])

        lenPaSet = set()
        while(len(lenPaSet) < populNum-eliteNum):  # 选择 N-E个普通个体
            fg = random.randint(eliteNum, lenPopux-1)
            lenPaSet.add(fg)
        lenPaSet = list(lenPaSet)
        for i in lenPaSet:
            # print("111")
            popux[i].pop(0)
            oPopul.append(popux[i])

    return

 #  geneNum = cityNum | gene = city | x = 染色体 = n*gene

漏洞肯定有很多,我已经改了两天了,不过最后的结果我还是很满意的。希望下一次也不太远。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Joeyangz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值