你并不需要在两个list
秒值进行转换,以set
S,只有一个。我认为跳过不必要的转换使其更具可读性和优雅性。
所以,要么:
set(a).intersection(b)
或者:
s = set(a)
any(e in s for e in b)
后者的优点是短路只要它找到一个匹配,更好地表达逻辑,并返回True
或False
而不是非虚假或错误的set
,但它是两行而不是一个,如果那样会困扰你。我不知道这个优点是否消除了将循环放入生成器表达式而不是C函数内部的代价。
绩效结果与list
s这个小几乎是毫无意义的,所以让我们试试这个:
In [373]: a=[random.choice(string.ascii_lowercase) for _ in range(10000)]
In [374]: b=[random.choice(string.ascii_lowercase) for _ in range(10000)]
In [375]: %timeit(set(a))
10000 loops, best of 3: 180 us per loop
In [376]: s=set(a) # since all versions need to do this
In [391]: %timeit(s & set(b))
1000000 loops, best of 3: 178 us per loop
In [392]: %timeit(s.intersection(b))
1000000 loops, best of 3: 247 us per loop
In [393]: %timeit(discard(e in s for e in b))
1000000 loops, best of 3: 550 ns per loop
In [394]: %timeit(any(e in s for e in b))
1000000 loops, best of 3: 749 ns per loop
In [395]: %timeit(any(e in a for e in b))
1000000 loops, best of 3: 1.42 us per loop
为了把这些数字都在纳秒的规模,加上早在set(a)
的成本是除了最后需要和比较来自三个Python版本的相同测试(Apple股票CPython 2.7.2,python.org CPython 3.3.0,Homebrew PyPy 1.9.0/2.7。2,所有的64位Mac版本):
CP272 CP330 PyPy
s & set(b) 358000 316000 180500
s.intersection(b) 427000 459000 180900
discard(genexp) 180550 157341 90094
any(genexp) 180749 157334 90208
any(list-genexp) 1420 686 960
现在我想到了这一点,这是完全合理的。很早就发生碰撞的几率非常高,因此将整个事件转换为集合的成本控制了一切。
这意味着我们需要一个新的测试,具有10000个唯一值。让我们重复这个测试:
In [29]: a, b = list(range(10000)), list(range(10000))
In [30]: random.shuffle(a)
In [31]: random.shuffle(b)
CP272 CP330 PyPy
s & set(b) 1277000 1168000 1141000
s.intersection(b) 1165000 1117000 2520000
discard(genexp) 1699000 1271000 770000
any(genexp) 389800 344543 320807
any(list-genexp) 62000 10400 1520
这些更合理。而且他们仍然有道理。如果你比较相同的10000个元素随机洗牌,你必须走多远?不足以使set
的成本 - 使这两个列表中的任何一个值得做,更不用说它们两个!
所以,让我们试着那里有没有一致的情况下:
In [43]: a=list(range(10000, 20000))
CP272 CP330 PyPy
s & set(b) 751000 770000 733000
s.intersection(b) 466000 530000 1920000
discard(genexp) 1246000 985000 749000
any(genexp) 1269000 966000 893000
any(list-genexp) 185000000 176000000 5870000
我不知道PyPy是怎么做的最后一个如此之快,但除此之外,这里没有惊喜。
那么,哪一个最好?很明显,如果你期望碰到很多碰撞,你希望尽可能地避免做集合 - 但是如果你期望碰撞少,你至少要做一组。如果你不知道,我认为最安全的赌注是any(genexp)
- 最坏的情况是它比最好的要差3倍,如果碰撞率很高,它会快很多。但你可以看看这些数字并亲自查看。或者,当然,更好的办法是将它们全部对照您期望遇到的实际测试数据。
是元素每个列表中是唯一的?如果是这样,你可以简单地使用'sets'来做到这一点。 – 2013-01-16 06:39:35
是的,列表中的元素是唯一的。 – Amyth
@Mike:等等...为什么你不能用set来做这件事,即使这些元素不唯一?您多次丢失元素存在的信息,但如果您关心的是元素存在,则不需要该信息。 (如果你这样做,你总是可以使用'Counter'来代替'set'来保留它。) – abarnert