代码之家  ›  专栏  ›  技术社区  ›  Eric Schoonover thSoft

python集交集问题

  •  7
  • Eric Schoonover thSoft  · 技术社区  · 14 年前

    我有三套:

    s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])]   # true, 16 and 14
    s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])]    # false
    

    我想要一个函数,如果列表中的每个集与列表中的至少一个其他集相交,它将返回true。是否有内置的此功能或简单的列表理解功能?

    8 回复  |  直到 14 年前
        1
  •  2
  •   aaronasterling    14 年前

    这有点冗长,但我认为这是一个相当有效的解决方案。它利用了这样一个事实:当两个集合相交时,我们可以将它们都标记为连通的。它通过将标志列表与集合列表保持相同的长度来实现这一点。当设置 i 并设置 j Intersect,它为它们设置标志。然后,它循环遍历集合列表,并仅尝试为尚未相交的集合查找交集。在阅读了这些评论之后,我想这就是@victor所说的。

    s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])]   # true, 16 and 14
    s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])]    # false
    
    
    def connected(sets):
        L = len(sets)
    
        if not L: return True
        if L == 1: return False
    
        passed = [False] * L
        i = 0
        while True:
            while passed[i]: 
                i += 1
                if i == L: 
                    return True
    
            for j, s in enumerate(sets):
                if j == i: continue
                if sets[i] & s: 
                    passed[i] = passed[j] = True
                    break
            else:
                return False
    
    
    print connected(s0)
    print connected(s1)
    

    我决定连接一个空的集合列表(如果您生成一个列表元素,我可以生成一个与之相交的元素;)。一个只有一个元素的列表被琐碎地断开连接。无论哪种情况,如果你不同意,都可以改变。

        2
  •  13
  •   jchl    14 年前
    all(any(a & b for a in s if a is not b) for b in s)
    
        3
  •  5
  •   jchl    14 年前

    下面是一个非常简单的解决方案,它对大输入非常有效:

    def g(s):
        import collections
        count = collections.defaultdict(int)
        for a in s:
            for x in a:
                count[x] += 1
        return all(any(count[x] > 1 for x in a) for a in s)
    
        4
  •  2
  •   jchl    14 年前

    这里有一个更有效的(如果更复杂的话)解决方案,它执行一个线性的交叉数和多个O(n*log(n))阶的并集,其中n是 s :

    def f(s):
        import math
        j = int(math.log(len(s) - 1, 2)) + 1
        unions = [set()] * (j + 1)
        for i, a in enumerate(s):
            unions[:j] = [set.union(set(), *s[i+2**k:i+2**(k+1)]) for k in range(j)]
            if not (a & set.union(*unions)):
                return False
            j = int(math.log(i ^ (i + 1), 2))
            unions[j] = set.union(a, *unions[:j])
        return True
    

    请注意,此解决方案仅适用于python>=2.6。

        5
  •  1
  •   Jochen Ritzel    14 年前

    像往常一样,我想给不可避免的 itertools 解决方案;-)

    from itertools import combinations, groupby
    from operator import itemgetter
    
    
    def any_intersects( sets ):
        # we are doing stuff with combinations of sets
        combined = combinations(sets,2) 
        # group these combinations by their first set
        grouped = (g for k,g in groupby( combined, key=itemgetter(0)))
        # are any intersections in each group
        intersected = (any((a&b) for a,b in group) for group in grouped)
        return all( intersected )
    
    
    s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])]
    s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])] 
    print any_intersects( s0 ) # True
    print any_intersects( s1 ) # False
    

    这真的很懒惰,只会做需要的交叉点。它也可能是一个非常混乱和不可读的一行程序;-)

        6
  •  1
  •   martineau Nae    14 年前

    要回答你的问题,不,没有一个内置的或简单的列表理解可以满足你的需要。这是另外一个 itertools 基于解决方案的效率非常高——令人惊讶的是速度是@thc4k的两倍 迭代工具 回答使用 groupby() 在计时测试中使用您的样本输入。它可能会进一步优化,但如本文所示,可读性非常好。像@aaronmcsmound一样,当输入列表中没有或只有一个集合时,我随意决定返回什么。

    from itertools import combinations
    
    def all_intersect(sets):
        N = len(sets)
        if not N: return True
        if N == 1: return False
    
        intersected = [False] * N
        for i,j in combinations(xrange(N), 2):
            if not intersected[i] or not intersected[j]:
                if sets[i] & sets[j]:
                    intersected[i] = intersected[j] = True
        return all(intersected)
    
        7
  •  0
  •   Community gkalpak    7 年前

    这种策略可能不如@victor的建议有效,但可能比 jchl's answer 由于集合算法的使用增加( union )

    s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])]
    s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])]
    
    def freeze(list_of_sets):
        """Transform a list of sets into a frozenset of frozensets."""
        return frozenset(frozenset(set_) for set_ in list_of_sets)
    
    def all_sets_have_relatives(set_of_sets):
        """Check if all sets have another set that they intersect with.
    
        >>> all_sets_have_relatives(s0)   # true, 16 and 14
        True
        >>> all_sets_have_relatives(s1)   # false
        False
        """
        set_of_sets = freeze(set_of_sets)
        def has_relative(set_):
            return set_ & frozenset.union(*(set_of_sets - set((set_,))))
        return all(has_relative(set) for set in set_of_sets)
    
        8
  •  0
  •   dheerosaur    14 年前

    根据集合的分布情况,这可能会提供更好的性能。

    def all_intersect(s):
       count = 0
       for x, a in enumerate(s):
          for y, b in enumerate(s):
             if a & b and x!=y:
                count += 1
                break
       return count == len(s)