前言

前面三篇文章对大家来说应该很简单吧?不过轻松了这么久,今天再来看点刺激的。关于判断接受准则的代码。其实,判断接受准则有很多种,效果也因代码而异。今天介绍的是模拟退火的判断接受准则。那么,相关的原理之前的推文有讲过,不懂的同学回去翻翻这个文章 复习一下哈,小编也回去看看,咳咳~。好了,废话不多说,开始干活。

01 总体概述

其实这个ALNS的代码库提供了很多的判断接受准则,有最简单的直接根据目标值来判断,也有各种复杂的模拟退火降温冷却等过程来判断。不过,今天挑一个最具代表性的来讲吧,就是模拟退火的判断接受准则。其代码实现是由两个类IAcceptanceModule、SimulatedAnnealing来实现的。它们的关系依旧如下:

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

02 IAcceptanceModule

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

class IAcceptanceModule
{
public:
//! Indicate if the new created solution have to be accepted or not
//! \param bestSolutionManager a reference to the best solution manager.
//! \param currentSolution current solution.
//! \param newSolution new solution.
//! \param status the status of the current alns iteration.
//! \return true if the transition is accepted, false otherwise.
virtual bool transitionAccepted(IBestSolutionManager& bestSolutionManager, ISolution& currentSolution, ISolution& newSolution, ALNS_Iteration_Status& status) = 0; //! Some Acceptance modules needs to initialize some variable
//! only when the solver actualy starts working. In this case
//! you should override this method.
virtual void startSignal(){};
};

03 SimulatedAnnealing

SimulatedAnnealing继承于上面的接口类IAcceptanceModule,它利用模拟退火的判断接受准则实现了transitionAccepted的功能。值得注意的是,该类成员变量里面是一个CoolingSchedule,用来获取当前温度。该表有另一个抽象类ICoolingSchedule定义,下面会详细说道。

class SimulatedAnnealing: public IAcceptanceModule {
private:
//! The cooling schedule to be use to compute the temperature each time it
//! is needed.
ICoolingSchedule* coolingSchedule;
public:
//! Constructor.
//! \param cs the cooling schedule to be used by the simulated annealing.
SimulatedAnnealing(ICoolingSchedule& cs); //! Destructor.
virtual ~SimulatedAnnealing(); //! Compute if the newly created solution have to be accepted or not
bool transitionAccepted(IBestSolutionManager& bestSolutionManager, ISolution& currentSolution, ISolution& newSolution, ALNS_Iteration_Status& status); virtual void startSignal(); };

其成员函数的实现也非常的简单,不过多说两句。先利用CoolingSchedule获取当前冷却过程的温度。如果新解目标值<当前解的,那么直接接受就行了。如果>,那么按照一定的概率接受。具体公式解释嘛,小编截个图过来吧,因为在以前的文章已经讲过了:

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

bool SimulatedAnnealing::transitionAccepted(IBestSolutionManager& bestSolutionManager, ISolution& currentSolution, ISolution& newSolution, ALNS_Iteration_Status& status)
{
double temperature = coolingSchedule->getCurrentTemperature();
if(newSolution < currentSolution)
{
return true;
}
else
{
double difference = newSolution.getPenalizedObjectiveValue() - currentSolution.getPenalizedObjectiveValue();
double randomVal = static_cast<double>(rand())/static_cast<double>(RAND_MAX);
return (exp(-1*difference/temperature)>randomVal);
}
} void SimulatedAnnealing::startSignal()
{
coolingSchedule->startSignal();
}

04 ICoolingSchedule

4.1 ICoolingSchedule

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

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

class ICoolingSchedule
{
public:
//! \return the current temperature.
virtual double getCurrentTemperature()=0; //! This method should be called when the optimization
//! process start. The cooling schedules that actually need
//! this should override this method.
virtual void startSignal(){};
};

4.2 LinearCoolingSchedule

由于CoolingSchedule有很多类型,小编挑一个LinearCoolingSchedule给大家讲解吧。LinearCoolingSchedule主要的根据是迭代的次数来工作的。成员函数getCurrentTemperature是核心,用以获取当前的温度,便于上面的判断接受准则计算概率。

class LinearCoolingSchedule: public ICoolingSchedule {
private:
//! The current temperature.
double currentTemperature; //! The amount to remove at each temperature recomputation.
double amountRemove;
public:
//! Constructor.
//! \param initSol the initial solution.
//! \param csParam the cooling schedule parameters.
//! \param nbIterations the number of iterations to be performed.
LinearCoolingSchedule(ISolution& initSol, CoolingSchedule_Parameters& csParam, size_t nbIterations); //! Constructor.
//! \param startingTemperature the initial temperature.
//! \param nbIterations the number of iterations to be performed.
LinearCoolingSchedule(double startingTemperature, size_t nbIterations); //! Destructor.
virtual ~LinearCoolingSchedule(); //! Compute and return the current temperature.
//! \return the current temperature.
double getCurrentTemperature(); void startSignal(){};
};

然后现在来看看其具体方法是怎么实现的吧。其实也很简单,没有那么复杂。每次获取currentTemperature的时候呢,先让currentTemperature降降温,再返回。降温的幅度是利用currentTemperature 减去 amountRemove实现的。那么amountRemove又是怎么得出来的呢?LinearCoolingSchedule提供了两个构造函数,对应不同的计算方法:

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

    amountRemove = currentTemperature/static_cast(nbIterations);

    其中,setupPercentage为参数,nbIterations为总的迭代次数。
  2. amountRemove = startingTemperature/static_cast(nbIterations);

    其中,startingTemperature为传入参数。
LinearCoolingSchedule::LinearCoolingSchedule(ISolution& initSol, CoolingSchedule_Parameters& csParam, size_t nbIterations) {
currentTemperature = (csParam.setupPercentage*initSol.getPenalizedObjectiveValue())/(-log(0.5));
amountRemove = currentTemperature/static_cast<double>(nbIterations); } LinearCoolingSchedule::LinearCoolingSchedule(double startingTemperature, size_t nbIterations) {
assert(nbIterations>0);
assert(startingTemperature>=0);
currentTemperature = startingTemperature;
amountRemove = startingTemperature/static_cast<double>(nbIterations); } LinearCoolingSchedule::~LinearCoolingSchedule() {
// Nothing to be done.
} double LinearCoolingSchedule::getCurrentTemperature()
{
currentTemperature-= amountRemove;
if(currentTemperature < 0)
{
currentTemperature = 0;
}
assert(currentTemperature>=0);
return currentTemperature;
}

05 小结

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

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

代码及相关内容可关注公众号。更多精彩尽在微信公众号【程序猿声】

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

  1. 代码 | 自适应大邻域搜索系列之(4) - Solution定义和管理的代码实现解析

    前言 上一篇讲解了destroy和repair方法的具体实现代码,好多读者都在喊酸爽和得劲儿--今天这篇就讲点简单的,关于solution的定义和管理的代码实现,让大家回回神吧--哈哈. 01 总体概 ...

  2. 代码 | 自适应大邻域搜索系列之(7) - 局部搜索LocalSearch的代码解析

    前言 好了小伙伴们我们又见面了,咳咳没错还是我.不知道你萌接连被这么多篇代码文章刷屏是什么感受,不过,酸爽归酸爽.今天咱们依然讲代码哈~不过今天讲的依然很简单,关于局部搜索LocalSearch的代码 ...

  3. 代码 | 自适应大邻域搜索系列之(2) - ALNS算法主逻辑结构解析

    00 前言 在上一篇推文中,教大家利用了ALNS的lib库求解了一个TSP问题作为实例.不知道你萌把代码跑起来了没有.那么,今天咱们再接再厉.跑完代码以后,小编再给大家深入讲解具体的代码内容.大家快去 ...

  4. 代码 | 自适应大邻域搜索系列之(5) - ALNS_Iteration_Status和ALNS_Parameters的代码解析

    前言 上一篇推文说了,后面的代码难度直线下降,各位小伙伴可以放去n的100次方心了.今天讲讲一些细枝末节,就是前面一直有提到的参数和一些状态的记录代码.这个简单啦,小编也不作过多解释了.大家直接看代码 ...

  5. 代码 | 自适应大邻域搜索系列之(3) - Destroy和Repair方法代码实现解析

    前言 上一篇文章中我们具体解剖了ALNS类的具体代码实现过程,不过也留下了很多大坑.接下来的文章基本都是"填坑"了,把各个模块一一展现解析给大家.不过碍于文章篇幅等原因呢,也不会每 ...

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

    01 首先来区分几个概念 关于neighborhood serach,这里有好多种衍生和变种出来的胡里花俏的算法.大家在上网搜索的过程中可能看到什么Large Neighborhood Serach, ...

  7. 自适应大邻域搜索代码系列之(1) - 使用ALNS代码框架求解TSP问题

    前言 上次出了邻域搜索的各种概念科普,尤其是LNS和ALNS的具体过程更是描述得一清二楚.不知道你萌都懂了吗?小编相信大家早就get到啦.不过有个别不愿意透露姓名的热心网友表示上次没有代码,遂不过瘾啊 ...

  8. 大数据学习系列之九---- Hive整合Spark和HBase以及相关测试

    前言 在之前的大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 中介绍了集群的环境搭建,但是在使用hive进行数据查询的时候会非常的慢,因为h ...

  9. 大数据小白系列——HDFS(2)

    这里是大数据小白系列,这是本系列的第二篇,介绍一下HDFS中SecondaryNameNode.单点失败(SPOF).以及高可用(HA)等概念. 上一篇我们说到了大数据.分布式存储,以及HDFS中的一 ...

随机推荐

  1. SpinWait 第二篇

    SpinWait 提供了两个方法和两个只读属性. 方法: SpinWait.Reset() : 重置自旋计数器,将计数器置 0.效果就好像没调用过SpinOnce一样.SpinWait.Once() ...

  2. ASCII,UTF-8,Unicode字符串相互转换

    #include<string> #include<windows.h> #include<vector> using namespace std; //utf8 ...

  3. Asp.Net 加载不同项目程序集

    我们做项目时有时候不想添加别的项目的引用,但是那个项目又必须在 Global 中进行注册 最常见的就是插件机制,参考: https://shazwazza.com/post/Developing-a- ...

  4. 如何封装属于自己的WPF控件库

    在网上找了一下,发现这方面的资料并不多.做传统桌面的本来就不多了吧,更别说WPF了.我可能也要另寻出路了,不过我还是觉得做桌面挺有意思的. 言归正传  首先,新建一个WPF自定义控件库项目 这里我们封 ...

  5. jenkins pipline

    def getHost(){ def remote = [:] remote.name = 'server02' remote.host = '39.19.90' remote.user = 'roo ...

  6. sql 防注入(更新问题)

    一下这条语句虽然不会是数据表中的数据发生变化,但是会对数据库主从造成影响 update `article` where `article_id` = '40&n974742=v995656' ...

  7. vue复制textarea文本域内容到粘贴板

    vue实现复制内容到粘贴板   方案:找到textarea对象(input同样适用),获取焦点,选中textarea的所有内容,并调用document.execCommand("copy&q ...

  8. vue中组件之间的通信

    一.vue中组件通信的种类 父组件向子组件的通信 子组件向父组件的通信 隔代组件之间的通信 兄弟 组件 之间的通信 二.实现通信的方式  props vue自定义的事件 消息订阅与发布 vuex sl ...

  9. 安装HANA Rules Framework(HRF)

    1. 收集文档 1.1  SAP HANA Rules Framework by the SAP HANA Academy link 1.2  HANA Rules Framework (HRF) b ...

  10. 流程控制 while for

    循环执行 计算机最擅长的功能之一就是按照规定的条件,重复执行某些操作,这是程序设计中最能发挥计算机特长的程序结构. 1.while语句 while(表达式){ 各种语句.... } 当表达式的值为tr ...