Q值。编写一个返回数组中第二大数字的算法
a = [1, 2, 3, 4, 5]
print(max([x for x in a if x != max(a)]))
>> 4
我试图弄清楚这个算法是如何工作的,以及pythons内部魔术是否会像写一个线性算法一样有效,这个算法只是循环遍历列表a
一次并存储最高和第二高的值。
如果我错了,请纠正我:
对max(a)
的调用将是O(n)
[x for x in a]
也是O(n)
python是否足够智能来缓存max(a)
的值,或者这意味着算法的列表推导部分是O(n ^ 2)?
然后最后的max([listcomp])
将是另一个O(n),但这只会在理解完成后运行一次,所以最终的算法将是O(n ^ 2)?
内部是否有任何花哨的业务会缓存max(a)
值并导致此算法的运行速度比O(n ^ 2)快?
答案 0 :(得分:6)
找出答案的简单方法是计时。考虑这个时间码:
for i in range(1, 5):
a = list(range(10**i))
%timeit max([x for x in a if x != max(a)])
17.6 µs ± 178 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 698 µs ± 14.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 61.6 ms ± 340 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) 6.31 s ± 167 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
每次将元素数乘以10,运行时间增加100.这几乎肯定是O(n**2)
。对于O(n)
算法,运行时将随元素数量线性增加:
for i in range(1, 6):
a = list(range(10**i))
max_ = max(a)
%timeit max([x for x in a if x != max_])
4.82 µs ± 27.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 29 µs ± 161 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) 262 µs ± 3.89 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 2.42 ms ± 13 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 24.9 ms ± 231 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
但我不确定算法是否真的能满足要求。考虑列表a=[1,3,3]
,即使heapq
模块告诉我第二大元素是3
(不是1 - 您的算法返回的内容):
import heapq
>>> heapq.nlargest(2, [1,3,3])[0]
3
答案 1 :(得分:3)
python是否足够聪明,可以缓存max(a)或will的值 这意味着列表理解部分算法是
O(n^2)
?
不,因为,正如MSeifert在评论中所说,python不会对a
做出假设,因此不会缓存每次重新计算的max(a)
的值。
您可能需要考虑一个跟踪一次传递中最大的两个项目的实现。您需要编写显式的for
循环并执行此操作。这是来自GeeksForGeeks
的有用链接(我推荐)。
或者,您可以考虑多个遍历仍然在复杂性上呈线性的遍历。
In [1782]: a = [1, 2, 3, 4, 5]
In [1783]: max(set(a) - {max(a)}) # 3 linear traversals
Out[1783]: 4
这里有改进的余地,但正如我所说的,没有什么比明确的for
循环方法更好。