【发布时间】:2014-09-21 08:46:01
【问题描述】:
我目前正在研究一种工作轮班调度算法。我们的轮班时间表完全包括 4-3(4 天上,3 天休息)和 4-3 轮班(例如:周日、周一、周二、下一周和下周周日、周五、周六休息) ) - 星期从星期日到星期六。
“直线”4-3 移位或旋转 4-3 有 49 种可能的变体。由于它是 24/7 操作,因此需要考虑一周中的所有 7 天。截至目前,我将其用于一个较小的工作组,其中有 11 人在早班,12 人在晚班,但还有其他多达 200 人的工作组,我想将此算法扩展到其他工作组。
基本前提是管理组每天都有所需的人力,算法将只返回一组早班和晚班,为他们提供所需的人力。
很快就变得非常痛苦,为 11 个人安排 49 次可能的班次(有重复)并整理所有这些可能的组合需要数千年的时间。结果,我能够从 49 个班次列表中挑选出最常用的 16-20 个班次。
这使它易于管理,但仅适用于 11 或 12 人。显然,每增加一个人,可能的组合数量就会呈指数增长。截至目前,我的算法执行以下操作:
-
作为变量,我有一堆班次,其中 1 和 0 代表他们在那里的一周中的每一天的“班次”和“班次” - 超过两周的时间。我现在只使用一个示例......例如:
h = [0,1,1,1,1,0,0,0,1,1,1,1,0,0] e = [0,0,1,1,1,1,0,0,0,1,1,1,1,0] d = [0,0,0,1,1,1,1,0,0,0,1,1,1,1] c = [1,1,1,1,0,0,0,1,1,1,1,0,0,0] m = [0,1,1,0,1,1,0,0,1,1,0,1,1,0] p = [1,1,0,0,0,1,1,1,1,0,0,0,1,1] q1 = [1,1,1,0,0,0,1,1,1,1,0,0,0,1] a = [1,0,0,0,1,1,1,1,0,0,0,1,1,1]
然后我有一个列表,其中包含大约 16 到 20 个(在此示例中我将仅使用 8 个)班次,这些班次是最常用的(如果不是专门使用的话)加上一个用于计算人数的变量和经理要求的变量(early_count):
shifts = [h,e,c,d,m,p,q1, a]
early_bid_personnel = 11
early_count = [5,6,7,7,6,8,5]
然后我有一个生成器表达式为早班创建所有可能的班次组合,并查看星期日加起来是否达到所需的数字 (5)。那些在星期日加起来的然后被 mon 生成器表达式引用,所有这些星期一被制成表格,然后被 Tue 列表引用。我在 14 天的时间里这样做 - 因为一些轮班在第二周扭曲了人员平衡:
sun = (combs for combs in combinations_with_replacement(shifts,early_bid_personnel) if (sum(combs[i][0] for i in range(0,early_bid_personnel)) == early_count[0]))
mon = (mon for mon in sun if (sum(mon[i][1] for i in range(0,early_bid_personnel)) == early_count[1]))
tue = (tue for tue in mon if (sum(tue[i][2] for i in range(0,early_bid_personnel)) == early_count[2]))
wed = (wed for wed in tue if (sum(wed[i][3] for i in range(0,early_bid_personnel)) == early_count[3]))
thu = (thu for thu in wed if (sum(thu[i][4] for i in range(0,early_bid_personnel)) == early_count[4]))
fri = (fri for fri in thu if (sum(fri[i][5] for i in range(0,early_bid_personnel)) == early_count[5]))
sat = (sat for sat in fri if (sum(sat[i][6] for i in range(0,early_bid_personnel)) == early_count[6]))
sec_sun = (sec_sun for sec_sun in sat if (sum(sec_sun[i][7] for i in range(0,early_bid_personnel)) == early_count[0]))
sec_mon = (sec_mon for sec_mon in sec_sun if (sum(sec_mon[i][8] for i in range(0,early_bid_personnel)) == early_count[1]))
sec_tue = (sec_tue for sec_tue in sec_mon if (sum(sec_tue[i][9] for i in range(0,early_bid_personnel)) == early_count[2]))
sec_wed = (sec_wed for sec_wed in sec_tue if (sum(sec_wed[i][10] for i in range(0,early_bid_personnel)) == early_count[3]))
sec_thu = (sec_thu for sec_thu in sec_wed if (sum(sec_thu[i][11] for i in range(0,early_bid_personnel)) == early_count[4]))
sec_fri = (sec_fri for sec_fri in sec_thu if (sum(sec_fri[i][12] for i in range(0,early_bid_personnel)) == early_count[5]))
sec_sat = (sec_sat for sec_sat in sec_fri if (sum(sec_sat[i][13] for i in range(0,early_bid_personnel)) == early_count[6]))
我遍历的 sec_sat 表达式,在自定义字典中查找 1 和 0 的字符串,并将其转换为班次分配的实际字母。然后我基本上为晚班再次做同样的事情。两者结合起来为经理提供了他们正在寻找的确切数字。这很好用,例如,11 个人只有 8 个班次可供选择,而 12 个人可以选择 8 个班次延迟。但是,如果工作组的规模扩大到 20 人,我想用 12、14、16 或 gasp 所有 49 个班次来确定班次,这仍然是相当不合理的.
我知道第一个生成器表达式仍在使用替换检查每个组合,并且只返回周日加起来的组合,这是核心问题。我可以认真地使用一些帮助来找出一种比 O(n^2) 更好或更糟的方法。
我周围有什么方法可以生成所有可能的组合并检查每个组合吗?另外,如果我在初始生成器表达式中加入一些约束,例如最多只有 5 个 'a' 移位:
sun = (combs for combs in combinations_with_replacement(shifts,early_bid_personnel) if (sum(combs[i][0] for i in range(0,early_bid_personnel)) == early_count[0]) and combs.count(a) <= 5)
生成器表达式仍然必须生成一些东西,检查它是否有 5 个或更少的 'a' 班次,如果有更多则跳过它,对吗?
【问题讨论】:
标签: python algorithm optimization