自适应大邻域搜索算法

自适应大邻域搜索算法(Adaptive Large Neighborhood Search)是由Ropke与Pisinger在2006年提出的一种启发式方法,其在邻域搜索的基础上增加了的对算子的作用效果的衡量,使算法能够自动选择好的算子对解进行破坏与修复,从而有一定几率得到更好的解。在邻域搜索算法中,有的算法可以只使用一种邻域,如模拟退火算法,因此它仅仅搜索了解空间的一小部分,找到全局最优的概率较小,它的优势之一是可以避免陷入局部最优;而有的算法可以使用多种算子,如变邻域搜索算法,它通过在当前解的多个邻域中寻找更满意的解,能够大大提高算法在解空间的搜索范围,但是它在使用算子时盲目地将每种算子形成的邻域结构都搜索一遍,缺少了一些启发式信息的指导。而自适应大邻域搜索算法就弥补了这种不足,这种算法根据算子的历史表现与使用次数选择下一次迭代使用的算子,通过算子间的相互竞争来生成当前解的邻域结构,而在这种结构中有很大概率能够找到更好的解。

自适应大邻域搜索算法的步骤如下:
1.生成初始解X,最优解X’ = X,初始化算法参数。
2.重复如下步骤直到停止准则:
2.1轮盘赌方法根据算子权重选择破坏与修复算子对当前解进行操作得到新解X’’。
2.2更新算子使用次数
2.3如f(X’’) < f(X),则X = X’’。
2.4如f(X’’) < f(X’),则X’ = X’’。
2.5如f(X’’) > f(X),则以一定的概率接受该解作为当前解。
2.5更新算子的分数。
2.6更新算子的权重。权重更新式子如下:
在这里插入图片描述
式中,Wd为算子权重,Sd为算子分数,Ud为算子的使用次数。
3.输出最优解。

对于TSP问题,ALNS往往能在较短时间内找到满意解:

# 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)

            if disCal(newSolution) <= disCal(solution):
                solution = newSolution
                if disCal(newSolution) <= disCal(bestSolution):
                    bestSolution = newSolution
                    destroyScore[destroyOperatorIndex] += 1.5     # update the score of the operators
                    repairScore[repairOperatorIndex] += 1.5
                else:
                    destroyScore[destroyOperatorIndex] += 1.2
                    repairScore[repairOperatorIndex] += 1.2
            else:
                if rd.random() < np.exp(- disCal(newSolution)/ T):  # the simulated annealing acceptance criteria
                    solution = newSolution
                    destroyScore[destroyOperatorIndex] += 0.8
                    repairScore[repairOperatorIndex] += 0.8
                else:
                    destroyScore[destroyOperatorIndex] += 0.6
                    repairScore[repairOperatorIndex] += 0.6

            wDestroy[destroyOperatorIndex] = wDestroy[destroyOperatorIndex] * b + (1 - b) * \
                                             (destroyScore[destroyOperatorIndex] / destroyUseTimes[destroyOperatorIndex])
            wRepair[repairOperatorIndex] = wRepair[repairOperatorIndex] * b + (1 - b) * \
                                           (repairScore[repairOperatorIndex] / repairUseTimes[repairOperatorIndex])
            # update the weight of the operators

            T = a * T 
        iterx += 1
        T = 100

    print(bestSolution)
    print(disCal(bestSolution))
  • 14
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值