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

本文主要是介绍代码 | 自适应大邻域搜索系列之(6) - 判断接受准则SimulatedAnnealing的代码解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

前言

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

01 总体概述

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

其中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 notbool transitionAccepted(IBestSolutionManager& bestSolutionManager, ISolution& currentSolution, ISolution& newSolution, ALNS_Iteration_Status& status);virtual void startSignal();};

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

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

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:
1240

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 小结

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

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

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

posted @ 2019-05-10 20:25 短短的路走走停停 阅读( ...) 评论( ...) 编辑 收藏

这篇关于代码 | 自适应大邻域搜索系列之(6) - 判断接受准则SimulatedAnnealing的代码解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/161494

相关文章

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

Python实现Excel批量样式修改器(附完整代码)

《Python实现Excel批量样式修改器(附完整代码)》这篇文章主要为大家详细介绍了如何使用Python实现一个Excel批量样式修改器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录前言功能特性核心功能界面特性系统要求安装说明使用指南基本操作流程高级功能技术实现核心技术栈关键函

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

如何通过try-catch判断数据库唯一键字段是否重复

《如何通过try-catch判断数据库唯一键字段是否重复》在MyBatis+MySQL中,通过try-catch捕获唯一约束异常可避免重复数据查询,优点是减少数据库交互、提升并发安全,缺点是异常处理开... 目录1、原理2、怎么理解“异常走的是数据库错误路径,开销比普通逻辑分支稍高”?1. 普通逻辑分支 v

Java MCP 的鉴权深度解析

《JavaMCP的鉴权深度解析》文章介绍JavaMCP鉴权的实现方式,指出客户端可通过queryString、header或env传递鉴权信息,服务器端支持工具单独鉴权、过滤器集中鉴权及启动时鉴权... 目录一、MCP Client 侧(负责传递,比较简单)(1)常见的 mcpServers json 配置

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱