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

前言

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

01 总体概述

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

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

02 IAcceptanceModule

这个抽象类也很简单,只提供了一个接口transitionAccepted,以用来判断是否要接受新的解,为纯虚函数,需要在后续的代码中重写的。
```C++
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定义,下面会详细说道。
```C++
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获取当前冷却过程的温度。如果新解目标值<当前解的,那么直接接受就行了。如果>,那么按照一定的概率接受。具体公式解释嘛,小编截个图过来吧,因为在以前的文章已经讲过了:

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

```C++
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:
![](https://upload-images.jianshu.io/upload_images/10386940-3ed3efb467930f6e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

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

```C++
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是核心,用以获取当前的温度,便于上面的判断接受准则计算概率。

```C++
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<double>(nbIterations);
其中,setupPercentage为参数,nbIterations为总的迭代次数。
2. amountRemove = startingTemperature/static_cast<double>(nbIterations);
其中,startingTemperature为传入参数。
```C++
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 小结

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

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

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

原文地址:https://blog.51cto.com/14328065/2393229

时间: 2024-11-07 21:02:56

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

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

前言 上一篇讲解了destroy和repair方法的具体实现代码,好多读者都在喊酸爽和得劲儿--今天这篇就讲点简单的,关于solution的定义和管理的代码实现,让大家回回神吧--哈哈. 01 总体概述 总所周知的是,每一个算法的最终目标都是求解出一个合理的满足心意的solution.因此对solution的定义和管理基本是每个算法都要涉及的.在本ALNS代码中呢,也对solution进行了一定的抽象和规范化,提供了一些标准化的接口,同样需要在具体使用中去重写这些接口. 关于solution的处

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

前言 好了小伙伴们我们又见面了,咳咳没错还是我.不知道你萌接连被这么多篇代码文章刷屏是什么感受,不过,酸爽归酸爽.今天咱们依然讲代码哈~不过今天讲的依然很简单,关于局部搜索LocalSearch的代码. 01 总体概述 其实,LocalSearch在本算法中不是必须使用的,用户可以根据需要来选择是否启用这个功能.但是一般情况下,有了LocalSearch以后效果会好一点.而且本着服务读者的态度(我可以不用,但是小编你不能不讲),就讲讲这个模块吧.和之前讲的几个模块差不多,具体代码也是分成两个部分

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

00 前言 在上一篇推文中,教大家利用了ALNS的lib库求解了一个TSP问题作为实例.不知道你萌把代码跑起来了没有.那么,今天咱们再接再厉.跑完代码以后,小编再给大家深入讲解具体的代码内容.大家快去搬个小板凳一起过来围观学习吧~ 01 总体概述 前排高能预警,在下面的讲解中,会涉及很多C++语言的知识,特别是类与派生这一块的内容,如果C++基础比较薄弱的同学则需要回去(洗洗睡)再好好补一补啦,在这里小编就不再过多科普基础知识了.默认大家都是C++大佬,能一口说出虚函数表是什么的内种-- 描述整

代码 | 自适应大邻域搜索系列之(5) - ALNS_Iteration_Status和ALNS_P

前言 上一篇推文说了,后面的代码难度直线下降,各位小伙伴可以放去n的100次方心了.今天讲讲一些细枝末节,就是前面一直有提到的参数和一些状态的记录代码.这个简单啦,小编也不作过多解释了.大家直接看代码都能看懂,不过小编还是会把逻辑结构给大家梳理出来的.好了,开始干活. 01 ALNS_Iteration_Status 这个类,咳咳,不是抽象类了哈.主要用来记录ALNS迭代过程中的一些中间变量和状态等.主要是成员变量,成员函数都是简单的getter(获取成员变量的接口)或者setter(设置成员变

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

前言 上一篇文章中我们具体解剖了ALNS类的具体代码实现过程,不过也留下了很多大坑.接下来的文章基本都是"填坑"了,把各个模块一一展现解析给大家.不过碍于文章篇幅等原因呢,也不会每一行代码都进行讲解,那些简单的代码就跳过了,相信大家也能一眼就看懂.好了,废话不多说,开始干活吧. 01 照旧总体概述 前面我们提到,ALNS中的重中之重就是Destroy和Repair方法了,在整一个ALNS框架中呢,涉及这俩货的有Destroy和Repair方法的具体实现.Destroy和Repair方法

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

前言 上次出了邻域搜索的各种概念科普,尤其是LNS和ALNS的具体过程更是描述得一清二楚.不知道你萌都懂了吗?小编相信大家早就get到啦.不过有个别不愿意透露姓名的热心网友表示上次没有代码,遂不过瘾啊~哎,大家先别急,代码有得你们酸爽的-- 不过由于ALNS的代码量实在太大,小编打算把这个做成一个系列来一一为大家讲解,好让小伙伴们彻底把这个算法框架的代码吃透.今天暂时还是先不对代码进行讲解,先来教大家怎么使用ALNS的框架求解一个TSP问题吧~ 环境准备 小编的演示是基于Windows 10 x

干货 | 自适应大邻域搜索(Adaptive Large Neighborhood Search)入

01 首先来区分几个概念 关于neighborhood serach,这里有好多种衍生和变种出来的胡里花俏的算法.大家在上网搜索的过程中可能看到什么Large Neighborhood Serach,也可能看到Very Large Scale Neighborhood Search或者今天介绍的Adaptive Large Neighborhood Search. 对于这种名字相近,实则大有不同的概念,很是让小编这样的新手头疼.不过,小编喜欢凡事都要弄得清清楚楚明明白白的.为了防止大家混淆这些相

【PPT&amp;视频】《陈新河:万亿元大数据产业新生态》——央视网大数据名人讲堂之大数据产业系列

[PPT&视频]<陈新河:万亿元大数据产业新生态>--央视网大数据名人讲堂之大数据产业系列 原创 2016-07-16 陈新河 软件定义世界(SDX) 热门下载(点击标题即可阅读) ?[下载]2015中国数据分析师行业峰会精彩PPT下载(共计21个文件) 因微信限制,部分图不能显示出来,高清完整版全文请扫描二维码,见每篇文章底部专栏 <陈新河:万亿元大数据产业新生态>--央视网大数据名人讲堂之大数据产业系列 嘉宾介绍 陈新河   中关村大数据产业联盟副秘书长 Talking

用Python实现一个大数据搜索及源代码

在日常生活中,大家了解搜索引擎如百度.360.搜狗.谷歌等,搜索是大数据领域里常见的需求.Splunk和ELK分别是该领域在非开源和开源领域里的领导者.本文利用很少的Python代码实现了一个基本的数据搜索功能,试图让大家理解大数据搜索的基本原理. 布隆过滤器(BloomFilter) 第一步我们先要实现一个布隆过滤器. 布隆过滤器是大数据领域的一个常见算法,它的目的是过滤掉那些不是目标的元素.也就是说如果一个要搜索的词并不存在与我的数据中,那么它可以以很快的速度返回目标不存在. 让我们看看以下