如何检查任何给定集合中是否存在值

时间:2015-06-01 10:34:53

标签: python set

假设我有不同的设置(它们必须不同,我不能按照我正在处理的数据加入它们):

r = set([1,2,3])
s = set([4,5,6])
t = set([7,8,9])

检查任何一个变量是否存在于其中的最佳方法是什么?

我正在使用:

if myvar in r \
   or myvar in s \
   or myvar in t:

但我想知道是否可以通过使用setunion属性来减少这种情况。

以下有效,但我找不到定义多个联合的方法:

if myvar in r.union(s)
   or myvar in t:

我也想知道这个联盟是否会以某种方式影响性能,因为我猜一个临时的set将会动态创建。

5 个答案:

答案 0 :(得分:16)

您可以使用内置any

r = set([1,2,3])
s = set([4,5,6])
t = set([7,8,9])
if any(myvar in x for x in [r,s,t]):
    print "I'm in one of them"

any会在返回True的第一个条件上发生短路,因此您可以绕过构建潜在巨大的union或检查可能包含的大量集合。​​

而且我也想知道这个联盟是否会影响某种程度的表现,因为我想一个临时的设置将会动态创建。

根据wiki.python.com s|t O(len(s)+len(t)),而查询为O(1)

对于每个n元素的l集合,迭代执行union以构建集合将导致:

a.union(b).union(c).union(d) .... .union(n)

相当于O(l+l)a.union(b) O(2l+2l+l)的{​​{1}}等等,总计为a.union(b).union(c)

O(n*(n+1)/2)*l)是二次的,并且无法使用集合的性能优势。

使用O(n^2*l)的n个集合中的查找将在any

执行

答案 1 :(得分:14)

只需使用任何:

if any(myvar in x for x in  (r,s,t))

设置查找是0(1)所以创建一个联合以检查变量是否在任何集合中是完全没有必要的,而不是简单地使用inany进行检查,这将立即短路找到匹配项并且不会创建新集。

我也想知道这个联盟是否会影响某种表现

是的,当然联合集会影响性能,它会增加复杂性,每次创建一个新集合O(len(r)+len(s)+len(t)),这样你就可以告别使用高效查找集的真正意义。

所以最重要的是你要保持有效的查找,你必须将它们组合一次并将它们保存在内存中创建一个新变量,然后使用它来查找myvar所以初始创建将是0(n),之后查找将为0(1)

如果你不想每次想要进行查找而首先创建联合,那么你将得到r+s+t -> set.union(*(r, s, t))长度的线性解决方案,而不是最差的三个常量(平均)查找。这也意味着始终添加或删除从r,st添加/删除的新联合集中的任何元素。

在中等大小的集合上的一些现实时间显示了完全不同的区别:

In [1]: r = set(range(10000))

In [2]: s = set(range(10001,20000))

In [3]: t = set(range(20001,30000))

In [4]: timeit any(29000 in st for st in (r,s,t))
1000000 loops, best of 3: 869 ns per loop  

In [5]: timeit 29000 in r | s | t
1000 loops, best of 3: 956 µs per loop

In [6]: timeit 29000 in reduce(lambda x,y :x.union(y),[r,s,t])
1000 loops, best of 3: 961 µs per loop

In [7]: timeit 29000 in r.union(s).union(t)
1000 loops, best of 3: 953 µs per loop

联盟的时间显示,几乎所有的时间都花在了联合呼叫上:

In [8]: timeit r.union(s).union(t)
1000 loops, best of 3: 952 µs per loop

使用较大的集合并获取最后一组中的元素:

In [15]: r = set(range(1000000))

In [16]: s = set(range(1000001,2000000))

In [17]: t = set(range(2000001,3000000))


In [18]: timeit any(2999999 in st for st in (r,s,t))
1000000 loops, best of 3: 878 ns per loop

In [19]: timeit 2999999 in reduce(lambda x,y :x.union(y),[r,s,t])
1 loops, best of 3: 161 ms per loop

In [20]: timeit 2999999 in r | s | t
10 loops, best of 3: 157 ms per loop

无论集合使用any有多大都没有区别,但随着集合大小的增加,使用union的运行时间也会增长。

使其更快的唯一方法是坚持or,但我们正在采用几百纳秒的差异,这是创建生成器表达式和函数调用的成本:

In [22]: timeit 2999999 in r or 2999999 in s or 2999999 in t
10000000 loops, best of 3: 152 ns per loop

对于联合集,set.union(*(r,s,t))也是最快的,因为你没有构建中间集:

In [47]: timeit 2999999 in set.union(*(r,s,t))
10 loops, best of 3: 108 ms per loop
In [49]: r | s | t  == set.union(*(r,s,t))
Out[49]: True

答案 2 :(得分:5)

|是python中sets的联合运算符。您可以使用|将多个集合定义为:

>>> r = set([1,2,3])
>>> s = set([4,5,6])
>>> t = set([7,8,9])
>>> r | s | t
set([1, 2, 3, 4, 5, 6, 7, 8, 9])

答案 3 :(得分:4)

您可以使用reduce函数将两个参数的函数累加应用于可迭代项

>>> r = set([1,2,3])
>>> s = set([4,5,6])
>>> t = set([7,8,9])
>>> 
>>> reduce(lambda x,y :x.union(y),[r,s,t])
set([1, 2, 3, 4, 5, 6, 7, 8, 9])

为了检查其中任何一个的成员资格,您可以在any中使用更高效的生成器表达式,因为python使用hash table来存储集合并检查成员船具有O(1 )在诸如词典或frozenset之类的数据结构中。另外,为了检查,你们所有人的成员资格都使用all

if any(i in item for item in [r,s,t]):
    #do stuff

但在这种情况下(不适用于大型集),使用or运算符会更快。

value in r|s|t 

这是所有方式的基准:

~$ python -m timeit "r = set([1,2,3]);s = set([4,5,6]);t = set([7,8,9]);3 in reduce(lambda x,y :x.union(y),[r,s,t])"
1000000 loops, best of 3: 1.55 usec per loop
~$ python -m timeit "r = set([1,2,3]);s = set([4,5,6]);t = set([7,8,9]);3 in r|s|t"
1000000 loops, best of 3: 1.11 usec per loop
~$ python -m timeit "r = set([1,2,3]);s = set([4,5,6]);t = set([7,8,9]);any(3 in item for item in [r,s,t])"
1000000 loops, best of 3: 1.24 usec per loop
~$ python -m timeit "r = set([1,2,3]);s = set([4,5,6]);t = set([7,8,9]);3 in r.union(s).union(t)"
1000000 loops, best of 3: 1.19 usec per loop

注意因为@Padraic Cunningham提到使用any的大型集合非常有效

答案 4 :(得分:2)

你可以简单地做

if myvar in r.union(s).union(t)

你不必担心这里的表现。是的,它会动态创建一个临时集,但因为它没有被存储,所以会收集垃圾。