1. 背景:

复杂的排班规则:
rule1: 员工每天工作时间不能超过8 小时
rule2: 员工连续工作时间不能超过2小时
rule3: 员工连续工作天数不能大于 5 天
rule4: 部门人员薪资要尽可能的最低
rule5: 员工总工作时长要尽可能的低
rule6: 员工每天排的班次数不能超过 3 次
rulen: 其他能想到的规则
复杂的班次规则
rule7: 单个班次时长不能小于 2 小时

假设有A、B、C 三个部门,分别有员工 :
A1、A2、…… An
B1、B2、…… Bn
C1、C2、…… Cn
其中
A 部门的员工,需要尽可能满足 rule 1、rule 2、rule 3、rule 6、必需满足 rule1
B 部门的员工,需要尽可能满足 rule 1、rule 3、rule 5、rule 6、必需满足 rule 2
C 部门的员工,需要尽可能满足 rule3、rule 5、必需满足 rule 4以及未知规则 rule <?>
C部门的员工 C2、C4 , 需要多一个 rule 1,因为他们年纪大了

  1. 问题分析:

对于以上问题,想要找到一个解决方案, 需要我们为每一个人,去匹配每一个规则,这个本质属于一个求组合的过程(有可能因为需求的调整演变成排列问题,那将会更复杂,暂时不升级),要求在得到所有的可能组合后,再从其中找出一个可行且否最优方案, 并且每一个人的每一次匹配的变化,都有可能对整体排班方案产生全局影响, 这个影响有可能是好的也可能让方案变得更差,假设按基于24小时每天我们设计了40 种工作时段(也有可能会更多),假设 A 部门有20 个员工, 然后我们要给 A 部门的员工排班, 那么至少有有C(40,20)种组合方案,再加上 rule1 的存在, 这种组合方案数又变得不固定了。

在有限的时间内,如果找到最好的方案变成了一个无法解的问题, 我们识别其为 NP 不完全问题。
即:在有限的时间内, 我们无法从海量的可能中挑出最优的那个。

  1. 解决方案:

面对 NP 不完全问题我们只能找到其近似解。在诸多可能的解决方案中, 我们选择了两阶段寻优算法作为解决方案,即:启发式(FirstFit) + 寻优(禁忌算法)
一阶段启发式,我们使用了 FirstFit 的方式,迅速迭代以求找到一个初步可行解。它至少是一个保底的可行解决方案;
二阶段寻优,使用禁忌算法来对一阶段的结果进行优化。

  1. 禁忌算法介绍:

邻域
对于组合优化问题,给定任意可行解x,x∈D,D是决策变量的定义域,对于D上的一个映射:N:x∈D→N(x)∈2(D) 其中2(D)表示D的所有子集组成的集合,N(x)成为x的一个邻域,y∈N(x)称为x的一个邻居。

候选集合
候选集合一般由邻域中的邻居组成,可以将某解的所有邻居作为候选集合,也可以通过最优提取,也可以随机提取,例如某一问题的初始解是[1,2,3],若通过两两交换法则生成候选集合,则可以是[1,3,2],[2,1,3],[3,2,1]中的一个或几个。

禁忌表
禁忌表包括禁忌对象和禁忌长度。由于在每次对当前解的搜索中,需要避免一些重复的步骤,因此将某些元素放入禁忌表中,这些元素在下次搜索时将不会被考虑,这些被禁止搜索的元素就是禁忌对象;
禁忌长度则是禁忌表所能接受的最多禁忌对象的数量,若设置的太多则可能会造成耗时较长或者算法停止,若太少则会造成重复搜索。

评价函数
用来评价当前解的好坏,TSP问题中是总旅程距离。

特赦规则
禁忌搜索算法中,迭代的某一步会出现候选集的某一个元素被禁止搜索,但是若解禁该元素,则会使评价函数有所改善,因此我们需要设置一个特赦规则,当满足该条件时该元素从禁忌表中跳出。

终止规则
一般当两次迭代得到的局部最优解不再变化,或者两次最优解的评价函数差别不大,或者迭代n次之后停止迭代,通常选择第三种方法。

禁忌算法的核心思想最精髓的两个点: 一为:迭代: 计算一阶段的结果得分 score, 参考以下伪码思路进行迭代:

Java开发每天值班表功能_迭代

我们要给迭代设置一个终止条件,这个条件可以是时间, 也可以是迭代次数。由于计分制度,能够保证每次得到的可行结果一定不会比之前的更差 ,所以这个过程我们理解为:寻优

二为:禁忌表
因为每次 move 都是随机挑选,所以非常有可能会遇到相同的 move,为了避免重复无效的 move 运算,禁忌算法加入了禁忌表(tabuList )的概念, 即:对于遇到已经 move 过的,我们给它缓存起来,再遇到的时候就丢弃掉。
我们下个定义叫《禁忌元素》,比如 把班次 123 匹配给员工 A1,下次随机时又遇到了把班次 123匹配给 A1, 就属于重复运算了, 我们给它记录下来加入到禁忌表,再遇到就不算它,称这个加入禁忌表的组合为《禁忌元素》。
接下来我们做个假设,假设有一个方案:
planok = “把班次 28 匹配给员工 A2” + “把班次 123”匹配给员工 A1" ,是一个最佳组合方案,
但是按照之前的操作, “班次 123 + 员工A1” 还在禁忌表里呢, 我们是得不到这个方案的因为一旦尝试把 “班次 123” 排给 “员工A1” 的时候,就会被禁忌表拦住。
所以禁忌算法的第二个核心思想是:禁忌表的退化,即每个《禁忌元素》都有一个计数,这个计数我们给它一个初始值 n (n 一般按经验取,在实际业务中这个参数可以通过机器学习得到),每次 move 遇到相同的《禁忌元素》时, 就给这个《禁忌元素》的 n 减1 直到 n 变成0, 而一旦 n = 0 了, 这个《禁忌元素》就又被拿出来又能被参与寻优了, 这样我们就又有了得到 planok 的机会。

  1. 小结

以上讲了智能排班的背景, 结合案例解释了我们如何通过两阶段算法实现一个排班的寻优