簡體   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