【问题标题】:How to merge sets which have intersections (connected components algorithm)? [duplicate]如何合并具有交集的集合(连通分量算法)? [复制]
【发布时间】:2019-02-13 15:06:23
【问题描述】:

是否有任何有效的方法来合并具有交集的集合。例如:

l = [{1, 3}, {2, 3}, {4, 5}, {6, 5}, {7, 5}, {8, 9}]

预期结果是:

r = [{1, 2, 3}, {4, 5, 6, 7}, {8, 9}]

应该合并所有有交集(公共组件)的集合。例如:

{1, 3} & {2, 3}
# {3}

所以这两个集合应该合并:

{1, 3} | {2, 3}
# {1, 2, 3}

很遗憾,我没有任何可行的解决方案。

更新:结果中集合的顺序并不重要。

【问题讨论】:

  • 请说明在什么条件下合并?
  • 看起来你想要一个connected components 算法。
  • 为避免投机性的答案,请解释您究竟想要达到什么目标,并说明您已经尝试过什么以及问题所在。
  • [{1,2}, {2,3}, {3,4}] 这样的输入的期望输出是什么? [{1,2,3,4}]?或者[{1,2,3}, {3,4}]
  • 他们应该有交集{1, 3} & {2, 3} -> {3} or {4, 5}, {6, 5}, {7, 5} -> {5}.

标签: python set graph-theory


【解决方案1】:

如@mkrieger1 在 cmets 中提到的,实现connected components algorithm 的一种有效方法是将集合列表转换为一组可散列的冻结集,以便在遍历它时找到与当前集合相交的冻结集您可以轻松地将其从池中移除:

pool = set(map(frozenset, l))
groups = []
while pool:
    groups.append(set(pool.pop()))
    while True:
        for candidate in pool:
            if groups[-1] & candidate:
                groups[-1] |= candidate
                pool.remove(candidate)
                break
        else:
            break

给定l = [{1, 3}, {2, 3}, {4, 5}, {6, 5}, {7, 5}, {8, 9}]groups 将变为:

[{1, 2, 3}, {4, 5, 6, 7}, {8, 9}]

而给定l = [{1, 2}, {3, 4}, {2, 3}]groups 将变为:

[{1, 2, 3, 4}]

而给定l = [{1}, {2}, {1, 2}]groups 将变为:

[{1, 2}]

【讨论】:

  • 这是预期结果还是错误[{1}, {2}, {1, 2}]:[{1, 2}, {1}]
  • 确实是一个错误。我已经用while 循环修复了它。谢谢。
【解决方案2】:

我提出这个解决方案:

def merge_sets(set_list):
    if len(set_list) == 0:
        # short circuit to avoid errors
        return []

    current_set = set_list[0]
    new_set_list = [current_set, ]

    for s in set_list[1:]:          # iterate from the second element
        if len(current_set.intersection(s)) > 0:
            current_set.update(s)
        else:
            current_set = set(s)    # copy
            new_set_list.append(current_set)

    return new_set_list

适用于的测试用例:

test_cases = [
    {
        'input': [{1, 3}, {2, 3}, {4, 5}, {6, 5}, {7, 5}, {8, 9}],
        'output': [{1, 2, 3}, {4, 5, 6, 7}, {8, 9}],
    },
    {
        'input': [{1, 2}, {2, 3}, {3, 4}],
        'output': [{1, 2, 3, 4}, ],
    },
    {
        'input': [{1}, {2}, {1, 2}],
        'output': [{1}, {1, 2}],
    },
    {
        'input': [{1, 2}, {3, 4}, {2, 3}],
        'output': [{1, 2}, {2, 3, 4}],
    },
]

for case in test_cases:
    print('input   ', case['input'])
    print('expected', case['output'])

    new_output = merge_sets(case['input'])
    print('real    ', new_output)
    assert new_output == case['output']

这对你有用吗?

【讨论】:

  • [{1}, {1, 2}]那里有一个交叉点所以输出错误
  • 注意:我的解决方案确实考虑了输入列表中集合的顺序。这就是你需要的@MykolaZotko?或者输入可以是一组无序的集合,应该合并直到没有更多的合并可以做?
  • @BenoîtPilatte 算法是否应该考虑输入集的顺序?如果是,那么我的解决方案有效;如果不是,那么我的解决方案不是正确的
  • 对于[{1}, {2}, {1,2}],结果必须是{1, 2}而不是{1}, {1, 2}
  • @BenoîtPilatte 顺序并不重要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多