一、ALNS算法与流程
自适应大领域搜索算法本质上是一种搜索算法,它的精髓在于用摧毁算子和修复算子对队列进行
摧毁和修复,从而进行启发式的搜索,
自适应表现在算子的竞争性选择上面。
算法的大体流程分为这么几步:
1)生成初始解:初始解的生成可以随机生成,也可以将当前节点的编号按照顺序存储作为初始
解,初始解会影响算法收敛的速度。
2)重复以下步骤进行迭代直到停止准则
a.根据算子权重选择破坏与修复算子,并更新算子使用次数
b.破坏算子和修复算子依次对当前解操作得到新解
c.更新当前解
d.更新最优解
e.更新算子分数
f.更新算子权重
3)返回最优解
二、算子选择
轮盘赌算法是一种归一化的随机选择算法,具体是分配给每个算子一个权重,轮盘赌先求出总的
权重和,之后将每个算子的权重进行归一化处理,之后产生一个随机值,这个随机值落在哪个算子的
权重区间内就选择哪个算子。在具体实现时,也可以不对权重进行归一化,而直接用权重作为区间进
行选择。
三、算子分数更新
算子分数分为多种情况:
1)产生的解比最优解优秀,那么加1.5分
2)如果比当前解优秀,那么说明该算子能产生较优解,所以加1.2分
3)如果不比当前解优秀,但是在1%概率内接受了,那么加0.8分
4)如果不比当前解优秀拒接了,那么加0.6分。
算子分数的更新多少是自己设定的,接受概率也是自己设定的,这几项参数并不绝对。
四、算子权重更新

新产生的权重等于上面公式的计算结果。
其中,ρ是权重更新快慢的参数,w为当前的权重,m为算子的分数,t为算子的使用次数,可以看
出,初始权重较大、且单位使用次数得分较高的算子被选中的概率较高。
五、摧毁算子
摧毁算子是用来对队列进行摧毁的一类函数,在这里提供三种摧毁思路:
1、随机摧毁:随机摧毁是ALNS算法不可少的一个摧毁算子,这个算子可以摧毁任意位置的节点。
具体实现为,随机产生N个不重复的节点摧毁位置,对队列进行摧毁。
2、最大N点摧毁:当前解队列是一个有向的路径,那么对于路径中的任意一个点,都会有唯一的
一个前驱和唯一的一个后继(除首尾节点),所以对每个节点的来和去两条路径的消耗进行统计作为
当前点的消耗,选择最大的N个点移除。
3、连续递增N节点移除:这个算子会维护一个变量K,每一次都从这个变量开始,选择N个连续的
节点进行摧毁,如果K后面不够N个点,则返回第一个点开始进行补足,每次摧毁后K=(K+1)%num,num
为节点的个数。
六、修复算子
修复算子是用来对摧毁后的队列进行修复的一类函数,在这里提供两种思路
1、随机修复:这个算子是必不可少的修复函数,表现为,任意选择N个不重复的插入的节点,将
摧毁算子摧毁后的节点插入到对应的位置。
2、最小N点插入:对于每一个摧毁节点,对摧毁队列的每一个位置进行试插入,统计每个位置的
消耗,选择插入使得总增加的消耗最小的位置进行插入。下一个节点要在前一个节点已经插入的基础
上进行消耗的判别。
七、C++代码实现参考框架

其中,Data类包含存储和操作数据的所有数据和操作,Operator包含算子和算子分数更新等一
系列的算子相关的资源,control层用于实现ALNS算法的流程,view可以根据需要,选择前后端分离
的实现方式,比如用vc6.0写后端数据处理,用qt写前端显示页面等。
Data类设计:
属性设计:
1)最优解队列:用于保存最优解队列的节点编号
2)当前解队列:用于保存当前解队列的节点编号
3)摧毁队列:用于保存去除了摧毁节点的剩下的队列
4)摧毁节点队列:用于保存被去除的节点的队列
5)消耗矩阵:用来保存任意两点的消耗(可优化)
操作设计:
1)路径消耗函数:用于统计任意一个队列的路径的消耗
2)更新当前解函数:用来根据算子分数更新的几种情况来更新当前解,并返回更新种类
3)更新最优解函数:用来评估当前解和最优解消耗,更新最优解
4)摧毁函数:传入下标队列,对当前解队列进行摧毁
5)修复函数:传入下标队列,用于将摧毁节点队列中的节点加入摧毁队列
Operator类设计:
算子类采用继承的方式,算子的使用次数更新、分数更新、权重更新、以及轮盘赌选择算子这几
个函数都写在Operator类(基类)里面,摧毁算子(OperatorDes)和修复算子(OperatorFix)各
一个类,继承于这个基类,下面详细介绍修复算子的设计,摧毁算子和修复算子设计相同。
OperatorFix:
属性设计:
1)权重更新系数
2)算子分数队列
3)算子权重队列
4)算子使用次数队列
操作设计:
1)两个修复算子函数:产生N个节点的修复下标队列,真正的摧毁在Data类中进行。
接口设计:
1)Fix(n,data):这是一个函数,供外部调用,在里面根据传入的算子编号n,选择对应的
修复算子,对data进行操作,这样外部只需要通过OperatorFix继承于Operator的轮盘赌选择算子
选择出对应的算子,传入即可进行对应的修复,而不需要知道具体的修复的细节。
Control类设计:
属性设计:
1)Data数据类的实例
2)OperatorFix类的实例
3)OperatorDes类的实例
操作设计:
1)运行函数:按照文章最一开始提到的流程,调用对应的函数,需要注意的是参数的传递。
界面显示设计:前面提到了前后端分离,这里提供三种前后端分离的实现方式:
1)文件传输数据
2)socket网络编程传输套接字
3)c++利用管道、信号量等技术实现通信