为什么评论显着加快?弹出,比较和长度检查不应该是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))
更新: 我把问题缩短了,因为代码明显变慢了我的问题。我不太关心在运行时被杀死的代码。
答案 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变为空,这样我从缩小的列表中获得了更高的性能,而只需要做我的删除工作中的一万分之一。
另外,你应该得到一个内存错误,因为你试图分配一个肯定比你的硬盘大得多的列表 - 更不用说你的记忆了。