Python 根据人员列表和他们必须会见的人创建时间表

Python 根据人员列表和他们必须会见的人创建时间表,python,Python,我有一份人员ID列表,以及他们应会见的人员,如下所示: people = [ [1, [2,3,4]], [2, [1,3,4]], [3, [1,2,4]], [4, [1,2,3]] ] # ^ person_id # ^ list of person to meet 我想安排列表,以便每个人在不同的时间段会面,即我想要的输出是 people = [ [1, [2,3,4]], [2, [1,4,3]],

我有一份人员ID列表,以及他们应会见的人员,如下所示:

people = [
    [1, [2,3,4]], 
    [2, [1,3,4]], 
    [3, [1,2,4]],
    [4, [1,2,3]]
]
#    ^ person_id
#        ^ list of person to meet
我想安排列表,以便每个人在不同的时间段会面,即我想要的输出是

people = [
    [1, [2,3,4]], 
    [2, [1,4,3]], 
    [3, [4,1,2]],
    [4, [3,2,1]]
]
#        ^ ^ ^  timeslot 1,2,3
这意味着
1
必须在第一个时隙中满足
2
3
必须满足
4

对于少数人,我可以用手来做。当我有一个更大的列表时,我想知道是否有办法解决这个问题。我将CSV文件附加在较大的人员ID上,以及他们必须会见的人员


注意不确定这是否适用于堆栈溢出,我可以问其他地方。请随时提出建议。

更新了,以提高清晰度,以防有人从谷歌这里绊倒。内容几乎相同

按照数据的设置方式,每次会议在某种意义上“属于”一个人。以人员1为例,他们需要与人员2、3和4会面。这将强制这些人参加会议,即使2-4人的会议列表中没有反映这些会议。换句话说,存在一种对称性,因此,如果人1和人2之间有一次会面,那么人2和人1之间也会有一次会面。任何时候,当一个问题自然有感兴趣的对象(人)和他们之间的对称关系(会议)时,图形可能是解决问题的有效工具。在你的例子中,你有下面的图表

根据这张图表再次说明你的问题,你需要一种方法将这些会议分成小组,这样在任何小组中,同一个人不会同时参加两次会议。这通常被称为图的边着色,在图中,使用所谓的“颜色”标记这些组中的每一组,并尝试为每一条边着色,以便没有顶点具有两条相同颜色的相邻边。在你的问题中,你给出的解决方案如下所示

相关信息包括维辛定理。如果一个人的最大会议次数是N,那么您需要N或N+1种不同的颜色(不同的时间段)来解决您的问题。当然,额外的时间段可以很好地工作,但通常你需要一个最小的解决方案。在你的问题上,你给出的解决方案显然是最小的。一般来说,您可能有一些时间段未被某些个人占用

在实践中,实现边界是NP完全的。有各种各样的启发式算法,通常做得很好,而且很接近。
networkx
库(顺便说一句,我很喜欢)实际上不支持边着色,我不相信,但是一个等价的构造是图的线图的顶点着色。当你把所有的边都变成顶点,并且说两个这样的顶点是通过一条边连接的,如果它们以前是通过一个顶点连接的边,那么线图就是你得到的。你的问题的折线图并不太复杂

当查看直线图而不是原始曲线图时,您希望找到一个顶点着色,因为现在顶点是会议(您仍然希望分配),边表示由一个不能同时参加两个会议的人连接。对于您的问题,您给出的解决方案是线图的以下顶点着色

在图论中进行这项练习的要点是,对图论算法进行了充分的研究和理解。下面的代码将数据结构转换为图形,使用
networkx
库查找折线图的顶点着色,并将数据重新格式化为所需的结构

import networkx as nx

def build_line_graph(people):
    G = nx.Graph()
    G.add_edges_from(((p, q) for p, L in people for q in L))
    return nx.line_graph(G)

def color_graph(G):
    return nx.greedy_color(G)

def format_answer(coloring):
    res = {}
    N = max(coloring.values()) + 1
    for meeting in coloring:
        time_slot = coloring[meeting]
        for meeting_member in (0, 1):
            if meeting[meeting_member] not in res:
                res[meeting[meeting_member]] = [None] * N
            res[meeting[meeting_member]][time_slot] = meeting[1-meeting_member]
    return res

def nest_answer(people, formatted):
    return [[p, formatted[p]] for p, v in people]

>>> format_answer(color_graph(build_line_graph(people)))
{1: [2, 3, 4], 2: [1, 4, 3], 3: [4, 1, 2], 4: [3, 2, 1]}

>>> nest_answer(people, format_answer(color_graph(build_line_graph(people))))
[[1, [2, 3, 4]], [2, [1, 4, 3]], [3, [4, 1, 2]], [4, [3, 2, 1]]]
有几个原因,我更喜欢字典的答案,但我们可以重新格式化它,以提供您所要求的准确答案,只需很少的努力

我要提到的一点是,你的问题没有唯一正确的答案。一个有效答案的任何时隙排列也是一个有效答案,通常还有许多其他类型的颜色可以满足你的必要属性。此外,贪心算法不能保证产生最优解(事实上,它有病态地糟糕的反例),但在这种情况下,它实际上会产生与您提出的相同的解。通常这个解决方案会很好。

这里有一个简单(但可能不是最佳)的解决方案:

people = [
    [1, [2,3,4]],
    [2, [1,3,4]],
    [3, [1,2,4]],
    [4, [1,2,3]]
]
meetings = set(tuple(sorted([meeter, meetee]))
               for meeter, meetees in people for meetee in meetees)

current = []
schedule = []
busy = set()
while meetings:
    for meeting in meetings:
        meeter, meetee = meeting
        if meeter in busy or meetee in busy:
            continue
        current.append(meeting)
        meetings.remove(meeting)
        busy.add(meeter)
        busy.add(meetee)
        break
    else:
        schedule.append(current)
        current = []
        busy = set()
if current:
    schedule.append(current)

num_slots = len(schedule)
personal_schedule = {meeter: [None] * num_slots for meeter, meetees in people}
for slot_index, slot in enumerate(schedule):
    for meeting in slot:
        meeter, meetee = meeting
        personal_schedule[meeter][slot_index] = meetee
        personal_schedule[meetee][slot_index] = meeter
personal_schedule = [list(items) for items in personal_schedule.items()]
print(personal_schedule)

首先构建所有会议的列表。然后,我们尝试在当前时间段中安排尽可能多的会议,以确保该会议中的所有人在该时间段中都不忙。当我们再也找不到这样的会议时,我们就转到下一个时间段。最后,将会议日程转换为OP中要求的个人日程格式。

这适用于堆栈溢出。数据是否对称?例如,一打人需要会见第四个人,而第四个人只需要会见一个人吗?在这种情况下,你想让所有会议的联合或交叉发生(或其他事情)?@HansMusgrave是的,它是对称的>>每个人都应该与每个人会面k次。我通过线性规划生成匹配。如果您想尝试,我会为我的问题中的示例数据集附加CSV文件。如果您有更多问题/澄清,请告诉我@提提帕塔看起来不错。我只是好奇一个简单的解决方案是否有效,或者是否需要更加小心。这让我想起了一些图形握手算法。我不在家,不在图书馆,但我会看看谷歌会出现什么。@titipata我不认为这是微不足道的,但如果你能接受一种额外的依赖,那么大部分的辛苦工作都是由其他人完成的。我正在编写一个解决方案。谢谢@Amadan,这非常有效。我将详细检查解决方案,并在明天之前接受!