有好些时间没有写过关于OptaPlanner的东西了,其实近半年来,OptaPlanner还是推出了不少有用、好用的新特性。包括本文讲到的以Stream接口实现评分编程。关于OptraPlanner的约束详细用法,可以参考官方资料:Constraint streams score calculation​docs.optaplanner.org

最近几个版本推出的新功能、特性中,有不少功能还处于初始探索阶段,甚至有些功能还未成体系,包括我在上一篇文件中推出的SolverManger实现批量异步规划。此功能尚未支持ProblemChanged接口,从而无法实现Realtime Planning. 因此,若需要将这些功能应用于项目实践,还请自行作详细调查分析,以免在项目中处于进退两难境地。

PS. 任何技术都一样,功能、版本越新,带来的收益越高,当然需要面对的风险也越高。

对OptaPlanner有初步认识都清楚,我们使用OptaPlanner规划建模时,需要在模型中表达一系列约束,以描述各个业务实体的约束和规划的优化目标。以往通常有两种方式实现评分逻辑(详细可分为3种)。分别是:

  1. 通过Drools脚本中的Rule来描述约束并进行评分;
  2. 通过Java编写评分逻辑,通过Java编辑评分逻辑又分为:
    1. Java简易评分 - Easy Java score calculation
    2. Java增量评分 - Incremental Java score calculation

从7.31版本开始提供的constraint streams属于Java增量评分的一种。在普通的Java增量评分中,我们需要针对各个约束逻辑,编辑相应的判断,并在满足一定条件后,通过ScoreHolder对象进行记分。引擎会将各个层次的分数进行累加,成为当前方案的总分。Constraint Streams的原理也一样,只是通过强大的Stream特性,令评分逻辑更为简洁,使用更短的代码即可实现更丰富的逻辑描述。

关于Java的Stream特性(Java1.8及以后的版本才出现)的使用方法,可自行通过其它网络资源学习,本文假设读者熟悉Java Stream的各种用法。

我们先以一个简单的示例说明Constraint streams接口的使用方法:

private int doNotAssignAnn() {int softScore = 0;schedule.getShiftList().stream().filter(Shift::isEmployeeAnn).forEach(shift -> {softScore -= 1;});return softScore;}

通过上述代码块是一Java简易评分的示例,从方法名doNotAssignAnn就很容易理解到,该约束的作用是“使得任务不要分配给Ann”。我们知道在OptaPlanner里,评分通常都是负数,表示惩罚一个行为,令引擎找出尽可能规避这种行为的方案。示例中使用了Java的Stream功能进行判断和过滤。其逻辑是:从班次列表中找出所有分配给了Ann的班次,对每一个满足这个条件的班次进行扣分,并把分数加总作为方法的返回值。

那么同样的约束要求,使用Constraint Stream应该如何实现呢?见以下代码:

private Constraint doNotAssignAnn(ConstraintFactory factory) {return factory.from(Shift.class).filter(Shift::isEmployeeAnn).penalize("Don't assign Ann", HardSoftScore.ONE_SOFT);}

先要提醒一下,与Java简单评分法类似(评分类需要实现EasyScoreCalculator接口),OptaPlanner的Constraint Stream提供一个名为ConstraintProvider的接口,实现评分的类需要实现这个接口,这个接口只有一个需要实现的方法 - defineConstraints,它传入ConstrantFactory类,返回一个Constraint数组,数组的元素就是已进行了评分和惩罚的各个约束对象。上面的代码中可以看到,doNotAssignAnn方法返回一个Constraint对象,这个对象表示了对Ann被分配到的班次数的惩罚分数。上述代码可以看到,我们只需要对ConstraintFactory的对象factory进行Stream操作,一步即可完成判断、过滤和惩罚三个操作,完成这些操作后会得到一个操作过的Contraint对象,返回该对象即可。上述代码中,对于factory的三步操作也相当明了,大家可以自己理解。

但是对于一些更复杂的判断,其实现步骤与模式也一样,只不过需要编写一些更复杂的Lambda表达式来进行判断、过滤和各种运算。如下代码:

  private Constraint requiredCpuPowerTotal(ConstraintFactory factory) {return factory.from(CloudProcess.class).groupBy(CloudProcess::getComputer, sum(CloudProcess::getRequiredCpuPower)).filter((computer, requiredCpuPower) -> requiredCpuPower > computer.getCpuPower()).penalize("requiredCpuPowerTotal",HardSoftScore.ONE_HARD,(computer, requiredCpuPower) -> requiredCpuPower - computer.getCpuPower());}

该代码是CloudBalance中用于,计算限制一台计划机被分配超出其CPU运算能力的约束。大家可以回想,或从官方示例中看一下CloudBalance的其中一个最基本约束 - 每台计算机所分得的CPU需求,不可超过该计算机的可用CPU能力。

因此,可以看到,factory除了过from操作获得所有Process对象,通过filter对Process进行过滤,通过penalize进行计分外。factory对象还有一个groupBy方法,用于对所有Process中的Computer进行分组并加总每一组(即每个Computer)的所有CPU计算能力需求量。因此,在filter方法中,就找出那些超出CPU能力的Computer(即分组),在penalize方法中,对整所有超出CPU需求中的计算进行扣分,扣分值是超出部分。

由此可能,OptaPlanner提供的Constraint Stream可以进行更复杂的条件判断,至于这种方法是否更好用,就取决于大家对Stream(类似C#中的Linq)的熟悉程度。

至于整个Constraint Stream代码的结果方式,即上面提到的实现ConstraintProvider接口的代码如下(摘自官方示例CloudBalance):

public class CloudBalancingConstraintProvider implements ConstraintProvider {@Overridepublic Constraint[] defineConstraints(ConstraintFactory constraintFactory) {return new Constraint[] {requiredCpuPowerTotal(constraintFactory),requiredMemoryTotal(constraintFactory),requiredNetworkBandwidthTotal(constraintFactory),computerCost(constraintFactory)};}// ************************************************************************// Hard constraints// ************************************************************************private Constraint requiredCpuPowerTotal(ConstraintFactory constraintFactory) {return constraintFactory.from(CloudProcess.class).groupBy(CloudProcess::getComputer, sum(CloudProcess::getRequiredCpuPower)).filter((computer, requiredCpuPower) -> requiredCpuPower > computer.getCpuPower()).penalize("requiredCpuPowerTotal",HardSoftScore.ONE_HARD,(computer, requiredCpuPower) -> requiredCpuPower - computer.getCpuPower());}
.
.
.

重复提示一下,Constraint Stream功能是7.31版才开始提供的功能,从功能接口上应该是未够成功的,如果需要在项目中实现一些更为复杂的约束描述,建议暂时还是不要直接使用。在OptaPlanner的用户手册中,也有相关的提示;大家看情况而用。

最近一段时间OptaPlanner更新算比较频繁,但从网站上的更新内容看到,很多版本尽管隔了很长时间,但接口上的更新内容却不多。我向Geoffrey查询过,他表示这些版本更多的情况是在实现一些引擎内部的优化和一些新的内部运算功能,但这些功能不一定反映到API上,因此对于我们使用者来说,并没有太大的变化。可是如果大家也跟进将OptaPlanner的程序包也更新到最新版本,就会发现,很多一些常用的接口、方法,都已经被标准为将为放弃,从Javadocs上可以看到一些当前版本被标识为@Deprecated的方法、成员,已明确说明将在8.x中停止使用。

上述功能希望可以帮大家理解并应用OptaPlanner的第四种评分方式。

Constraint satisfaction solver (Java™, Open Source)​www.optaplanner.org

本系列文章在公众号不定时连载,请关注公众号(搜“让APS成为可能”或扫以下QR Code)及时接收,二维码:

如需了解更多关于OptaPlanner的应用,请发电邮致:kentbill@gmail.com
或到讨论组发表你的意见:https://groups.google.com/forum/#!forum/optaplanner-cn
若有需要可添加本人微信(13631823503)或QQ(12977379)实时沟通,但因本人日常工作繁忙,通过微信,QQ等工具可能无法深入沟通,较复杂的问题,建议以邮件或讨论组方式提出。(讨论组属于google邮件列表,国内网络可能较难访问,需自行解决)

OptaPlanner的新约束表达方式 Constraint Streams相关推荐

  1. python字符串怎么表示_python中字符串的几种表达方式(用什么方式表示字符串)...

    原博文 2018-11-21 18:45 − 说明: 今天在学习python的基础的内容,学习在python中如何操作字符串,在此记录下. 主要是python中字符串的几种表达,表示方式. pytho ...

  2. 计算机视觉关于进化计算表达方式0.0.6

    计算机视觉关于进化计算表达方式0.0.6 罗瑶光 关键词:卷积视觉,元基索引,肽展腐蚀,新陈代谢. 1 思想 天赋是需要后天培养的, 正如我自己, 年轻的时候能接触许多计算机的课程和知识点. 如加州路 ...

  3. jpg图片无损放入PDF的程序,PDF文件格式分析,图像表达方式

    wxleasyland@139.com 用打印的方式,一般是将JPG解码后的数据发给打印机,而不是JPG原始数据,所以PDF打印机会重新压缩. 不对JPG图像进行解压,直接将JPG图像传送到虚拟打印机 ...

  4. c语言程序设计分段定时器,单片机C语言编程定时器的几种表达方式

    原标题:单片机C语言编程定时器的几种表达方式 吴鉴鹰单片机开发板地址 店铺:[吴鉴鹰的小铺] 地址:[https://item.taobao.com/item.htm?_u=ukgdp5a7629&a ...

  5. 一种新的攻击方式:使用Outlook 表单进行横向渗透和常驻

    本文讲的是一种新的攻击方式:使用Outlook 表单进行横向渗透和常驻, 背景 最近我们针对CrowdStrike服务进行例行调查,发现了一种攻击方法,其主要用于横向渗透和系统常驻,而且是以前我们没有 ...

  6. 30岁找不到工作很绝望_计算机为绝望的新编码员工作方式的快速指南

    30岁找不到工作很绝望 by Danielle Ormshaw 丹妮尔·欧姆肖(Danielle Ormshaw) 计算机为绝望的新编码员工作方式的快速指南 (The quick guide to t ...

  7. 开发日记-20190916 gradle新的依赖方式implementation,api,compileOnly

    转载于:gradle 新的依赖方式 implementation.api.compileOnly gradle升级之后,有了新的依赖方式,下面我来介绍一下他们的使用 先看看之前的 再看看现在的 区别和 ...

  8. MySQL5.7.12新密码登录方式及密码策略

    MySQL5.7.12新密码登录方式及密码策略 在Centos6.6上安装MySQL5.7.12时,遇到了一个问题 安装后在/root目录下没有发现有.mysql_secret这个文件,所以没有没法按 ...

  9. python中字符串的几种表达方式(用什么方式表示字符串)

    今天在学习python的基础的内容,学习在python中如何操作字符串,在此记录下. 主要是python中字符串的几种表达,表示方式. python的几种表达方式 1 使用单引号扩起来字符串 > ...

最新文章

  1. SQL Server使用侦听器IP访问时遇到The target principal name is incorrect. Cannot generate SSPI context...
  2. android焦距跟实际距离,手机等效焦距一算便知_nubia Z5S Mini_手机Android频道-中关村在线...
  3. 基于jQuery+ashx+.net实现三级栏目联动操作
  4. jQuery1.4新特性
  5. html制作花样链接卡页面_网站404页面怎么做
  6. springcloud工作笔记100---@PostConstruct注解的作用
  7. (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  8. 操作系统学习---进程
  9. OpenCV-图像处理(26、直方图反向投影(Back Projection))
  10. 数学之美 第二版 pdf
  11. ICESat2学习笔记9 :python读取ATL08数据
  12. FID - Web特征数据来了
  13. 什么是IPv6,IPv6有什么优势
  14. 线性代数————思维导图(上岸必备)(相似理论)
  15. 网络安全-使用PGP实现电子邮件安全
  16. GPT/GP2/GPT3
  17. numpy求矩阵特征值与特征向量
  18. 记忆里:小时候的农村青山绿水,鸟语花香,彩蝶飞飞
  19. 华为视频显示服务器异常,给予用户一份贴心|(数十份案例)APP异常界面与Toast产品设计落地...
  20. 一个应届生拿字节跳动offer的努力,整整用了一年的时间

热门文章

  1. 2017年总结--心情篇
  2. 蔡徐坤一条微博转发过亿 幕后推手被判刑五年
  3. 免费公共DNS的IP地址
  4. Nginx系列教材 (五)- 和Tomcat进行负载均衡
  5. 腾讯云短信服务(SMS)申请流程
  6. 小米4 miui专用 Xposed安装器86版
  7. 从Dijkstra谈帅才的洞察力
  8. Linux软件更新时遇到的各种问题以及解决办法
  9. 安装kali Linux到U盘
  10. 简单微信公众号支付,亲测好用!