Python集相交问题

Python集相交问题,python,set,Python,Set,我有三套: 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。是否有内置的或简单的列表理解功能?有点冗长,但我认为这是一个非常有效的解决方案。它利用了这样一个事实,即当两

我有三套:

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。是否有内置的或简单的列表理解功能?

有点冗长,但我认为这是一个非常有效的解决方案。它利用了这样一个事实,即当两个集合相交时,我们可以将它们都标记为连接的。它通过保持标志列表与集合列表一样长来实现这一点。当set
i
和set
j
相交时,会为这两个设置标志。然后,它在集合列表上循环,只尝试为尚未相交的集合查找交集。看了评论后,我想这就是@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)

我决定连接一个集合的空列表(如果生成列表中的元素,我可以生成它相交的元素;)。一个只有一个元素的列表是不连通的。如果你不同意,在任何一种情况下都有一行可以改变。

这一策略可能没有@Victor的建议那么有效,但可能比增加使用集合算术(
union
)更有效

all(any(a & b for a in s if a is not b) for b in s)

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

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)
这里有一个更有效(如果更复杂)的解决方案,它执行线性数量的交点和数量的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。

这里有一个非常简单的解决方案,对于大型输入非常有效:

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)

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


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

回答你的问题,不,没有一个内置的或简单的列表理解可以满足你的需要。下面是另一个基于
itertools
的解决方案,它非常有效——在使用示例输入的计时测试中,使用
groupby()
回答问题的速度是@THC4k的
itertools
的两倍。它可能会被进一步优化,但正如所展示的那样可读性很强。像@AaronMcSmooth一样,当输入列表中没有或只有一个集合时,我任意决定返回什么

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)

优雅,但我更喜欢两个直循环和一个计数器,以避免将两个项目进行两次比较,这样至少可以将速度提高两倍。
bool()
调用是多余的。@Victor Ionescu,为什么不用您喜欢的方法给出一个答案呢。这至少值得十次重复:)@spoon16:我试图消除不必要的比较。当然,有了所有额外的代码,我不确定这是否会产生一个比jchl非常优雅的解决方案更优化的版本。我可能会尝试在一个大的集合序列上计时,看看结果如何。@KennyTM:谢谢,我没有意识到
any()
执行了自动布尔转换。我删除了
bool()
调用。@jchl:集合是不可散列的,所以它们不能是集合中的元素。我不确定将顶级集合设置为冻结集合是否有多大意义,但它似乎比常规集合快,而且符合逻辑。@jchl:啊,我明白你现在在说什么了。看起来我在重构并发布代码时出现了某种精神消化不良问题。“不过,doctest通过了!”不管怎样,它现在已经修复。我的分析表明它们稍微快一点。只是几乎没有想到的坏消息。我刚刚分析了这一个,对于@jchl大约需要4.7毫秒的相同类型的大随机输入,它需要大约2.5秒。对我来说,坏消息是,尽管我付出了所有的努力,我只赢了他0.2毫秒。“啊!我被杀了!”真有趣。。这是一个大的集合,还是很多集合,或者两者都有。你是说
中断
而不是
继续
?2.您确定计数为-1吗??3.丢失第2行,并将最后3行替换为
返回计数==len
。4.不会
x!=y和a&b更好吗?5.如果一组人没有伙伴,你就不会提前退出——什么是“可以提供更好的性能”的基础?比什么好?你好,你好。。。你的密码坏了
count
成为非空的成对集合交点数,小于1。这完全不是OP想要的。偶然地,它给出了OP的两个测试用例的正确答案。试试这个:s2=[set([16,9,2,10]),set([16,22,14,15]),set([14,2])。。。它应该返回True,但您的计数是6-1==5,因此您的代码返回False。@John,+1,谢谢您指出所有错误。我已编辑以进行所有必要的更正。4.我更喜欢
a&b
而不是
x!=y
,因为后者通常是正确的。这不好吗?5.是的,没错。与使用所有集合进行交叉点检查相比,这可能会提供更好的性能。如果
x!=y
是为了避免为
a是b
计算
a和b
。关键是检查
x!=y
然后执行一个设定的交点。这比执行上一个答案要慢得多。而你之前的答案平均为4.7毫秒(在大的随机输入上),这一个需要250毫秒。工会是扼杀你(通过这个实现)和直觉的东西。理论上很吸引人,但价格昂贵。我认为这是由于所有Python代码(与以前的解决方案相比,几乎所有的解决方案都可以用C语言进行计算)导致的一个大常量因素。另外,如果
any()
调用可以经常使用快捷方式,那么我的第一个解决方案将很好地工作,而这个解决方案在所有情况下都很好地工作。此解决方案在该输入上比第一个解决方案的性能更好(9秒对24秒):
[设置范围(N)中的i((i))+[set(范围(N))]
N=10000from 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
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)