繁体   English   中英

在 Python 的列表中查找第二大项目的更有效方法

[英]More Efficient Way to find the Second Largest Item in a List in Python

我为在整数列表中查找第二大项的简单任务编写了这个简单的代码:

def second_largest(input_list):
  input_list.sort()
  return input_list[-2]

但是,对于大型列表,此 function 可能真的无效,例如一百万个项目的运行时间超过 1.5 秒。

我知道这是因为 function更改了列表本身(使用 .sort 方法),这对于长列表可能非常低效。 如何在不使用更改列表的低效方法的情况下执行此任务?

谢谢大家。

以下情况如何:

lst = range(1000000)

largest, second_largest = lst[0], lst[0]
for x in lst:
    if x > largest:
        largest, second_largest = x, largest
    elif x > second_largest:
        second_largest = x
print(largest, second_largest) # 999999 999998

它只遍历一个可迭代对象一次,所以我希望它是有效的。 (当然,当len(lst) == 1时,它有一个极端情况。在这种情况下,人们可能会争辩说不应该定义second_largest 。)


对于 100000000 个项目,我有以下速度比较:

import time
import heapq

lst = list(range(100000000))

start = time.time()
largest, second_largest = lst[0], lst[0]
for x in lst:
    if x > largest:
        largest, second_largest = x, largest
    elif x > second_largest:
        second_largest = x
print(largest, second_largest) # 999999 999998
print("Elapsed:", time.time() - start) # Elapsed: 12.085833549499512

start = time.time()
print(heapq.nlargest(2, lst)[1]) # 99999998
print("Elapsed:", time.time() - start) # Elapsed: 17.82271456718445

弹出max,再次找到max:

my_list.pop(my_list.index(max(my_list)))
max(my_list)

正如@juanpa.arrivillaga 提到的,我们可以使用堆队列算法的heapq.nlargest 方法

import heapq

data = list(range(100))
data.append(100)
print(heapq.nlargest(2, data)[1])

Output:

99

免责声明:

如果数据包含重复值,它将返回唯一的第二大元素。

我发现基于np.argpartition的解决方案是最快的。 它确实需要 Numpy ,但它没有考虑将列表转换为 numpy 数组。 但是,最后,如果您希望进一步计算数字,您可能需要使用 numpy arrays 因为这些操作通常比列表操作快得多。 所以我假设你就是这种情况。


首先,您应该将列表转换为数组:

import numpy as np
v = list(range(10000000))
var = np.array(v)

然后我们可以使用 np.argpartition

%%time
ind = np.argpartition(var, -2)[-2]

CPU times: user 46.3 ms, sys: 25.9 ms, total: 72.2 ms
Wall time: 71.7 ms

ind = 9999998 ,所以结果似乎是正确的。

现在,如果我们在这里与其他建议进行比较:

%%time
v.pop(v.index(max(v)))
max(v)

CPU times: user 619 ms, sys: 1.96 ms, total: 621 ms
Wall time: 623 ms

大约慢 10 倍

%%time
heapq.nlargest(2,v)[1]

嗯……慢得多

最后一个

largest, second_largest = v[0], v[0]
for x in v:
    if x > largest:
        largest, second_largest = x, largest
    elif x > second_largest:
        second_largest = x
print(largest, second_largest) # 999999 999998

CPU times: user 1.41 s, sys: 0 ns, total: 1.41 s
Wall time: 1.41 s

再一次,慢了很多倍。


如果您打算在某处使用 arrays,那么np.argpartition解决方案似乎是最快的。 如果没有,您将花费大量 CPU 时间将您的列表转换为您将不再使用的数组(见下文)。 不过,它仍然优于其他一些解决方案。 如果我们考虑到数组的转换,我们会得到这样的结果:

%%time
var=np.array(v)
ind = np.argpartition(var, -2)[-2]

CPU times: user 867 ms, sys: 59 ms, total: 926 ms
Wall time: 924 ms

这个问题很好地解释了为什么这个解决方案更快。 原因是您只使用np.argpartition执行部分排序,而不是对列表进行完全排序。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM