自适应大领域搜索算法(ALNS) 详解及python示例

https://huaweicloud.csdn.net/63806872dacf622b8df86ebe.html?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Eactivity-6-113065064-blog-121124290.235%5Ev38%5Epc_relevant_sort&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Eactivity-6-113065064-blog-121124290.235%5Ev38%5Epc_relevant_sort&utm_relevant_index=11

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)权重更新

在这里插入图片描述
可设定以下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]])

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

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

def disCal(path):    
    distance = 0          #若路径为path = 【1,2, 3】,len(pathÿ
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
自适应大邻域算法(Adaptive Large Neighborhood Search,ALNS)是一种启发式算法,用于解决组合优化问题。它通过动态调整邻域结构,以达到更好的搜索效果。 以下是一个用Python实现ALNS算法的示例: ```python import random class Solution: def __init__(self, n): self.n = n self.seq = list(range(1, n+1)) random.shuffle(self.seq) self.cost = self.evaluate() def evaluate(self): # 计算当前解的成本 cost = 0 for i in range(self.n-1): for j in range(i+1, self.n): if abs(i-j) == abs(self.seq[i]-self.seq[j]): cost += 1 return cost def get_neighbor(self): # 生成一个随机邻居 i, j = random.sample(range(self.n), 2) seq = self.seq.copy() seq[i], seq[j] = seq[j], seq[i] return Solution.from_seq(seq) def apply_move(self, move): # 应用一个移动操作 i, j = move self.seq[i], self.seq[j] = self.seq[j], self.seq[i] self.cost = self.evaluate() @classmethod def from_seq(cls, seq): # 从序列创建一个解 instance = cls(len(seq)) instance.seq = seq instance.cost = instance.evaluate() return instance class ALNS: def __init__(self, problem, max_iter=1000, p_init=0.9, p_decay=0.99, max_tabu_size=10): self.problem = problem self.max_iter = max_iter self.p_init = p_init self.p_decay = p_decay self.max_tabu_size = max_tabu_size self.tabu_list = [] def run(self): curr = self.problem best = curr p = self.p_init for i in range(self.max_iter): neighbor = curr.get_neighbor() if neighbor.cost < curr.cost or random.random() < p: curr = neighbor if curr.cost < best.cost: best = curr self.update_tabu_list(curr, neighbor) p *= self.p_decay return best def update_tabu_list(self, curr, neighbor): # 更新禁忌表 move = self.get_move(curr, neighbor) if move in self.tabu_list: self.tabu_list.remove(move) self.tabu_list.append(move) if len(self.tabu_list) > self.max_tabu_size: self.tabu_list.pop(0) def get_move(self, curr, neighbor): # 获取当前解到邻居解的移动操作 for i in range(curr.n): if curr.seq[i] != neighbor.seq[i]: for j in range(i+1, curr.n): if curr.seq[j] != neighbor.seq[j] and (i, j) not in self.tabu_list: return (i, j) ``` 在这个示例中,我们定义了一个`Solution`类来表示一个解。`Solution`类有一个`seq`属性表示解的序列,一个`cost`属性表示解的成本。`Solution`类还有一个`evaluate`方法来计算解的成本,一个`get_neighbor`方法来生成一个随机邻居,一个`apply_move`方法来将一个移动操作应用到当前解上。`Solution`类还有一个`from_seq`类方法来从序列创建一个解。 我们还定义了一个`ALNS`类来实现ALNS算法。`ALNS`类有一个`run`方法来运行算法,一个`update_tabu_list`方法来更新禁忌表,一个`get_move`方法来获取当前解到邻居解的移动操作。 在`run`方法中,我们首先初始化当前解和最佳解为问题的初始解。然后在循环中,我们生成一个随机邻居,并根据概率和成本比较决定是否接受该邻居。如果接受该邻居,则更新当前解和禁忌表,并更新最佳解。最后,我们通过逐步减小概率`p`来控制算法的收敛性。 这个示例实现了一个简单的N皇后问题,但可以通过修改`Solution`类和`ALNS`类来适应其他组合优化问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

喝凉白开都长肉的大胖子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值