我有三套:
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的函数。是否内置了这个或简单的列表理解?
答案 0 :(得分:13)
all(any(a & b for a in s if a is not b) for b in s)
答案 1 :(得分:5)
这是一个非常简单的解决方案,对大型输入非常有效:
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)
答案 2 :(得分:2)
这有点冗长,但我认为这是一个非常有效的解决方案。它利用了这样一个事实:当两组相交时,我们可以将它们标记为连接。它通过保持标志列表和集合列表来完成此操作。当设置i
并设置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)
我决定连接一个空的列表(如果你生成一个列表的元素,我可以生成一个它相交的元素;)。仅包含一个元素的列表是不相关的。如果你不同意,这两种情况都要改变。
答案 3 :(得分:2)
这是一个更有效(如果复杂得多)的解决方案,它执行线性数量的交叉点和多个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。
答案 4 :(得分:1)
像往常一样,我想提出不可避免的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
这真的很懒,只会做所需的交叉点。它也可能是一个非常令人困惑和难以理解的oneliner; - )
答案 5 :(得分:1)
要回答你的问题,不,没有内置或简单的列表理解能够满足您的需求。这是另一个基于itertools
的解决方案非常有效 - 令人惊讶的是,在使用您的示例输入的时序测试中使用itertools
的@ {THC4k groupby()
答案速度的两倍快。它可能会进一步优化,但如所呈现的那样非常易读。就像@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)
答案 6 :(得分:0)
这种策略不太可能像@ Victor的建议那样有效,但由于设置算术(union
)的使用增加,可能比jchl's answer更有效。
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)
答案 7 :(得分:0)
这可能会提供更好的性能,具体取决于集合的分布。
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)