后两者如何比前一种解决方案更好?
primes = (1, 2, 3, 5, 7)
# Classic solution
items = list(range(10))
for prime in primes:
items.remove(prime)
items
# List comprehension
items = list(range(10))
[item for item in items if item not in primes]
# Filter
items = list(range(10))
list(filter(lambda item: item not in primes, items))
这三个例子是我在一本书中看到的,它说第一个解决方案需要O(n * m)时间(n = len(项目),m = len(素数))而后两个采用O (n * 1)时间......导致第一个解决方案的50次比较(实际上稍好一些 - 40次比较),后者只有10次。
我不明白这一点。我不明白它是如何节省时间或内存的。
以下是本书中解释这一点的段落:
要从列表中删除或插入单个项目,Python需要复制 整个列表,特别是较大的列表。执行时 这只有一次,当然不是那么糟糕。但是当执行一个大的 删除次数,过滤器或列表理解要快得多 解决方案,因为如果结构合理,它只需要复制列表 一旦。 ....那么例子...... 对于大型项目列表,后两者要快得多。这是因为 操作要快得多。要比较使用n = len(项目)和m = len(素数), 第一个是O(m * n)= 5 * 10 = 50个操作,而后两个操作 O(n * 1)= 10 * 1 = 10次操作。
编辑:
这本书没有错。 primes = set((1, 2, 3, 5, 7))
是正确的声明,而不是primes = (1, 2, 3, 5, 7)
答案 0 :(得分:2)
如果图书中的代码与您发布的内容完全相同,则图书错误。
第一个例子有时间复杂度O(n*m)
,但其他两个例子也是如此。
如果primes
是set
(或dict
),则情况确实如此 - 在散列映射中使用in
运算符进行查找具有时间复杂度{{1 },但在O(1)
或list
中有tuple
!因此,O(n)
的总复杂性。
让我们通过一些测量来检查:
O(n*m)
请注意,t = tuple(range(10000))
l = list(t)
s = set(t)
d = {i:1 for i in l}
In [16]: %%timeit
4738 in t
....:
10000 loops, best of 3: 45.5 µs per loop
In [17]: %%timeit
4738 in l
....:
10000 loops, best of 3: 45.4 µs per loop
In [18]: %%timeit
4738 in s
....:
10000000 loops, best of 3: 36.9 ns per loop
In [19]: %%timeit
4738 in d
....:
10000000 loops, best of 3: 38 ns per loop
中的查询为set
(与~37ns
类似),比dict
/ list
快3个数量级,{{ 1}}。
答案 1 :(得分:2)
主要问题来自items.remove(prime)
。原因是因为Python的列表是可变长度数组,而不是链表。它们使用连续的内存块来引用其他对象。如果从块中的任何位置插入/移除元素,则块中的所有元素都必须移动到新的连续内存块(一些优化实现用于插入数组的开头或结尾)。 see the documentation here
循环遍历列表len(primes)
次,对于每次删除,您遍历项len(items)
次。如果元素存在,则将项目列表复制到新的连续块,不包括匹配的项目。将元素复制到新块有隐藏的成本。
另外两个示例遍历列表,将所有当前元素保留为原样。并根据提供的过滤器返回一个新列表。