我有三个集合:
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。是否有内置函数或简单的列表推导可用?
all(any(a & b for a in s if a is not b) for b in s)
这里有一个非常简单的解决方案,对于大量输入非常有效:
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)
count.setdefault(x, 0) += 1
代替if/else
语句。 - aaronasterlingcount.setdefault(x, 0)
不是一个左值。 - jchlcollections.Counter
,要么使用count= collections.defaultdict(int)
和count[x]+=1
。 - tzotdefaultdict
。我已经相应地简化了我的答案。 - jchl这个解决方案可能有点啰嗦,但我认为它是相当高效的。它利用了两个集合相交时我们可以将它们都标记为已连接的事实。它通过保持与集合列表一样长的标志列表来实现这一点。当集合 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)
我认为空集列表是连通的(如果您产生了该列表的一个元素,我可以生成它相交的元素;)。只有一个元素的列表是不连通的,这是显而易见的。无论哪种情况下都只需要更改一行代码,如果您不同意,请告知。
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
any()
调用经常进行快捷方式,则我的第一个解决方案将很好地工作,而这个解决方案在所有情况下都很有效。对于此输入,此解决方案的性能优于第一个解决方案(9秒对24秒):[set((i,)) for i in range(N)] + [set(range(N))]
,其中N = 10000
,我怀疑在更大的N
下仍然会表现得更好。 - jchl像往常一样,我想给出不可避免的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
这真的很懒,只会做必需的交集。它也可能是一个非常混乱和难以阅读的一行代码;-)
回答你的问题,没有内置或简单的列表推导式可以做到你想要的。这里有另一个基于itertools
的解决方案,非常高效--在使用你的示例输入进行定时测试时,令人惊讶地比@THC4k使用groupby()
的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)
这种策略可能不像@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)
根据集合的分布情况,这可能会提供更好的性能。
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)
break
而不是continue
吧?count = -1
这样写没问题吗?return count == len(s)
。x != y and a & b
会更好吧?count
成为非空两两集合交集的数量,减1。这与OP想要的完全不同。偶然情况下,它可以给出OP的两个测试用例的正确答案。试试这个:s2 = [set([16, 9, 2, 10]), set([16, 22, 14, 15]), set([14, 2])] ... 它应该返回True,但是你的计数是6-1 == 5,所以你的代码返回False。 - John Machina&b
而不是x!= y
,因为后者更常见。这样做有问题吗?5. 是的,没错。与对所有集合进行交集检查的方法相比,这可能会提供更好的性能。 - dheerosaurx != y
的整个目的是避免为 a is b
计算 a & b
。重点是检查 x != y
要比执行集合交集便宜得多。 - aaronasterling
bool()
调用是多余的。 - kennytmany()
执行自动布尔转换。我删除了bool()
调用。 - jchlset(())
不等于set(())
。 - intuited