【问题标题】:Combine a list of pairs (tuples)?组合对(元组)的列表?
【发布时间】:2016-10-07 20:52:20
【问题描述】:

从链接对的列表中,我想将这些对组合成公共 ID 组,这样我就可以将 group_ids 写回数据库,例如:

UPDATE table SET group = n WHERE id IN (...........);

例子:

[(1,2), (3, 4), (1, 5), (6, 3), (7, 8)]

变成

[[1, 2, 5], [3, 4, 6], [7, 8]]

允许:

UPDATE table SET group = 1 WHERE id IN (1, 2, 5);
UPDATE table SET group = 2 WHERE id IN (3, 4, 6);
UPDATE table SET group = 3 WHERE id IN (7, 8);

[(1,2), (3, 4), (1, 5), (6, 3), (7, 8), (5, 3)]

变成

[[1, 2, 5, 3, 4, 6], [7, 8]]

允许:

UPDATE table SET group = 1 WHERE id IN (1, 2, 5, 3, 4, 6);
UPDATE table SET group = 2 WHERE id IN (7, 8);

我已经编写了一些有效的代码。我传入一个元组列表,其中每个元组是一对链接的 id。我返回一个列表列表,其中每个内部列表都是一个公共 id 的列表。

我遍历元组列表并将每个元组元素分配给组,如下所示:

  • 如果 a 和 b 都不在列表中,则创建一个新列表,附加 a 和 b 并将新列表附加到列表列表中
  • 如果 a 在某个组中但 b 不在,则将 b 添加到 a 组中
  • 如果 b 在组中但 a 不在组中,则将 a 添加到 b 组中
  • 如果 a 和 b 已经在不同的组中,则合并 a 和 b 组
  • 如果 a 和 b 已经在同一个组中,则什么也不做

我期待数百万个链接对,我期待数十万个 gropus 和数十万个组成员。所以,我需要快速的算法,我正在寻找一些真正有效的代码的建议。虽然我已经实现了这个来构建列表列表,但我对任何事情都持开放态度,他们的关键是能够构建上述 SQL 以将组 ID 返回到数据库。

def group_pairs(list_of_pairs):
    """

    :param list_of_pairs:
    :return:
    """
    groups = list()
    for pair in list_of_pairs:
        a_group = None
        b_group = None

        for group in groups:
            # find what group if any a and b belong to

            # don't bother checking if a group already found
            if a_group is None and pair[0] in group:
                a_group = group
            # don't bother checking if b group already found
            if b_group is None and pair[1] in group:
                b_group = group
            # if a and b found, stop looking
            if a_group is not None and b_group is not None:
                break

        if a_group is None:
            if b_group is None:
                # neither a nor b are in a group; create a new group and
                # add a and b
                groups.append([pair[0], pair[1]])
            else:
                # b is in a group but a isn't, so add a to the b group
                b_group.append(pair[0])
        elif a_group != b_group:
            if b_group is None:
                # a is in a group but b isn't, so add b to the a group
                a_group.append(pair[1])
            else:
                # a and b are in different groups, add join b to a and get
                # rid of b
                a_group.extend(b_group)
                groups.remove(b_group)
        elif a_group == b_group:
            # a and b already in same group, so nothing to do
            pass

    return groups

【问题讨论】:

    标签: python list


    【解决方案1】:

    与:

    def make_equiv_classes(pairs):
        groups = {}
        for (x, y) in pairs:
            xset = groups.get(x, set([x]))
            yset = groups.get(y, set([y]))
            jset = xset | yset
            for z in jset:
                groups[z] = jset
        return set(map(tuple, groups.values()))
    

    你可以得到:

    >>> make_equiv_classes([(1,2), (3, 4), (1, 5), (6, 3), (7, 8)])
    {(1, 2, 5), (3, 4, 6), (8, 7)}
    
    >>> make_equiv_classes([(1,2), (3, 4), (1, 5), (6, 3), (7, 8), (5, 3)])
    {(1, 2, 3, 4, 5, 6), (8, 7)}
    

    复杂度应该是O(np),其中n是不同整数值的数量,p是对的数量.

    我认为set 是单个组的正确类型,因为它使联合操作快速且易于表达,而dict 是存储groups 的正确方法,因为您可以获得常量时间查找询问特定整数值属于哪个组的问题。

    如果我们愿意,我们可以设置一个测试工具来为这段代码计时。首先,我们可以在中等大小的东西上构建一个随机图,比如 10K 个节点(即不同的整数值)。我将放入 5K 随机链接(对),因为这往往会给我数千个组,它们加起来约占节点的三分之二(也就是说,大约 3K 节点将位于单个组中,不链接到任何东西否则)。

    import random
    pairs = []
    while len(pairs) < 5000:
        a = random.randint(1,10000)
        b = random.randint(1,10000)
        if a != b:
            pairs.append((a,b))
    

    然后,我们可以计时(我在这里使用 IPython 魔法):

    In [48]: %timeit c = make_equiv_classes(pairs)
    10 loops, best of 3: 63 ms per loop
    

    这比您最初的解决方案更快:

    In [49]: %timeit c = group_pairs(pairs)
    1 loop, best of 3: 2.08 s per loop
    

    我们还可以使用这个随机图来检查两个函数的输出对于一些大的随机输入是否相同:

    >>> c = make_equiv_classes(pairs)
    >>> c2 = group_pairs(pairs)
    >>> set(tuple(sorted(x)) for x in c) == set(tuple(sorted(x)) for x in c2)
    True
    

    【讨论】:

    • 谢谢。这很棒。我将示例数据作为整数给出。在我的真实数据中,它们是 varchars,例如 ABC12345、XYZ789。 dict 仍然是存储组的方式吗?
    • 有了这个make_equiv_classes,只要它们是可散列的,你的数据是什么类型就无关紧要了。 varchars(Python 中的str)应该可以正常工作。
    猜你喜欢
    • 2016-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多