使用Python为工作人员创建自动计划
我正试图写一个程序,它会列出一个工作人员和需要填补的时间段的列表,以便为每个工作人员制定一个方便的时间表,并填补所有的时间段。每个员工都有一个最大轮班数(请注意,该员工不必连续轮班),以及一个他们可以填补的空缺列表。如果未填充所有插槽,程序将返回使用Python为工作人员创建自动计划,python,Python,我正试图写一个程序,它会列出一个工作人员和需要填补的时间段的列表,以便为每个工作人员制定一个方便的时间表,并填补所有的时间段。每个员工都有一个最大轮班数(请注意,该员工不必连续轮班),以及一个他们可以填补的空缺列表。如果未填充所有插槽,程序将返回False,否则将生成分配给各个插槽的工作人员列表 数据集和预期结果的模拟示例: 我试着从为工人创建一个类开始,该类由(name、max\u shift、avail\u slots)初始化,然后是一些getter方法 Udon = ('Udon', 1,
False
,否则将生成分配给各个插槽的工作人员列表
数据集和预期结果的模拟示例:
我试着从为工人创建一个类开始,该类由(name、max\u shift、avail\u slots)初始化,然后是一些getter方法
Udon = ('Udon', 1, [3,4])
Ramen = ('Ramen', 1, [2])
Soba = ('Soba' , 2, [1,3])
Noodle-workers = [Soba, Ramen, Udon]
Slots = [1, 2, 3, 4]
Schedule(Noodle-workers, Slots)
预计这将再次出现:
乌冬-4
Soba-3
拉面-2
Soba-1
它们可以都在列表或字典中,每一行可以是一个元组。在结果如何呈现上有创造性的杠杆作用。但是,如果插槽未填满,则函数返回False
测试程序可伸缩性的另一个数据集如下:
Erika = Worker("Erika", 1, [1, 3, 7, 9])
Ryan = Worker("Ryan", 1, [1, 8, 10])
Reece = Worker("Reece", 1, [5, 6])
Gordon = Worker("Gordon", 2, [2, 3, 9])
David = Worker("David", 2, [2, 8, 9])
Katie = Worker("Katie", 1, [4, 6])
Aashish= Worker("Aashish", 2, [1, 10])
Grant = Worker("Grant", 2, [1, 11])
Raeanne= Worker("Raeanne", 2, [1, 11, 12])
Erin = Worker("Erin", 1, [4])
Alex = Worker("Alex", 1, [7])
Workers = [Erika, Ryan, Reece, Gordon, David, Katie, Aashish, Grant,
Raeanne, Erin]
SLOTS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
将Erin(工人[9])与工人列表中的Alex切换,以获得所有插槽的完整时间表
我的主要问题是分解概念来解决问题。我想到了一种树状结构,为工人生成所有可能的任务组合,然后消除那些不满足最大轮班数的任务组合。这对我来说也是个问题,因为我需要在两个有资格获得一个职位的工人之间进行选择。此外,我感觉生成式递归可能会解决这个问题,但我不知道如何用伴随的结构实现它来产生结果
无论如何,如果这个问题能够得到解决,以及解决它的概念,我将非常感激。考虑一个树,其中节点本身就是分配问题(“工作列表”/“插槽列表”对),边是单独的分配(“将Udon放在插槽3上”) 在这里,工人不仅仅是一个被命名的人,而是一个包含相关工作信息的人:剩余班次、可用时间 这是如何开始的: 然后可以进行深度优先搜索:
class Worker:
def __init__(self, name, shifts, slots):
self.name = name
self.shifts = shifts
self.slots = slots
def copy(self):
return Worker(self.name, self.shifts, self.slots[:])
def assign(self, slot):
assert slot in self.slots
self.shifts -= 1
self.slots.remove(slot)
def Schedule(team, slots):
if slots == []:
return {}
for worker in team:
if worker.shifts > 0:
for slot in worker.slots:
if slot in slots:
wcp = worker.copy()
new_team = [w if w != worker else wcp for w in team]
wcp.assign(slot)
assignments = Schedule(new_team, [s for s in slots if s != slot])
if assignments is not None:
assignments[slot] = worker.name
return assignments
return None
Udon = Worker('Udon', 1, [3,4])
Ramen = Worker('Ramen', 1, [2])
Soba = Worker('Soba' , 2, [1,3])
Noodle_workers = [Soba, Ramen, Udon]
Slots = [1, 2, 3, 4]
print(Schedule(Noodle_workers, Slots))
更新。一些解释:
- 列表理解:我使用和(可能)滥用它们,它们使创建列表更快。请注意,
关键字位于不同的位置,因为每行使用不同的机制:if
:简单的列表理解([w if w!=团队中w的worker else wcp]
)与三元运算符([expr(i)for i in…]
)a if b else c
:条件列表理解([s表示插槽中的s如果s!=slot]
[expr(i)表示插槽中的i如果]
- 扩展:建议的实现使用递归,其深度将等于要分配的插槽数量。如果该数量很高,这可能是一个问题
:在开始之前,我将更详细地解释一下树模型新团队的目的
- 起始问题是通过给出一组工人和他们需要分配到的插槽列表来定义的
- 每个工人都有自己的名字,他能轮班的次数,他可以工作的时间列表
- 当一名工人被分配到某个时段时,我们必须更新他仍然可以工作的班次数以及他仍然可以工作的时段列表。这在
方法中发生:assign
我们还将从要分配的插槽列表中删除所选插槽self.shifts -= 1 self.slots.remove(slot)
- 这就引出了一个新的分配问题:如何将剩余的工人分配到剩余的插槽?第一个工人的每个选择都会导致一个不同的新分配问题。新分配问题是树结构中前一个问题的子节点
- 在深度优先搜索期间,如果一次搜索失败,我们将不得不在树中后退,继续循环父节点中可能的分配
Worker
对象列表(或OP代码中的列表)和插槽列表组成。在python中,列表和对象通过引用(指针)传递。如果在深度优先搜索的给定步骤中,修改了Worker
对象(因为一旦分配了Worker,就必须修改),当我们爬回树上探索其他分支时,此对象保持修改状态,这会破坏代码。为了避免这种情况,我们为要分配的每个工作人员创建一个副本:
wcp = worker.copy()
并在修改后将此副本传递给子节点。这样做可以保证返回到该节点时不会损坏任何数据
插槽列表也会出现同样的问题:如果我们在使用slot.remove(slot)
删除插槽后,将slot
传递给子节点,并且如果一些子节点也这样做,那么列表slot
将被破坏,如果我们这样做,列表团队
也会被破坏
这可以通过向子节点传递一个新的工作人员列表和一个新的插槽列表来解决。Hi@christophrings我真的很高兴向您展示我所做的工作。正如我承诺的那样,我研究了您的代码,在测试和调试过程中,我注意到根据时间长度对团队中的工作人员进行排序非常重要e插槽列表(即Worker.slots,但是我使用getters so Worker.get_slots())。排序非常重要的原因是,就像Erika有很多可能的插槽一样,一旦她被分配到一个插槽,她将无法填充一个关键插槽。因此,先填充选项较少的插槽更好,然后那些更灵活的插槽可以填充剩余的插槽。这可能只是我的系统
class Worker(object):
def __init__ (self, name, shifts, slots):
self.name = name
self.shifts = shifts
self.slots = slots # Slot is a list of available slots a TA can fill
def get_name(self):
return self.name
def get_shifts(self):
return self.shifts
def get_slots(self):
return self.slots
def copy(self):
return Worker(self.name, self.shifts, self.slots[:])
def assign(self, slot):
assert slot in self.slots
self.slots.remove(slot)
self.shifts -= 1
def __str__ (self):
return self.name + ', max-shifts = ' + str(self.shifts) + ', slots = ' + '[' + ','.join(str(e) for e in self.slots) +']'
def merge(left, right):
''' Helper function for the merge_sort function that follows'''
result = []
i,j = 0,0
while i < len(left) and j < len(right):
# This is the criterion merge_sort uses to sort the list argument
# in this case I'll be sorting according to the length of list of slots for each Worker
if len(left[i].get_slots()) < len(right[j].get_slots()):
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
while i < len(left):
result.append(left[i])
i += 1
while j < len(right):
result.append(right[j])
j += 1
return result
def merge_sort(L):
''' This sorts any List according to the condition in the func above'''
if len(L) < 2:
return L[:]
else:
middle = len(L)//2
left = merge_sort(L[:middle])
right = merge_sort(L[middle:])
return merge(left, right)
def poss_assignments(team, slots):
"""
creates a list of possible assignments for every available slot
team - List of Workers
slots - List of positive integer (slots)
return - Dictionary with slots as keys and a list of Workers as values
"""
poss_dict = {}
poss_slots = slots[:]
for i in poss_slots:
val_list = []
poss_dict[i] = val_list
for t in team:
if i in t.get_slots():
poss_dict[i] += [t] #replace with [t.get_name()]: Use this to see the names this func produces
return poss_dict
def Schedule(team, slots):
team = merge_sort(team)
if slots == []:
return {}
elif team == [] and slots != []:
return print('No Workers to assign')
possible_assignments = poss_assignments(team,slots) # A dictionary of slots with list of workers open for that slot
accu_slot = slots[:]
i = 0
for slot in accu_slot:
if slot in possible_assignments.keys():
while i < len(possible_assignments[slot]):
worker = possible_assignments[slot][i]
wcp = worker.copy()
wcp.assign(slot)
new_slots = [s for s in slots if s != slot]
new_team = [w if w != worker else wcp for w in team]
assignment = Schedule(new_team, new_slots)
if assignment is not 'Schedule unattainable.':
assignment[slot] = wcp.get_name()
return assignment
else:
i += 1
else:
break
return 'Schedule unattainable.'
#Udon = Worker('Udon', 1, [3,4])
#Ramen = Worker('Ramen', 1, [2])
#Soba = Worker('Soba' , 2, [1,3])
#
#Noodle_workers = [Soba, Ramen, Udon]
#Slots = [1, 2, 3, 4]
#==============================================================================
Erika = Worker("Erika", 1, [1, 3, 7, 9])
Ryan = Worker("Ryan", 1, [1, 8, 10])
Reece = Worker("Reece", 2, [5, 6])
Gordon = Worker("Gordon", 2, [2, 3, 9])
David = Worker("David", 2, [2, 8, 9])
Katie = Worker("Katie", 1, [4, 6])
Aashish= Worker("Aashish", 2, [1, 10])
Grant = Worker("Grant", 2, [1, 11])
Raeanne= Worker("Raeanne", 2, [1, 11, 12])
Erin = Worker("Erin", 1, [4])
Alex = Worker("Alex", 1, [7])
Workers = [Erika, David, Grant, Raeanne, Ryan, Reece, Gordon, Katie, Aashish]
Slots = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
#print(Schedule(Workers, Slots))
#print(Schedule(Noodle_workers, Slots))