Optimization 或工具员工计划顺序分配员工组

Optimization 或工具员工计划顺序分配员工组,optimization,or-tools,Optimization,Or Tools,使用标准/简单护理示例: 我试图强制某个特定组/团队的护士连续分配几天。 如下所示,1-9名护士分为“组”。如果首先分配9号护士,则第4组和第7组的其他成员应分配到接下来的几天 我认为我可以通过计算一组成员每天被分配的次数来实现这一点,但我无法计算何时发生,即第I天和第I+1天都分配给同一组的护士 groups=[[3,7,8]、[1,6]、[5]、[4,9,7]]#护士3,7,8在同一组 ... 对于g组: 对于zip中的d1、d2(所有天数[:-1],所有天数[1:]): d1_alloc

使用标准/简单护理示例:

我试图强制某个特定组/团队的护士连续分配几天。 如下所示,1-9名护士分为“组”。如果首先分配9号护士,则第4组和第7组的其他成员应分配到接下来的几天

我认为我可以通过计算一组成员每天被分配的次数来实现这一点,但我无法计算何时发生,即第I天和第I+1天都分配给同一组的护士

groups=[[3,7,8]、[1,6]、[5]、[4,9,7]]#护士3,7,8在同一组
...
对于g组:
对于zip中的d1、d2(所有天数[:-1],所有天数[1:]):
d1_alloc=n in g的总和(移位[(n,d1,s)])
d2_alloc=n in g的总和(移位[(n,d2,s)])
# ??? 当两个总和都为1/真时,如何说仅计数???
#对于一组3,即[3,7,8],这应在一段时间内发生两次
完整代码:

来自ortools.sat.python导入cp_模型
所有天数=范围(1,10)
所有护士=范围(1,10)
组=[[3,7,8],[1,6],[5],[4,9,7]。#护士3,7,8在同一组
s=1#仅1班
model=cp_model.CpModel()
移位={}
对于所有天的d:
对于所有护士中的n:
移位[(n,d,s)]=model.NewBoolVar('shift_n%sd%是%i'(n,d,s))
#每班一名护士
对于所有天的d:
模型.相加(所有护士中n的总和(移位[(n,d,s)])=1)
#每个人都轮班工作
对于所有护士中的n:
模型添加(所有天数内d的总(班次[(n,d,s)])=1)
#小组内的护士应连续几天分配
#组的顺序并不重要-最后一组[4,9,7]可以分配为第一组
#组内的顺序不重要-可以是7,4,9
对于g组:
对于zip中的d1、d2(所有天数[:-1],所有天数[1:]):
d1_alloc=n in g的总和(移位[(n,d1,s)])
d2_alloc=n in g的总和(移位[(n,d2,s)])
# ??? 当两个总和都为1/true时,如何说仅计数???
#对于一组3,即[3,7,8],这应在一段时间内发生两次
solver=cp_model.CpSolver()
解算器。解算(模型)
对于所有天的d:
对于所有护士中的n:
如果解算器值(移位[(n,d,s)])==1:
打印('Day:'+str(d)+'=护士'+str(n))
编辑: 可以使用以下逻辑来实现这一点

对于组中的组:
对于zip中的n1、n2(组[:-1],组[1:]):
对于d,在所有天内[:-1]:
model.AddBoolOr([shift[n1,d,1],shift[n2,d+1,1].Not())
model.AddBoolOr([shifts[n1,d,1].Not(),shifts[n2,d+1,1]]))
此解决方案具有限制性-分配必须遵循与列出的组相同的顺序。组[3,7,8]将始终是3,7,8,但不是7,3,8或8,3,7,例如。。。那也可以

还必须确保第一天从小组开始就分配给某个人。 模型.Add(对于[3,1,5,4]中的n,求和(移位[(n,1,s)]==1)

如果只有最多2名成员的组,则以下命令将允许任何一种顺序。例如,对于[3,7]组。。。3,7或7,3

对于组中的组:
对于zip中的n1、n2(组[:-1],组[1:]):
#第1天
模型.加法(移位[n1,1,1],移位[n2,2,1])
模型.加法(移位[n2,1,1],移位[n1,2,1])
#第2天+必须检查前一天,以避免循环/重复分配
对于d,在所有天[1:-1]:
模型.AddImplication(移位[n1,d,1],移位[n2,d+1,1])。仅限移位[n2,d-1,1])。非()
模型.AddImplication(移位[n2,d,1],移位[n1,d+1,1])。仅限移位[n1,d-1,1])。非()
编辑2: 以下可用于求解任何大小的组

对于分组中的g:
对于范围(0,len(g))内的i:
#每个群序[3,7,2],[7,2,3],[2,3,7]有1个循环
对于d,在所有天内[:-(len(g)-1)]:
满足条件=[班次[g[0],d,1].\n1今天分配
如果d>1:
#确保前一天未分配组成员
对于n in g:
满足条件。\u.append(移位[n,d-1,1].Not())
#为接下来的x天应用规则-取决于组的大小
对于范围(d+1,d+len(g))内的一天:
或_cond=[]
对于g[1:]中的n:
或第二次追加(班次[n,天,1])
model.AddBoolOr(或cond).OnlyEnforceIf(满足条件)
x=g.pop(0)
g、 附加(x)

有关更复杂的约束,我建议查看


特别是中包含最小和最大序列约束。

谢谢Laurent。我确实看到了这个例子,但我认为可能有一个更简单的方法来实现这一点。你能告诉我是否可以做以下事情吗:-如果d1\u alloc是真的,那么-Max(0,(d1_alloc+d2_alloc-1))#假设如果d1和d2都为真,则只返回1,我将花一些时间来查看提供的示例,看看是否可以复制。再次感谢,你好,劳伦特。我能够使用与您提供的示例类似的逻辑来解决我的问题。(参见上面的代码编辑)。解决方案有点限制,但是,组的顺序必须保持有序。对于[3,7,8]组,如果护士3工作,那么第二天护士7工作,第二天护士8工作。实际上,不需要遵循这个顺序-例如,7,3,8也可以。你能推荐一种使此解决方案更灵活的方法吗?全天使用暗示(工作[7][day]=>work[3][day+1])感谢快速回复Laurent。我做了测试,如果小组最多只有2名成员,则暗示效果会很好,请参见上面的编辑。以[3,7]为例。。。如果第1天=第3天,则第2天=第7天,反之亦然。唯一的forceif用于停止一个重复循环(3,7,3,7…),但如果一个组中有3个[3,7,8],我不得不说