为什么使用集合进行列表比较?

时间:2012-05-09 17:15:59

标签: python

我在寻找计算两个列表差异的方法时,只是阅读了另一个用户问题。

Python, compute list difference

我的问题是我为什么要这样做

def diff(a,b):
    b = set(b)
    return [aa for aa in a if aa not in b]

而不是

def diff(a,b):
    tmp = []
    for i in a:
        if(i not in b):
            tmp.append(i)
return tmp

编辑:刚注意到第二个diff函数实际上返回了相似之处。现在应该是正确的。

5 个答案:

答案 0 :(得分:8)

从算法的角度来看,构造集合需要O(n)而列表理解需要O(n)(因为测试集合中是否包含元素是O(1))。但是在第二个示例中,将O(n^2)循环遍历两个列表。所以无论编程语言如何,第一种方法都是优越的。

此外,python中的列表推导本身比for循环更快。这进一步降低了常数因子(并且也显着降低)。我可以在this post中总结其中的原因:

  

列表推导只能由...组成   表达,而不是陈述,是一个相当重要的因素,更少   每次迭代都需要幕后工作。另一个因素   是列表推导的基础迭代机制是   比执行for循环更接近C循环。

答案 1 :(得分:5)

这两个选项之间的主要区别在于使用set的选项渐近效率更高。

在合理有利的条件下,查找集合中的项目可以在O(1)时间内完成;查找列表中的项目需要O(n)时间。

第二个不太重要的区别是,一个版本使用列表推导,而另一个版本使用for循环。列表推导倾向于产生更紧凑的代码。它们也往往更有效率(尽管性能是一个问题,获得准确图片的唯一方法是运行基准测试)。

答案 2 :(得分:2)

列表理解通常被认为比创建列表时使用常规列表操作更有效。如果考虑内存,您可能需要使用生成器。

This提供了for - 循环,maplist comprehension的一些信息。 @benhoyt还提供了一个有用的link比较循环与列表理解。

但是,请注意,如果性能是一个特定的问题,那么您可能值得花时间/基准测试各种选项,以便根据您的特定要求选择最佳选项。

答案 3 :(得分:2)

使用该集通常更快,因为它只需要迭代b一次,而您的示例必须为b中的每个元素迭代a

答案 4 :(得分:2)

我做了一些测试:

test_lists.py

a = range(1, 1000)
b = range(2, 1002)

tmp = []
for i in a:
    if(i not in b):
        tmp.append(i)

test_set_list_comprehensions.py

a = range(1, 1000)
b = range(2, 1002)

b = set(b)
[aa for aa in a if aa not in b]

test_set.py

a = range(1, 1000)
b = range(2, 1002)
list(set(a).difference(set(b)))

这就是timeit所说的:

~$ python -m timeit 'import test_lists'
1000000 loops, best of 3: 0.671 usec per loop
~$ python -m timeit 'import test_set_list_comprehension'
1000000 loops, best of 3: 0.766 usec per loop
~$ python -m timeit 'import test_set'
1000000 loops, best of 3: 0.656 usec per loop

所以最好的一个似乎是:

test_set.py

a = range(1, 1000)
b = range(2, 1002)
list(set(a).difference(set(b)))