Optaplanner规划引擎的工作原理及简单示例(1)

  在之前的文章中,老猿已介绍过APS及规划的相关内容,也对Optaplanner相关的概念和一些使用示例进行过介绍,接下来的文章中,我会自己做一个规划小程序 - 一个关于把任务分配到不同的机台上进行作来的小程序,并在这个小程序的基础上对Optaplanner中更多的概念,功能,及使用方法进行讲解。但在此之前,我需要先讲解一下Optaplanner在运行规则运算的原理。所以,本文是讲述一些关于寻找最优解的过程中的原理性的内容,作为后续通过示例深入讲解的基础。但这些原理知识不会涉及过分深奥的数学算法,毕竟我们的目标不是写一个新的规划引擎出来,只是理解一些概念,用于理解Optaplanner是依据什么找出一个相对优解的。好让在接下来的一系列文章中,可以快速无障碍地理解我所讲解的更细化的Optaplanner功能。

  好了,言归正传,本文主要是讲述Optaplanner是如何在用户定义的规则限制条件中,基于约束的限制,对被规划对象进行排列组合,再对比各个组合(称作解,或方案),并找出相对最优的解出来。在这个寻优过程中,Optaplanner会使用到一些相关算法,例如启发式算法(例如First Fit)和延迟接受法(例如禁忌搜索),从而提高寻找相对最优解的效率和防止嵌入局部最优解,从而可以在固定的时间内,找到尽可能优的方案。

  在理解Optapalnner是如何实现之前,我们先复习并展开一下上一篇提到的概念 - 约束。

约束(Constraint):

  也就是对事物的一种限制,规定事物的发展应该遵循什么规则,具体到Optaplanner里,就是用于表达出什么是对的,什么是错的,什么情况是最优,什么情况次优,什么情况较差。从而让引擎得到各个解的对比依据。

  在Optapalnner中的约束可以分为硬约束软约束两种,其实还有更多的约束类型 ,例如中间约束,甚至是无限层级的约束,但总结起来,其作用也就是把约束划分为不同层级,从而区分出不同的优等级而已,如果有软件开发经验的同学,可以理解不同层级的约束,分别是SQL语句里Order By子句后面的字段次序。在进行记录排序时,前面的字段排列的优先级,是从性质上优先于后面的字段的,大家理解了Order By子句,也就理解了不同层级约束的问题了。拉下来我们以最简单的软硬约束,来分析一下约束的作用。

硬约束:

  硬约束是用来规定什么情况是对的,什么情况是错的;什么组合是好的,什么组合是不好的......也就是它通常是用来对所得的解进行一些定性的状态定义。例如一个计划是否可行,例如会不会同一个机台同一个时间分配了两个不同的任务(假设每个机台同时只能做同一个任务)。一个员工所排班次是否正确(例如一个员工是否被安排了三个连续的班次)。若出现上种情况,即表示违反了硬约束,这种方案称作不可行方案。以后的文章里,会提到Optaplanner里有一个明确的概念 - Feasable Solution(可行方案,或称可行解),就是表示这个方案是完全符合硬约束的。

软约束:

  软约束规定什么情况最优,什么情况次优,什么情况是差的;它是用来定义方案优劣的定量状态。例如:一个计划的成本是否足够低;一个排班表到底有多大程度上的合理性,例如一个人正常情况下是需要5天工作制的,但如果遇到特殊情况,也可以连续工作6天,但这种情况是特殊的,需要额外付加班费(成本上升)最好不要出现这种情况。那么在编制这个排班表的时候,如果有一个方案是需要有人员连续工作6天,但如果找到另一个方案,可以令所有人均不需要连续工作6天,那么,后面这个方案就比那些有人需要连续工作6天的方案更好了。体现在软约束上,就是后面的排产表,其软约束上会比前一个排班表更好,违反的软约束更少。

  上述讲述的是两种常见约束,那么这些约束在Optaplanner里是如何生效的呢?那说需要有一种评分机制了,也是我们在使用Optaplanner里,比较难准确把握的一个内容之一。

评分机制:评分是用分数来评价事物特性的一种方法。但如果我们细心观察总结一下,会发现评份是可以通过两种方向来评价的;分别是正评分(奖励性评分)和负评分(惩罚性评分)。

正评分:通过获得分数的多少,来体现事物的优劣。例如我们在学校考试过程中,成绩是通过一种正分数来体现的,即做对一题奖励相应的分数,分数越高成绩越好;完美状态是获得满分。

负评分:通过扣除分数的多少,来体现事物的优劣。例如我们的驾驶证记分制,每违章一次就扣除相应的分数,很明显这种评份体系中,分数越低越好,也就是扣得越少越好;完美状态是扣0分。

  在对实际问题进行约束规划时,是一种封闭性约束,也就是约定事物往指定的一个方向发现,使用负评分的方式,很显然更合理。也就是一个方案有哪些不好的,我们通过对它评定一些惩罚分数标准,告诉引擎这种组合出现了一些不太好的情况。如此类推,每找到一个更佳、扣分更少的方案,就离完美就更近一步。无论是使用正方向评份还是反方向评分(或称负方向评分),在Optaplanner里都是可以实现的,只不过按我们日常的逻辑,在定义方案时,通常我们只会根据业务定义出一些规则,方案是需要守这些规则,当一个方案出现有违反规则时,就作出相应的惩罚性扣分;这种方法比当出现好的情况就加分更合理。因为我们的现实世界里,"好"是可能无限好的,当问题足够复杂,数据量足够大,即问题规模够大时,描述一个方案如何个好法,其实很难是一个定数。比描述一个方案如何个差法更难,因为前者可以是无限的,而后都就只需要我们定义好什么是差的标准,一但问题范围确定,它的最差情况(也就是最差的扣分情况)就有一个字数了。所以,在Optaplanner的世界里,常见的做法是,定义一些约束,并设定相应的惩罚分数标准(即将约束量化),用来描述这个方案的制约因素,当这个约束实打破时,就作出惩罚性记分,那么到最后,扣分越少的方案就越好。这就是Optaplanner实现寻优的最基本原理,但其实现是非常复杂的,会将问题划分为很多种类,将寻优的过程划分为多个阶段,每个阶段利用不同种类的算法来提高找到更优方案的效率,每个阶段有很多个步骤,每个步骤又有多个移动(没错,Optaplanner里就有Step与Move的概念,以后会详解);在以后的深入文章中,我会详细把这个过程分析出来。

  上面描述了硬约束、软约束和评份机制。那么如何将这两种约束与这种评分机制关联起来,令评分机制可以实现软、硬约束呢?大家可能已想到,在Optaplanner给出了软分数,硬分数的概念。在评分机制中,当出现一个方案违反了某个硬约束时,就给这个方案扣除这个约束相应的分数;同样地,当该方案违反了一种软约束时,就对该方案扣除该软约束相应的分数。这两个分数是分开处理的。因为通过它们对应的约束类别就知道,它们分别代表的性质不一样,硬分数对应的硬约束,代表的是一种定性评价;即描述方案好不好,行不行,可不可取等,一旦被记扣硬分数,那就表示这个方案的性质就变了,由可行方案变成不可行方案。理想的方案是一个硬分都不能扣的,一旦扣了就是不可行方案了。有人问,那么定义硬分数的分值有什么用?直接给一个标识出来,将方案的可用性定义为True or False,分别代表是事有硬约束被违反不就行了吗,多简单呀,因为一旦为False就是不可用了,再去讨论它扣了多少分,又有何意义呢?硬约束、硬分数不就是为了给方案定性而设立的吗?何必还要记录它的扣分量,多此一举呢?

  如果这样想,就是一种不全面的想法了。因为大家需要明白,现实世界往往是很大程度是不完美的,但而对不完美,我们是放弃这个世界,还是在不完美中进行坚持,对这个不完美的世界,朝完美的方向进行改造呢?上面的说法就比较抽象比较虚了,举个大家容易理解的例子。例如:刑法是用来惩罚犯罪的,在正常的法治社会中,犯罪对于一个人说,就相当于违反了硬约束(刑事处罚记录是终身跟随的)。也就是对于一个人来说,一生中是否触犯过刑法,是一个定性的问题。那么既然是定性问题,我们在设立刑法的时候,其对应的惩罚是不是只有一种就足够了呢?例如凡是触犯刑法,全部判死刑,那不就简单得多啦?事实上人类社会是不可能这样的,因为就算是触犯了刑法(这个已经是定性问题),但罪行也有轻重之分的、对应了刑法的不同条款,有些罪名经过对罪犯的惩戒,是可以再给他一次机会的,也说就是说触犯的刑法,是有轻重之分的,但性质不会变,他在国家司法机关的档案里,永远留有普被刑事处理的记录。所以,这可以称该种情况为定性范围内的定量问题。就是一个人做错了就是错了,其性质已经定了,但犯的错误有多大,还得是一个定量问题。因此,硬约束对应的扣除硬的分数有多有少就不难理解了。就是我们的方案如果出现了违反硬约束、被扣除了硬分数的,它在Optaplanner上就是一个不可行方案了。但是在众多的不可行方案里,其实还要区分哪个是更不可行,哪些其实只是违反了一点点,还是“稍为可行的”。回到我们的实际排程问题中,有可能客观条件限制,我们所有排出来的方案(例如生产计划、排班表、车辆调试线路图)都是不可行的,例如:我们排生产计划的时候,将交货期延误作为一种硬约束,但是现实的生产活动中,确确实实有可能无论你怎么排,因为产能、资源限制等因素,你是不可能找到一个完完全全符合交期的生产计划的,那么这个时间我们就需要找出一个违反得最小的计划出来,作为可行计划,视情况进行相应的修改并执行了。也就是说两害相遇取其轻。

  对于硬约束,除了上述讲到,当出现有可能确实需要使用不可行方案作为执行计划的情况外,在Optaplanner进行规则的过程中,其实也起到非常大作用的。先不说optaplanner引来来排程;如果让你来排,对于各种硬约束,全都不给出一个分数,而是给一个定性的标识,就是一旦出现违反了,就报一个违反硬约束的消息出来,你会怎么样?你肯定会抱怨提示的信息太简陋了,只有一个标识,最多只是知道哪里违反了,再也没有更详细的信息供你参考了。那你接下来的排产活动,其实就是一个组合一个组合逐一地去碰彩了。因为各个方案之间是否有关联,你是无法得知的,所以你根本找不到什么好的办法去将各种情况下的方案进行归类、比较进行往指定的一个方向收敛。但如果在一个硬约束被违反时,会出现一些明确的信息,是哪个硬约束被违反了。违反和程度是多少,扣了多少分,是因为哪个被规则的对象,放在哪里,或与哪个对象相邻从而导致的硬约束被违反。这样就形成了一个很明确指导方向,对于人而言,通过归纳统计就知道某些情况肯定会出现,或极大可能会出现违反硬约束的情况,那我们就可以在排列新方案时,尽力去避免这种情况了;也就是有了参考方向 。对于Optaplanner引擎来说也是同理,尽管它不像人这么聪明(但最从近的消息来看,Optapalnner团队已经着手思考人工智能引入到引擎中,从而实现如上述人类一样对这类问题进行归纳思考),但也能够作为其寻找更佳方案的过程中的一些很重要的参考,从而为寻优算法所用,进而提高寻优效率。例如遗传算法。

  软分数对应的软约束,代表的是一种定量评价;即描述方案有多好、有多差,成本有多高、有多低。它是一种优化约束,即在定义它的时候,就已经知道它必然是被违反的(也有可能完全不违反,那当然是好的,但如果是这样的话,就脱离了软约束的初充了)。所以,软件约束、软件分数的扣分值用途相对来说就容易理解得多了。

   综上所述,Optaplanner就是通过一种体现为分数的约束机制,进行寻找最优组合。当一个排产问题中,设定的软硬两种约束时,它会优先满足硬约束的要求,再满足软约束的要求,也就是说,软约束被扣为1万分,也不及硬约束被扣了1分重要,联系上面的SQL语句中的Order By子句的例子。

  Optaplanner其利用途径有以下两点:

1. 用分数来确定,一个方案是否可行,是优是劣;

2. 在决定每一步的时候,参考上一点的扣分情况,来确定下一次生成方法时,应该考虑哪此因素(想想遗传算法).

  这一篇我们先讲解一下原理,打一下基础,下一篇将用一个任务与机台的例子来说明一下这些原理在Optaplanner中是如何体现的。

原文地址:https://www.cnblogs.com/kentzhang/p/8947385.html

时间: 2024-10-31 02:34:17

Optaplanner规划引擎的工作原理及简单示例(1)的相关文章

JavaScript引擎的工作原理

(转) 1. 什么是JavaScript解析引擎? 简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序.比方说,当你写了 var a = 1 + 1; 这样一段代码,JavaScript引擎做的事情就是看懂(解析)你这段代码,并且将a的值变为2. 学过编译原理的人都知道,对于静态语言来说(如Java.C++.C),处理上述这些事情的叫编译器(Compiler),相应地对于JavaScript这样的动态语言则叫解释器(Interpre

如何去了解JavaScript引擎的工作原理

js:我们应该如何去了解JavaScript引擎的工作原理(转) http://www.nowamagic.net/librarys/veda/detail/1579 昨天收到一封来自深圳的一位前端童鞋的邮件,邮件内容如下(很抱歉,未经过他的允许,公开邮件内容,不过我相信其他人肯定也有同样的问题,所以,直接把问题原文抛出来): "读了你的几篇关于JS(变量对象.作用域.上下文.执行代码)的文章,我个人觉得有点抽象,难以深刻理解.我想请教下通过什么途径能够深入点的了解javascript解析引擎在

我们应该如何去了解JavaScript引擎的工作原理

我们应该如何去了解JavaScript引擎的工作原理 JavaScript探秘:编写可维护的代码的重要性 JavaScript探秘:谨慎使用全局变量 JavaScript探秘:var预解析与副作用 JavaScript探秘:for循环(for Loops) JavaScript探秘:for-in循环(for-in Loops) JavaScript探秘:Prototypes强大过头了 JavaScript探秘:eval()是"魔鬼" JavaScript探秘:用parseInt()进行

JAVA NIO工作原理及代码示例

简介:本文主要介绍了JAVA NIO中的Buffer, Channel, Selector的工作原理以及使用它们的若干注意事项,最后是利用它们实现服务器和客户端通信的代码实例. 欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. ByteBuffer 1.1直接缓冲区和非直接缓冲区 下面是创建ByteBuffer对象的几种方式 static ByteBuffer allocate(int capacity) static Byte

Keepalived工作原理(简单概述)

keepalived这项服务简单来说,就是来防止单点故障的. 所谓单点故障,再简单点来说就是老大挂了手下的小弟过来接班,老大复活了,继续当小弟,保证服务的高可用. 官方一点来说就是进行故障的切换转移,是通过VRRP虚拟路由器冗余协议来实现的,VRRP是通过一种竞选机制来将路由的任务交给某台VRRP路由器. 通常情况下,keepalived和LVS负载均衡软件一起出现,用来管理和监控整个集群的节点状态,这里就不详细介绍了. 看一下keepalived的配置文件,以及各个模块的作用功能 /etc/k

线程池的工作原理及使用示例

欢迎探讨,如有错误敬请指正 如需转载,请注明出处  http://www.cnblogs.com/nullzx/ 1. 为什么要使用线程池? 我们现在考虑最简单的服务器工作模型:服务器每当接收到一个客户端请求时就创建一个线程为其服务.这种模式理论上可以工作的很好,但实际上会存在一些缺陷,服务器应用程序中经常出现的情况是单个客户端请求处理的任务很简单但客户端的数目却是巨大的,因此服务器在创建和销毁线程所花费的时间和系统资源可能比处理客户端请求处理的任务花费的时间和资源更多. 线程池技术就是为了解决

Java并发包中CountDownLatch的工作原理、使用示例

1. CountDownLatch的介绍 CountDownLatch是一个同步工具,它主要用线程执行之间的协作.CountDownLatch 的作用和 Thread.join() 方法类似,让一些线程阻塞直到另一些线程完成一系列操作后才被唤醒.在直接创建线程的年代(Java 5.0 之前),我们可以使用 Thread.join().在线程池出现后,因为线程池中的线程不能直接被引用,所以就必须使用 CountDownLatch 了. CountDownLatch主要有两个方法,当一个或多个线程调

Java并发包中CyclicBarrier的工作原理、使用示例

1. CyclicBarrier的介绍与源码分析 CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活.线程进入屏障通过CyclicBarrier的await()方法. CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方

ScheduleThreadPoolExecutor的工作原理与使用示例

欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. ScheduleExecutorService接口.ScheduledFuture接口 从图中可以看出ScheduledExecutorService接口继承了ExecutorService接口,同时还添加了有关提交定时任务的四个方法. public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUn