如何测试列表中的所有项目是不相交的?

时间:2017-08-12 23:57:52

标签: python set disjoint-sets

给定一个包含多个迭代的列表,我想测试是否所有项都是disjoint

  

如果他们没有共同的元素,则认为两组不相交

示例:

iterables = ["AB", "CDE", "AF"]
all_disjoint(iterables)
# False

iterables = ["AB", "CDE", "FG"]
all_disjoint(iterables)
# True

Python集合有一个isdisjoint方法可行,但它设计用于一次测试两个元素。一种方法是将此方法应用于每个成对的元素组:

import itertools as it


def pairwise_(iterable):
    """s -> (s0,s1), (s1,s2), (s2,s3), ..., (sn,s0)"""
    # Modified: the last element wraps back to the first element.
    a, b = it.tee(iterable, 2)
    first = next(b, None)
    b = it.chain(b, [first])
    return zip(a, b)


def all_disjoint(x):
    return all((set(p0).isdisjoint(set(p1))) for p0, p1 in pairwise_(x))

我在这里修改了pairwise itertools recipe以便最后一次附加第一个元素。但这并不完全正确,因为它只测试相邻项目而不是每个项目对照列表中的所有其他项目。我想用更少的代码更优雅地测试所有元素。有更简单的方法吗?

4 个答案:

答案 0 :(得分:6)

IIUC,您可以获取字符串列表,组合它们,然后检查组合长度是否等于该字符串的等效设置长度。

您可以使用''.join加入字符串并定义您的功能:

def all_disjoint(iterables):
    total = ''.join(iterables)
    return len(total) == len(set(total))

现在,测试:

all_disjoint(['AB', 'CDE', 'AF'])
# False

all_disjoint(['AB', 'CDE', 'FG'])
# True

答案 1 :(得分:1)

首先,<div *ngIf="isAndroidTablet()">...</div> 会导致设置set(list('AB'))

其次,通过枚举{'A', 'B'}然后使用s,只查看上对角线,避免需要将值与自身或另一对进行比较。例如,如果for s2 in s[n+1:],那么[(s1,s2)对于n,s1在s [n + 1:]]中对s2的枚举将导致:s = ['A', 'B', 'C']。如果要从[('A', 'B'), ('A', 'C'), ('B', 'C')]导入list(combinations(s, 2)),则相当于combinations的结果。

鉴于上述情况,我使用itertools生成器来比较每个子集之间缺少任何交集。

由于any构造,它会在第一次观察共同元素时短路,从而无需计算每对。

any

答案 2 :(得分:1)

鉴于您所说的想要测试每个项目与所有其他项目不相交,我认为这样做符合您的要求:

import itertools as it

def all_disjoint(x):
    return all((set(p0).isdisjoint(set(p1))) for p0, p1 in it.combinations(x, 2))

iterables = ['AB', 'CDE', 'AF']
print(all_disjoint(iterables))  # -> False

iterables = ['AB', 'CDE', 'FG']
print(all_disjoint(iterables))  # -> True

# your code gives different answer on this one 
# (because it doesn't check what you want)
iterables = ['AB', 'CDE', 'AH', 'FG']
print(all_disjoint(iterables))  # -> False

答案 3 :(得分:0)

我为有兴趣的人添加了这些答案。

方法1 :我意识到这可以通过multiset(Counter)完成。

import itertools as it
import collections as ct


def all_disjoint(iterables):
    return all(not v-1 for v in ct.Counter(it.chain.from_iterable(iterables)).values())

方法2 :从more_itertools库中,more_itertools.unique_to_each生成每个可迭代的所有唯一项。以下代码将结果的长度与原始iterables进行比较:

import more_itertools as mit

def all_disjoint(iterables):
    return all(len(x) == len(y) for x, y in zip(iterables, mit.unique_to_each(*iterables)))