Python中len()和pop()的效率

时间:2013-10-18 04:16:29

标签: python performance

为什么评论显着加快?弹出,比较和长度检查不应该是O(1)吗?这会显着影响速度吗?

#! /usr/bin/python                                                                
import math                                                                       

pmarbs = []                                                                       
pows = 49                                                                         
pmarbs.append("W")                                                                
inc = 1                                                                           
for i in range(pows):                                                             
    count = 0                                                                     
    j = 0                                                                         
    ran = int(pow(2, i))                                                          
    marker = len(pmarbs) - inc                                                    
    while (j < ran):                                                              
        #potential marble choice                                                  
        pot = pmarbs[marker - j]                                                  
        pot1 = pot + "W"                                                          
        pot2 = pot + "B"                                                          

        if (pot2.count('W') < pot2.count('B')) and (len(pot2) > (i+1)):           
            count += 1                                                            
        else:                                                                     
            pmarbs.append(pot2)                                                   

        pmarbs.append(pot1)                                                       
#        if(len(pmarbs[0]) < i):                                                  
#           pmarbs.pop(0)                                                         
#           marker -= 1                                                           
        j += 1                                                                    


    if (count != 0):                                                              
        print(count)                                                              
        print("length of pmarbs = %s" % len(pmarbs)) 

更新: 我把问题缩短了,因为代码明显变慢了我的问题。我不太关心在运行时被杀死的代码。

2 个答案:

答案 0 :(得分:9)

回答部分问题:从列表的末尾(结尾)弹出CPython需要一段时间,但是从左端弹出(.pop(0))需要时间与列表长度成正比: all the_list[1:]中的元素在物理上向左移动一个位置。

如果您需要经常删除索引位置0,那么使用collections.deque的实例要好得多。 Deques支持从两端有效推送和弹出。

BTW,当我运行该程序时,我得到一个干净的例外:

...
length of pmarbs = 8306108
Traceback (most recent call last):
  File "xxx.py", line 22, in <module>
    pmarbs.append(pot2)
MemoryError

碰巧是在一个32位的Windows机器上。这并不让我感到惊讶;-)

答案 1 :(得分:5)

list.pop(index)O(n)操作,因为从列表中删除值后,您必须将列表中每个其他值的内存位置移到一个上。在大型列表上反复调用pop是浪费计算周期的好方法。如果你绝对必须一遍又一遍地从大型列表的前面删除collections.deque,这将为你提供更快的插入和删除。

len()O(1) ,因为删除是O(n),因为如果确保列表中的所有值都在内存中分配,则彼此相邻,列表的总长度只是尾部的内存位置 - 头部的内存位置。如果您不关心len()和类似操作的效果,那么您可以使用linked list进行常量时间插入和删除 - 这只会使len()O(n) }和pop()成为O(1)(你会得到一些其他时髦的东西,例如O(n)查找)。

我所说的关于pop()的所有内容也适用于insert() - 除了append(),通常需要O(1)

我最近处理过一个问题,需要从一个非常大的列表中删除大量元素(大约10,000,000个整数),并且每次我需要删除某些内容时,我的初始哑实现只使用了pop() - 结果证明不是完全工作,因为O(n)甚至花了一个算法循环,这本身需要n次。

我的解决方案是创建一个名为set()的{​​{1}},其中我保留了所有“已删除”元素的索引。我有一些辅助函数可以帮助我不必考虑跳过这些函数,所以我的算法并没有太难看。最终做的是每10,000次迭代执行一次ignore传递以删除O(n)中的所有元素并再次使ignore变为空,这样我从缩小的列表中获得了更高的性能,而只需要做我的删除工作中的一万分之一。

另外,你应该得到一个内存错误,因为你试图分配一个肯定比你的硬盘大得多的列表 - 更不用说你的记忆了。