自适应大领域搜索算法

5 篇文章 0 订阅
3 篇文章 0 订阅

1 前言

有关TSP问题的求解方法层出不穷,遗传算法、模拟退火、粒子群算法之类的介绍已经比较多了,但是发现关于自适应大领域搜索算法(Adaptive Large Neighborhood Search, ALNS)的介绍比较少,而且示例代码注释不太详细,刚接触的时候学起来有点费劲,就把学习资料和自己理解的过程简单写一下,造福和我一样的菜鸟吧。

2 什么是自适应大领域搜索

概念理解干货 | 自适应大邻域搜索(Adaptive Large Neighborhood Search)入门到精通超详细解析-概念篇

优点、步骤和python示例代码自适应大邻域搜索算法

参考文献及算法应用
[1]王新. 车辆和无人机联合配送路径问题研究[D].大连海事大学,2020.
[2]李婷玉. 多商户多车程同城物流配送车辆调度问题研究[D].大连理工大学,2018.
[3]张梦颖. 不确定因素下路径规划问题研究[D].中国科学技术大学,2016.

简而言之,自适应大领域搜索的核心思想是:破坏解修复解动态调整权重并选择(自适应)。通过设计多组破坏算子和修复算子,扩大解空间搜索范围,对当前解进行改进,表现好的破坏和修复方法相应地得到高分,权重也越高。在每次迭代中,根据以往表现对各个破坏和修复算子进行选择和权重调整,使用高效的组合方法,提高算法寻优能力,从而找到最优解。

2.1 破坏算子(destroy)

破坏解的方法主要有随机移除、最差移除、相似移除等。其中,随机移除删除当前解决方案中的任意节点;最差移除删除当前解决方案中距离较长的路段。

先破坏再修复,这一步得到破坏后的解和移除的节点列表。如,当前解(TSP城市访问顺序)为【1, 4, 2, 3】,随机删除节点4后,得到破坏后的解【1, 2, 3】

2.2 修复算子(repair)

修复解的方法主要有随机插入、贪婪插入等。其中,随机插入将移除的节点逐个插入到破坏后解的任意位置;贪婪插入将移除的节点插到距离成本最小,即插入后总路径最短的位置中。

修复操作的对象是破坏后的解和移除的节点列表,将移除的节点重新插入到破坏后的解中,得到完整的、新的一组解。如,将2.1中的移除节点4,随机插入到破坏后的解【1, 2, 3】中,可得【1, 2, 3, 4】

需要选择至少两组破坏和修复算子加入算法,更多的算子介绍详见上面的参考文献,也可以根据问题特性自行设计其他的破坏或修复算子。

2.3 动态调整权重并选择(自适应过程)

算法在迭代时根据算子权重和轮盘赌的方式选择、调整破坏算子和修复算子。

(1)权重更新

算子的权重按这个公式进行更新:在这里插入图片描述
式中,Wd为算子权重,Sd为算子分数,Ud为算子的使用次数,ρ为权重更新系数(控制权重变化的速度)。其实就是:算子新权重 = 算子旧权重 × (1 - 系数) + 系数 × (累加分数 ÷ 累加次数),将算子权重与以往表现挂钩。

在开始时,所有算子均具有相同的权重和分值。而分数,则是在每个迭代过程中,根据算子的不同表现情况阶梯式给分,得分越高表明算子表现越好。可设定以下4种加分情况:

(1)破坏/修复后得到新的全局最优解,+1.5分
(2)破坏/修复后没有得到全局最优解:
  ①尚未接受过的但比当前解好,+1.2分
  ②尚未接受过的且比当前解差:
   a)在一定标准下接受劣解,+0.8分
   b)不满足接受准则的劣解,+0.6分

阶梯分数自己定,1.5、1.2为示例。搜索过程中,若只接受优解容易陷入局部最优,为了避免这种情况应采用一定准则接受劣解。自适应大领域搜索中通常采用模拟退火算法Metropolis准则,在一定概率下接受劣解。

模拟退火算法的介绍深度学习 — 模拟退火算法详解(Simulated Annealing, SA)

(2)轮盘赌选择算子

得到新的权重后,算法基于轮盘赌的思想对算子进行选择,使算子被选中的概率与其权重表现成正比。

轮盘赌介绍Evolutionary Computing: 遗传算法_轮盘赌选择(转载)

3 python代码示例及详解

了解了上面的内容之后,下面这个示例代码就很容易理解了。

优点、步骤和python示例代码自适应大邻域搜索算法

原来的程序把两个轮盘赌函数放在了前面。为了方便理解,我把函数定义的顺序换了一下,由简到易,先分块注释:

(1)导入库,输入城市两两距离矩阵

import numpy as np
import random as rd
import copy

distmat = np.array([[0,350,290,670,600,500,660,440,720,410,480,970],
[350,0,340,360,280,375,555,490,785,760,700,1100],
[290,340,0,580,410,630,795,680,1030,695,780,1300],
[670,360,580,0,260,380,610,805,870,1100,1000,1100],
[600,280,410,260,0,610,780,735,1030,1000,960,1300],
[500,375,630,380,610,0,160,645,500,950,815,950],
[660,555,795,610,780,160,0,495,345,820,680,830],
[440,490,680,805,735,645,495,0,350,435,300,625],
[720,785,1030,870,1030,500,345,350,0,475,320,485],
[410,760,695,1100,1000,950,820,435,475,0,265,745],
[480,700,780,1000,960,815,680,300,320,265,0,585],
[970,1100,1300,1100,1300,950,830,625,485,745,585,0]])

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

(2)定义目标函数计算距离 —— disCal( )

已知访问顺序path,求经过的路径长度distance。

def disCal(path):    
    distance = 0          #若路径为path = 【1,2, 3】,len(path)=3
    for i in range(len(path) - 1):         #先循环两遍,求1-2和2-3的距离之和
        distance += distmat[path[i]][path[i + 1]]
    distance += distmat[path[-1]][path[0]]   #从起点出发,回到原来出发的城市,求首尾相连的距离,即3-1
    return distance   

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

(3)定义第一个摧毁算子 —— 随机移除randomDestroy( )

随机移除3个城市。输入当前解sol,输出需要移除的城市列表removed。

def randomDestroy(sol):
    solNew = copy.deepcopy(sol)
    removed = []
    removedIndex = rd.sample(range(0, distmat.shape[0]), 3)   #sample随机选取3个城市序号并存入列表,distmat.shape[0]得到距离矩阵维度,即城市总个数
    for i in removedIndex:   
        removed.append(solNew[i])    #将需要移除的城市添加到removed列表
        sol.remove(solNew[i])        #移除后剩下的城市列表
    return removed

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

(4)定义第二个摧毁算子 —— 最大3段距离移除max3Destroy( )

移除最长的3段距离。输入当前解sol,输出需要移除的城市列表removed。内部过程比随机移除复杂。

def max3Destroy(sol):
    solNew = copy.deepcopy(sol)
    removed = []
    dis = []
    for i in range(len(sol) - 1):
        dis.append(distmat[sol[i]][sol[i + 1]])      #在距离矩阵中选取路段的长度,如【1,2,3】,循环两次求1-2,2-3的距离,
    dis.append(distmat[sol[-1]][sol[0]])      #选取首尾的距离3-1,放入dis列表
    disSort = copy.deepcopy(dis)
    disSort.sort()     #sort对disSort进行排序,默认升序
    for i in range(3):     #判断最长的3个路段并移除
        if dis.index(disSort[len(disSort) - i -1]) == len(dis) - 1:
            #如果是距离列表的最后一个,就是城市首尾相连的距离,则去掉列表第一个城市
            removed.append(solNew[0])
            sol.remove(solNew[0])
        else:
            removed.append(solNew[dis.index(disSort[len(disSort) - i - 1]) + 1])
            '''
            len(disSort)-i-1得到排在最后面即距离最长的路段。len-1,len-2,...
            disSort[]得到最大值,dis.index得到最大值在距离列表中的索引
            solNew得到最大值路段所在起点的索引,+1删除路段终点
            如solNew = 【1,3,2】,dis = 【d13,d32,d21】=【9,7,8】
            disSort = 【7,8,9】,先移除最后面的9,再移除8,...
            9对应的dis索引为0,solnew对应索引0上的城市为1,移除1-3段,去掉3
            '''
            sol.remove(solNew[dis.index(disSort[len(disSort) - i - 1]) + 1])   #更新移除后的城市列表
    return removed

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

(5)定义第一个修复算子 —— 随机插入randomInsert( )

随机插入已经移除的3个城市。输入移除后的城市访问列表sol和移除列表removed,执行插入操作后访问列表sol变化。

def randomInsert(sol, removeList):    
    insertIndex = rd.sample(range(0, distmat.shape[0]), 3)    #随机生成插入的位置索引
    for i in range(len(insertIndex)):
        sol.insert(insertIndex[i], removeList[i])    #将移除列表中的元素逐个插入指定位置

 
 
  • 1
  • 2
  • 3
  • 4

(6)定义第二个修复算子 —— 贪婪插入greedyInsert( )

插入产生的距离成本最小的城市。输入移除后的城市访问列表sol和移除列表removed,执行插入操作后访问列表sol变化。

def greedyInsert(sol, removeList):   
    dis = float('inf')  #初始化距离
    insertIndex = -1   #初始化插入索引
    for i in range(len(removeList)):   #移除列表里有几个城市就循环几次
        for j in range(len(sol) + 1):   #对于可行解中的每一个索引位置
            solNew = copy.deepcopy(sol)
            solNew.insert(j, removeList[i])   #将移除列表中的元素插入新解列表索引j的位置中,生成新的城市访问顺序
            if disCal(solNew) < dis:   #插入后得到新解的距离是否<原解距离。solNew和当前解sol不一样,solNew只是作为中间过程插入哪里的判断条件
                dis = disCal(solNew)   #更新距离,将原解换成新解
                insertIndex = j    #确定移除列表中每一个城市插入的索引
        sol.insert(insertIndex, removeList[i])    #完成每一个城市的插入操作后,更新当前解
        dis = float('inf')   #完成每一个城市的插入操作后距离重新初始化

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

(7)定义破坏算子选择轮盘赌 —— selectAndUseDestroyOperator( )

输入摧毁算子权重destroyWeight和当前解currentSolution(城市访问顺序), 输出破坏解后的城市列表sol、移除城市列表removedCities、和选择的摧毁算子序号destroyOperator。

里面需要调用过程(3)和(4)中的2个摧毁算子函数randomDestroy( )、max3Destroy( )。

def selectAndUseDestroyOperator(destroyWeight, currentSolution):   
    destroyOperator = -1    #算子初始值,除0/1外的数
    sol = copy.deepcopy(currentSolution)     #深拷贝,currentSolution之后的改变不影响sol
    destroyRoulette = np.array(destroyWeight).cumsum()  #轮盘赌,cumsum()把列表里之前数的和加到当前列,如a=[1,2,3,4],comsum结果为[1,3,6,10]
    r = rd.uniform(0, max(destroyRoulette))    #随机生成【0,轮盘赌列表最大数】之间的浮点数
    for i in range(len(destroyWeight)):  #如wDestroyed = 【1,1】,destroyedRoulette = 【1,2】,判断生成的r在哪个范围内,【0,1】选序列为0的算子,【1,2】选序列为1的算子
        if destroyRoulette[i] >= r:   #判断是否在某个算子的对应范围内
            if i == 0:    #在序列为0的算子范围内,选择随机移除
                destroyOperator = i   
                removedCities = randomDestroy(sol)    #得到随机移除的城市列表
                destroyUseTimes[i] += 1     #随机移除算子使用次数累加
                break          #满足其中一个范围就跳出for循环
            elif i == 1:   #在序列为0的算子范围内,选择最大3段距离移除
                destroyOperator = i    #与上面类似
                removedCities = max3Destroy(sol)
                destroyUseTimes[i] += 1
                break
    return sol, removedCities, destroyOperator  

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

(8)定义修复算子选择轮盘赌 —— selectAndUseRepairOperator( )

代码与(7)类似,不再赘述,只是输入变成修复算子权重repairWeight、摧毁后的城市列表destroyedSolution、移除列表removeList,输出变成修复后解destroyedSolution、修复算子选择序号repairOperator。

里面需要调用过程(5)和(6)中的2个修复算子函数randomInsert( )和greedyInsert( )

def selectAndUseRepairOperator(repairWeight,destroyedSolution,removeList):    
    repairOperator = -1
    repairRoulette = np.array(repairWeight).cumsum()
    r = rd.uniform(0, max(repairRoulette))
    for i in range(len(repairRoulette)):
        if repairRoulette[i] >= r:
            if i == 0:
                repairOperator = i
                randomInsert(destroyedSolution,removeList)
                repairUseTimes[i] += 1
                break
            elif i == 1:
                repairOperator = i
                greedyInsert(destroyedSolution,removeList)
                repairUseTimes[i] += 1
                break
    return destroyedSolution,repairOperator

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

(9)初始化算法运行数据

设定系数、迭代次数等参数。这个例子用了简单的按顺序依次访问作为初始解,也可以通过节约法之类的方法生成初始解。

T = 100   #模拟退火温度
a = 0.97  #降温速度
b = 0.5   #权重更新系数,控制权重变化速度

#用列表分别存储2个摧毁算子和2个修复算子的权重、次数、分数等信息
wDestroy = [1 for i in range(2)] #摧毁算子的初始权重,[1,1]
wRepair = [1 for i in range(2)] #修复算子的初始权重
destroyUseTimes = [0 for i in range(2)] #摧毁初始次数,0
repairUseTimes = [0 for i in range(2)] #修复初始次数
destroyScore = [1 for i in range(2)] #摧毁算子初始得分,1
repairScore = [1 for i in range(2)] #修复算子初始得分
solution = [i for i in range(distmat.shape[0])] #初始解[0,1,2,3,…, 11]
bestSolution = copy.deepcopy(solution) #最优解
iterx= 0 #初始迭代次数
iterMax = 100 #最大迭代次数100

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

(10)主程序

设置终止条件等,内嵌2个选择轮盘赌函数及模拟退火算法接受准则。

if __name__ == '__main__':
    while iterx < iterMax:    #终止条件:达到迭代次数,不满足终止条件就缓慢降低温度继续搜索
        while T > 10:   #终止温度
            destroyedSolution, remove, destroyOperatorIndex = selectAndUseDestroyOperator(wDestroy, solution)   
            #摧毁轮盘赌输入初始摧毁权重、初始解,得到摧毁后城市列表、移除列表、选择的摧毁算子序号
            newSolution, repairOperatorIndex = selectAndUseRepairOperator(wRepair, destroyedSolution, remove)   
            #修复轮盘赌输入初始修复权重、摧毁后城市列表、移除列表,得到新的城市列表、选择的修复算子序号
        <span class="token keyword">if</span> disCal<span class="token punctuation">(</span>newSolution<span class="token punctuation">)</span> <span class="token operator">&lt;=</span> disCal<span class="token punctuation">(</span>solution<span class="token punctuation">)</span><span class="token punctuation">:</span>    <span class="token comment">#判断新解与旧解的距离</span>
            solution <span class="token operator">=</span> newSolution
            <span class="token keyword">if</span> disCal<span class="token punctuation">(</span>solution<span class="token punctuation">)</span> <span class="token operator">&lt;=</span> disCal<span class="token punctuation">(</span>bestSolution<span class="token punctuation">)</span><span class="token punctuation">:</span>   <span class="token comment">#新解&lt;最优解则替换成最优解</span>
                bestSolution <span class="token operator">=</span> solution
                destroyScore<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">1.5</span>
                repairScore<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">1.5</span>     <span class="token comment">#如果是最优解的话权重增加到1.5</span>
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                destroyScore<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">1.2</span>
                repairScore<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">1.2</span>     <span class="token comment">#不是最优解仅仅好于旧解的话权重增加1.2</span>
        <span class="token keyword">else</span><span class="token punctuation">:</span>
            <span class="token keyword">if</span> rd<span class="token punctuation">.</span>random<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> np<span class="token punctuation">.</span>exp<span class="token punctuation">(</span><span class="token operator">-</span> disCal<span class="token punctuation">(</span>newSolution<span class="token punctuation">)</span><span class="token operator">/</span> T<span class="token punctuation">)</span><span class="token punctuation">:</span>     
            <span class="token comment">#应改成(disCal(newSolution) - disCal(solution),使用模拟退火算法的接受准则在一定标准下接受劣解</span>
                solution <span class="token operator">=</span> newSolution
                destroyScore<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">0.8</span>     <span class="token comment">#满足接受准则的劣解,权重增加0.8</span>
                repairScore<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">0.8</span>
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                destroyScore<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">0.6</span>   <span class="token comment">#不满足接受准则权重仅增加0.6</span>
                repairScore<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">0.6</span>

        <span class="token comment">#更新权重,(1-b)应该放前面,这个例子里取b=0.5,无影响</span>
        wDestroy<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">=</span> wDestroy<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">*</span> b
        <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">-</span> b<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token punctuation">(</span>destroyScore<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">/</span> destroyUseTimes<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span><span class="token punctuation">)</span>
        wRepair<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">=</span> wRepair<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">*</span> b
        <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">-</span> b<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token punctuation">(</span>repairScore<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">/</span> repairUseTimes<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span><span class="token punctuation">)</span>

        T <span class="token operator">=</span> a <span class="token operator">*</span> T   <span class="token comment">#温度指数下降</span>
    iterx <span class="token operator">+=</span> <span class="token number">1</span>         <span class="token comment">#完成一次降温过程算一次迭代</span>
    T <span class="token operator">=</span> <span class="token number">100</span>      <span class="token comment">#一次迭代完毕后重新设置初始温度继续迭代</span>

<span class="token keyword">print</span><span class="token punctuation">(</span>bestSolution<span class="token punctuation">)</span>   <span class="token comment">#达到终止条件后输出最佳城市访问顺序及距离</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>disCal<span class="token punctuation">(</span>bestSolution<span class="token punctuation">)</span><span class="token punctuation">)</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

(11)完整代码及结果

完整代码如下:

# Adaptive Large Neighborhood Search
import numpy as np
import random as rd
import copy

distmat = np.array([[0,350,290,670,600,500,660,440,720,410,480,970],
[350,0,340,360,280,375,555,490,785,760,700,1100],
[290,340,0,580,410,630,795,680,1030,695,780,1300],
[670,360,580,0,260,380,610,805,870,1100,1000,1100],
[600,280,410,260,0,610,780,735,1030,1000,960,1300],
[500,375,630,380,610,0,160,645,500,950,815,950],
[660,555,795,610,780,160,0,495,345,820,680,830],
[440,490,680,805,735,645,495,0,350,435,300,625],
[720,785,1030,870,1030,500,345,350,0,475,320,485],
[410,760,695,1100,1000,950,820,435,475,0,265,745],
[480,700,780,1000,960,815,680,300,320,265,0,585],
[970,1100,1300,1100,1300,950,830,625,485,745,585,0]])

def disCal(path): # calculate distance
distance = 0
for i in range(len(path) - 1):
distance += distmat[path[i]][path[i + 1]]
distance += distmat[path[-1]][path[0]]
return distance

def selectAndUseDestroyOperator(destroyWeight,currentSolution): # select and use destroy operators
destroyOperator = -1
sol = copy.deepcopy(currentSolution)
destroyRoulette = np.array(destroyWeight).cumsum()
r = rd.uniform(0, max(destroyRoulette))
for i in range(len(destroyRoulette)):
if destroyRoulette[i] >= r:
if i 0:
destroyOperator = i
removedCities = randomDestroy(sol)
destroyUseTimes[i] += 1
break
elif i 1:
destroyOperator = i
removedCities = max3Destroy(sol)
destroyUseTimes[i] += 1
break
return sol,removedCities,destroyOperator

def selectAndUseRepairOperator(repairWeight,destroyedSolution,removeList): # select and use repair operators
repairOperator = -1
repairRoulette = np.array(repairWeight).cumsum()
r = rd.uniform(0, max(repairRoulette))
for i in range(len(repairRoulette)):
if repairRoulette[i] >= r:
if i 0:
repairOperator = i
randomInsert(destroyedSolution,removeList)
repairUseTimes[i] += 1
break
elif i 1:
repairOperator = i
greedyInsert(destroyedSolution,removeList)
repairUseTimes[i] += 1
break
return destroyedSolution,repairOperator

def randomDestroy(sol): # randomly remove 3 cities
solNew = copy.deepcopy(sol)
removed = []
removeIndex = rd.sample(range(0, distmat.shape[0]), 3)
for i in removeIndex:
removed.append(solNew[i])
sol.remove(solNew[i])
return removed

def max3Destroy(sol): # remove city with 3 longest segments
solNew = copy.deepcopy(sol)
removed = []
dis = []
for i in range(len(sol) - 1):
dis.append(distmat[sol[i]][sol[i + 1]])
dis.append(distmat[sol[-1]][sol[0]])
disSort = copy.deepcopy(dis)
disSort.sort()
for i in range(3):
if dis.index(disSort[len(disSort) - i - 1]) == len(dis) - 1:
removed.append(solNew[0])
sol.remove(solNew[0])
else:
removed.append(solNew[dis.index(disSort[len(disSort) - i - 1]) + 1])
sol.remove(solNew[dis.index(disSort[len(disSort) - i - 1]) + 1])
return removed

def randomInsert(sol,removeList): # randomly insert 3 cities
insertIndex = rd.sample(range(0, distmat.shape[0]), 3)
for i in range(len(insertIndex)):
sol.insert(insertIndex[i],removeList[i])

def greedyInsert(sol,removeList): # greedy insertion
dis = float(“inf”)
insertIndex = -1
for i in range(len(removeList)):
for j in range(len(sol) + 1):
solNew = copy.deepcopy(sol)
solNew.insert(j,removeList[i])
if disCal(solNew) < dis:
dis = disCal(solNew)
insertIndex = j
sol.insert(insertIndex,removeList[i])
dis = float(“inf”)

T = 100
a = 0.97
b = 0.5
wDestroy = [1 for i in range(2)] # weights of the destroy operators
wRepair = [1 for i in range(2)] # weights of the repair operators
destroyUseTimes = [0 for i in range(2)] #The number of times the destroy operator has been used
repairUseTimes = [0 for i in range(2)] #The number of times the repair operator has been used
destroyScore = [1 for i in range(2)] # the score of destroy operators
repairScore = [1 for i in range(2)] # the score of repair operators
solution = [i for i in range(distmat.shape[0])] # initial solution
bestSolution = copy.deepcopy(solution) # best solution
iterx, iterxMax = 0, 100

if name == main:
while iterx < iterxMax: # while stop criteria not met
while T > 10:
destroyedSolution,remove,destroyOperatorIndex = selectAndUseDestroyOperator(wDestroy,solution)
newSolution,repairOperatorIndex = selectAndUseRepairOperator(wRepair,destroyedSolution,remove)

        <span class="token keyword">if</span> disCal<span class="token punctuation">(</span>newSolution<span class="token punctuation">)</span> <span class="token operator">&lt;=</span> disCal<span class="token punctuation">(</span>solution<span class="token punctuation">)</span><span class="token punctuation">:</span>
            solution <span class="token operator">=</span> newSolution
            <span class="token keyword">if</span> disCal<span class="token punctuation">(</span>newSolution<span class="token punctuation">)</span> <span class="token operator">&lt;=</span> disCal<span class="token punctuation">(</span>bestSolution<span class="token punctuation">)</span><span class="token punctuation">:</span>
                bestSolution <span class="token operator">=</span> newSolution
                destroyScore<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">1.5</span>     <span class="token comment"># update the score of the operators</span>
                repairScore<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">1.5</span>
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                destroyScore<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">1.2</span>
                repairScore<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">1.2</span>
        <span class="token keyword">else</span><span class="token punctuation">:</span>
            <span class="token keyword">if</span> rd<span class="token punctuation">.</span>random<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;</span> np<span class="token punctuation">.</span>exp<span class="token punctuation">(</span><span class="token operator">-</span> disCal<span class="token punctuation">(</span>newSolution<span class="token punctuation">)</span><span class="token operator">/</span> T<span class="token punctuation">)</span><span class="token punctuation">:</span>  <span class="token comment"># the simulated annealing acceptance criteria</span>
                solution <span class="token operator">=</span> newSolution
                destroyScore<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">0.8</span>
                repairScore<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">0.8</span>
            <span class="token keyword">else</span><span class="token punctuation">:</span>
                destroyScore<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">0.6</span>
                repairScore<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">0.6</span>

        wDestroy<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">=</span> wDestroy<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">*</span> b <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">-</span> b<span class="token punctuation">)</span> <span class="token operator">*</span> \
                                         <span class="token punctuation">(</span>destroyScore<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span> <span class="token operator">/</span> destroyUseTimes<span class="token punctuation">[</span>destroyOperatorIndex<span class="token punctuation">]</span><span class="token punctuation">)</span>
        wRepair<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">=</span> wRepair<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">*</span> b <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">-</span> b<span class="token punctuation">)</span> <span class="token operator">*</span> \
                                       <span class="token punctuation">(</span>repairScore<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span> <span class="token operator">/</span> repairUseTimes<span class="token punctuation">[</span>repairOperatorIndex<span class="token punctuation">]</span><span class="token punctuation">)</span>
        <span class="token comment"># update the weight of the operators</span>

        T <span class="token operator">=</span> a <span class="token operator">*</span> T 
    iterx <span class="token operator">+=</span> <span class="token number">1</span>
    T <span class="token operator">=</span> <span class="token number">100</span>

<span class="token keyword">print</span><span class="token punctuation">(</span>bestSolution<span class="token punctuation">)</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>disCal<span class="token punctuation">(</span>bestSolution<span class="token punctuation">)</span><span class="token punctuation">)</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157

示例最后求得bestSolution = [8, 11, 7, 10, 9, 0, 2, 1, 4, 3, 5, 6],总距离为4140。

4 总结

从上面可以看出自适应大领域搜索算法综合性还是挺强的,除了简单TSP及相关的距离优化问题,改进一下也能用在其他的优化问题中,或者与其他算法进行对比,感兴趣的朋友自己找论文看一下。理解有误的话还请大家多批评指正~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值