前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >代码 | 自适应大邻域搜索系列之(6) - 判断接受准则SimulatedAnnealing的代码解析

代码 | 自适应大邻域搜索系列之(6) - 判断接受准则SimulatedAnnealing的代码解析

作者头像
用户1621951
发布2019-06-06 11:11:23
5030
发布2019-06-06 11:11:23
举报
文章被收录于专栏:数据魔术师

对大家来说应该很简单吧?不过轻松了这么久,今天再来看点刺激的。

关于判断接受准则的代码。

其实,判断接受准则有很多种,效果也因代码而异。今天介绍的是模拟退火的判断接受准则。

那么,相关的原理之前的推文有讲过,不懂的同学回去翻翻 干货 | 用模拟退火(SA, Simulated Annealing)算法解决旅行商问题 复习一下哈,小编也回去看看,咳咳~。好了,废话不多说,开始干活。

01 总体概述

其实这个ALNS的代码库提供了很多的判断接受准则,有最简单的直接根据目标值来判断,也有各种复杂的模拟退火降温冷却等过程来判断。

不过,今天挑一个最具代表性的来讲吧,就是模拟退火的判断接受准则。其代码实现是由两个类IAcceptanceModule、SimulatedAnnealing来实现的。

它们的关系依旧如下:

其中IAcceptanceModule依旧是抽象类,只提供接口。下面对这两货进行解析。

02 IAcceptanceModule

这个抽象类也很简单,只提供了一个接口transitionAccepted,以用来判断是否要接受新的解,为纯虚函数,需要在后续的代码中重写的。

代码语言:javascript
复制
 1class IAcceptanceModule
 2{
 3public:
 4    //! Indicate if the new created solution have to be accepted or not
 5    //! \param bestSolutionManager a reference to the best solution manager.
 6    //! \param currentSolution current solution.
 7    //! \param newSolution new solution.
 8    //! \param status the status of the current alns iteration.
 9    //! \return true if the transition is accepted, false otherwise.
10    virtual bool transitionAccepted(IBestSolutionManager& bestSolutionManager, ISolution& currentSolution, ISolution& newSolution, ALNS_Iteration_Status& status) = 0;
11
12    //! Some Acceptance modules needs to initialize some variable
13    //! only when the solver actualy starts working. In this case
14    //! you should override this method.
15    virtual void startSignal(){};
16};

03 SimulatedAnnealing

SimulatedAnnealing继承于上面的接口类IAcceptanceModule,它利用模拟退火的判断接受准则实现了transitionAccepted的功能。

值得注意的是,该类成员变量里面是一个CoolingSchedule,用来获取当前温度。该表有另一个抽象类ICoolingSchedule定义,下面会详细说道。

代码语言:javascript
复制
 1class SimulatedAnnealing: public IAcceptanceModule {
 2private:
 3    //! The cooling schedule to be use to compute the temperature each time it
 4    //! is needed.
 5    ICoolingSchedule* coolingSchedule;
 6public:
 7    //! Constructor.
 8    //! \param cs the cooling schedule to be used by the simulated annealing.
 9    SimulatedAnnealing(ICoolingSchedule& cs);
10
11    //! Destructor.
12    virtual ~SimulatedAnnealing();
13
14    //! Compute if the newly created solution have to be accepted or not
15    bool transitionAccepted(IBestSolutionManager& bestSolutionManager, ISolution& currentSolution, ISolution& newSolution, ALNS_Iteration_Status& status);
16
17    virtual void startSignal();
18
19};

其成员函数的实现也非常的简单,不过多说两句。先利用CoolingSchedule获取当前冷却过程的温度。

如果新解目标值<当前解的,那么直接接受就行了。如果>,那么按照一定的概率接受。

具体公式解释嘛,小编截个图过来吧,因为在以前的文章已经讲过了:

不过这里的能量差计算用的是解的目标惩罚值算的,不是目标值。

代码语言:javascript
复制
 1bool SimulatedAnnealing::transitionAccepted(IBestSolutionManager& bestSolutionManager, ISolution& currentSolution, ISolution& newSolution, ALNS_Iteration_Status& status)
 2{
 3    double temperature = coolingSchedule->getCurrentTemperature();
 4    if(newSolution < currentSolution)
 5    {
 6        return true;
 7    }
 8    else
 9    {
10        double difference = newSolution.getPenalizedObjectiveValue() - currentSolution.getPenalizedObjectiveValue();
11        double randomVal = static_cast<double>(rand())/static_cast<double>(RAND_MAX);
12        return (exp(-1*difference/temperature)>randomVal);
13    }
14}
15
16void SimulatedAnnealing::startSignal()
17{
18    coolingSchedule->startSignal();
19}

04 ICoolingSchedule

这货是一个抽象类,CoolingSchedule有很多种类型,根据不同需要由这个类可以派生出下面类型的CoolingSchedule

ICoolingSchedule只提供了两个接口,其中getCurrentTemperature是纯虚函数,用以获取当前的退火温度,需要重写。

代码语言:javascript
复制
 1class ICoolingSchedule
 2{
 3public:
 4    //! \return the current temperature.
 5    virtual double getCurrentTemperature()=0;
 6
 7    //! This method should be called when the optimization
 8    //! process start. The cooling schedules that actually need
 9    //! this should override this method.
10    virtual void startSignal(){};
11};

4.1 LinearCoolingSchedule

由于CoolingSchedule有很多类型,小编挑一个LinearCoolingSchedule给大家讲解吧。

LinearCoolingSchedule主要的根据是迭代的次数来工作的。成员函数getCurrentTemperature是核心,用以获取当前的温度便于上面的判断接受准则计算概率。

代码语言:javascript
复制
 1class LinearCoolingSchedule: public ICoolingSchedule {
 2private:
 3    //! The current temperature.
 4    double currentTemperature;
 5
 6    //! The amount to remove at each temperature recomputation.
 7    double amountRemove;
 8public:
 9    //! Constructor.
10    //! \param initSol the initial solution.
11    //! \param csParam the cooling schedule parameters.
12    //! \param nbIterations the number of iterations to be performed.
13    LinearCoolingSchedule(ISolution& initSol, CoolingSchedule_Parameters& csParam, size_t nbIterations);
14
15    //! Constructor.
16    //! \param startingTemperature the initial temperature.
17    //! \param nbIterations the number of iterations to be performed.
18    LinearCoolingSchedule(double startingTemperature, size_t nbIterations);
19
20    //! Destructor.
21    virtual ~LinearCoolingSchedule();
22
23    //! Compute and return the current temperature.
24    //! \return the current temperature.
25    double getCurrentTemperature();
26
27    void startSignal(){};
28};

然后现在来看看其具体方法是怎么实现的吧。其实也很简单,没有那么复杂。每次获取currentTemperature的时候呢,先让currentTemperature降降温,再返回。

降温的幅度是利用currentTemperature 减去 amountRemove实现的。

那么amountRemove又是怎么得出来的呢?LinearCoolingSchedule提供了两个构造函数,对应不同的计算方法:

1) currentTemperature =

(csParam.setupPercentage*initSol.getPenalizedObjectiveValue())/(-log(0.5));

amountRemove = currentTemperature / static_cast<double>(nbIterations);

其中,setupPercentage为参数,nbIterations为总的迭代次数。

2) amountRemove = startingTemperature/static_cast<double>(nbIterations);

其中,startingTemperature为传入参数。

代码语言:javascript
复制
 1LinearCoolingSchedule::LinearCoolingSchedule(ISolution& initSol, CoolingSchedule_Parameters& csParam, size_t nbIterations) {
 2    currentTemperature = (csParam.setupPercentage*initSol.getPenalizedObjectiveValue())/(-log(0.5));
 3    amountRemove = currentTemperature/static_cast<double>(nbIterations);
 4
 5}
 6
 7LinearCoolingSchedule::LinearCoolingSchedule(double startingTemperature, size_t nbIterations) {
 8    assert(nbIterations>0);
 9    assert(startingTemperature>=0);
10    currentTemperature = startingTemperature;
11    amountRemove = startingTemperature/static_cast<double>(nbIterations);
12
13}
14
15LinearCoolingSchedule::~LinearCoolingSchedule() {
16    // Nothing to be done.
17}
18
19double LinearCoolingSchedule::getCurrentTemperature()
20{
21    currentTemperature-= amountRemove;
22    if(currentTemperature < 0)
23    {
24        currentTemperature = 0;
25    }
26    assert(currentTemperature>=0);
27    return currentTemperature;
28}

05 小结

今天讲的总体也不是很难,相信之前模拟退火学得好的小伙伴一眼就能看懂了,如果其他小伙伴还不是很理解的话,回去看看之前的文章,看看模拟退火的判断接受准则再多加理解,相信对大家不是什么问题。

至此,代码已经讲得差不多了,估摸着还能再做几篇文章,依然感谢大家一路过来的支持。谢谢!咱们下期再见。

---The End---

文案 && 编辑:邓发珩

审稿:庄浩城

指导老师: 秦时明岳(华中科技大学管理学院)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据魔术师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档