使用Python

时间:2017-11-17 19:16:04

标签: python

我正在尝试编写一个程序,该程序需要填写工作人员和插槽列表,以便为每个工作人员提供方便并填充所有插槽的计划。每个工人都有最大班次(注意工人不必连续工作),以及他们可以填写的插槽列表。如果未填充所有插槽,程序将返回False,否则它将生成分配给各个插槽的工作人员列表。

数据集和预期结果的模拟示例:

我尝试通过为(name,max_shifts,avail_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   荞麦面 - 3   拉面 - 2   荞麦面 - 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(Workers [9])与Alex列在工作人员列表中,以获得所有老虎机的完整时间表

我的主要问题是打破概念来解决问题。我想到了树状结构,为工人生成所有可能的分配组合,然后消除那些不满足最大班次的组合。在两名有资格获得职位的工人之间进行选择的情况下,这对我来说也存在问题。此外,我感觉生成递归可能会解决问题,但我对如何用附带的结构实现它来产生结果感到茫然。

无论如何,如果能解决这个问题并且解决它的概念,我将非常感激。

2 个答案:

答案 0 :(得分:1)

考虑一个树,其中节点本身就是分配问题(工作人员列表' /'插槽列表对),边缘是个人分配("放乌冬面)在插槽3和#34;)。

在这里,工人不仅仅是一个有名的人,还有一个人和相关的工作信息:剩下的班次,可用的插槽。

这是如何开始的:

noodle tree

然后可以进行深度优先搜索:

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 != worker else wcp for w in team]:简单列表理解([expr(i) for i in ...])与三元运算符(a if b else c

    • 相结合
    • [s for s in slots if s != slot]:条件列表理解([expr(i) for i in ... if <condition>]

  • 扩展:建议的实现使用递归,递归的深度将等于要分配的槽的数量。如果这个数字很高,这可能是个问题。

  • new_team的目的:在开始之前,我将首先详细解释树模型。

    • 通过提供一个工作池和一个需要分配给它们的插槽列表来定义启动问题。

    • 每个工作人员的名字,特征的名称,他可以换班的数量,他可用的插槽列表。

    • 当工作人员被分配到一个插槽时,我们必须更新他仍然可以工作的班次数,以及他仍然可用的插槽列表。这种情况发生在assign方法中:

      self.shifts -= 1
      self.slots.remove(slot)
      

      我们还从要分配的广告位列表中删除所选的广告位。

    • 这会导致新的分配问题:如何将剩余的工作人员分配给剩余的工作空间?第一个工人的每一个选择都会导致不同的新任务问题。新的赋值问题是我们树结构中上一个问题的子节点。

    • 在深度优先搜索期间,如果一次搜索不成功,我们将不得不在树中重新开始,继续循环父节点中的可能分配。

现在请注意,问题包括Worker个对象列表(或OP代码中的列表)和一个插槽列表。在python中,列表和对象通过引用(指针)传递。如果在我们深度优先搜索的给定步骤中,修改了Worker对象(因为它必须被指定为该工作者),当我们爬回树以探索时,此对象会保持修改其他分支,这将打破代码。为避免这种情况,我们创建了要分配的每个工作人员的副本:

                wcp = worker.copy()

并在修改后将此副本传递给子节点。这样做,我们保证在返回该节点时不会损坏任何数据。

对于插槽列表会出现同样的问题:如果我们将slots传递给子节点,在使用slots.remove(slot)删除插槽后,如果某些子节点执行相同操作,则如果我们做同样的话,列表slots最终会被破坏,列表team也会被破坏。

这可以通过将新的工作列表和新的插槽列表传递给子节点来解决。

答案 1 :(得分:0)

您好@ChristophFrings我很高兴向您展示我所做的一切。正如我所承诺的那样,我研究了你的代码,在测试和调试的过程中,我发现根据插槽列表的长度对团队中的工作人员进行排序非常重要(例如,Worker.slots,但我使用getter,所以Worker.get_slots())。排序的原因非常重要,就像Erika有许多可能的插槽一样,一旦她被分配到一个插槽,她将无法填补线下的关键插槽。因此,首先关闭那些选项较少的选项会更好,然后那些更灵活的选项可以填补剩余的选项。它可能只是我的系统(2GB RAM,intel duo core),但运行你的初始代码没有那种感觉就像一个无限循环,特别是当我输入打印语句以更好地理解你的工作时。

此外,我观察到假条件(即绝对无法达到时间表)在初始代码中进入无限循环。也许它适用于你的系统,但我在你的上一篇文章之后尝试了它,并且代码仍在运行30分钟到一个小时,所以我试着调整它以确保当满足False条件时代码结束。我通过增加一个基本条件和一个休息条件来做到这一点。

我不能够感谢你,因为我觉得我从你的代码中学到了很多东西。以下代码的概念受到您在许多方面的工作的启发。主要区别在于增加了基本情况,采用了我最近在edx上的在线课程中学习的排序功能,最后使用了一个字典,显示了适合给定插槽的所有工作人员。这意味着代码不必按照列表的顺序机械地跟随每个人,然后在移动到下一个工作人员之前将每个人填充到最大班次。我在你的初始代码中也观察到了这一点,我觉得跟随插槽列表而不是工作人员更重要。

这是修改后的代码。

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))

请原谅我,如果代码没有正确显示我仍然试图在这个论坛上发布带有缩进要求的代码。

任何方式再次感谢很多,请帮我一个忙,并测试代码的各种条件,如空工人列表与插槽有元素,反之亦然,插槽与数字没有工人可填充(即一个在列表中没有工人的possible_assignments中输入密钥等。再次感谢您的帮助。