性能比较:插入与构建Python集操作

性能比较:插入与构建Python集操作,python,set,time-complexity,Python,Set,Time Complexity,在python中,使用 a) 从n个项目的列表构建一个集合 b) 在一个集合中插入n项 我找到了这一页(http://wiki.python.org/moin/TimeComplexity)但它没有足够的信息来断定哪个更快 看起来,在最坏的情况下,一次插入一个项目可能需要O(n*n)个时间(假设它使用dicts),在平均情况下需要O(n*1)。用列表初始化集合是否有任何性能改进?在复杂性方面-这肯定是相同的,因为两种方法的作用完全相同-在集合中插入n项 不同之处在于实现:从iterable初始化

在python中,使用 a) 从n个项目的列表构建一个集合 b) 在一个集合中插入n项

我找到了这一页(http://wiki.python.org/moin/TimeComplexity)但它没有足够的信息来断定哪个更快

看起来,在最坏的情况下,一次插入一个项目可能需要O(n*n)个时间(假设它使用dicts),在平均情况下需要O(n*1)。用列表初始化集合是否有任何性能改进?

在复杂性方面-这肯定是相同的,因为两种方法的作用完全相同-在集合中插入
n

不同之处在于实现:从iterable初始化的一个明显优势是,可以节省大量Python级别的函数调用—从iterable初始化完全在C级别(**)上完成

事实上,对500万个随机整数的列表进行的一些测试表明,逐个相加的速度较慢:

lst = [random.random() for i in xrange(5000000)]
set1 = set(lst)    # takes 2.4 seconds

set2 = set()       # takes 3.37 seconds
for item in lst:
    set2.add(item)

(**)查看集合代码(
Objects/setobject.c
),最终项目插入归结为调用
set\u add\u key
。从iterable初始化时,在紧密C循环中调用此函数:

while ((key = PyIter_Next(it)) != NULL) {
  if (set_add_key(so, key) == -1) {
    Py_DECREF(it);
    Py_DECREF(key);
    return -1;
  } 
  Py_DECREF(key);
}

另一方面,对
set.add
的每次调用都会调用属性查找,这将解析为C
set\u add
函数,该函数依次调用
set\u add\u key
。由于项添加本身相对较快(Python的哈希表实现非常高效),因此这些额外调用都会累积。

以下是使用
timeit
运行比较的结果。似乎set using list的初始化速度更快,想知道为什么会这样:

from timeit import timeit
timeit("set(a)","a=range(10)")
# 0.9944498532640864

timeit("for i in a:x.add(i)","a=range(10);x=set()")
# 1.6878826778265648
Python版本:2.7

在我的Thinkpad上:

$ python -V
Python 2.5.2
$ python -m timeit -s "l = range(1000)" "set(l)"
10000 loops, best of 3: 64.6 usec per loop
$ python -m timeit -s "l = range(1000)" "s = set()" "for i in l:s.add(i)"
1000 loops, best of 3: 307 usec per loop
In [37]: timeit.timeit('for a in x: y.add(a)',
                       'y=set(); x=range(10000)', number=10000)
Out[37]: 12.18006706237793

In [38]: timeit.timeit('y=set(x)', 'y=set(); x=range(10000)', number=10000)
Out[38]: 3.8137960433959961

似乎您想将从列表中构建一个集合与插入n个要设置的项进行比较,但第一行问题的意思有所不同。哎呀!感谢您指出sateesh。使用
timeit
,您很容易找到它。Python循环的性能比我预期的更接近,您可以通过创建一个包含对
set的引用的局部变量来更接近它。添加
并在循环中调用它,避免属性查找。在我的测试中,这只比使用
set()
构造函数慢15%左右!通过
s_add=s.add
python-m timeit-s“l=range(1000)”“s=set();s_add=s.add”“对于l:s_add(i)”,可以将第二个命令的速度提高2倍。