簡體   English   中英

什么是從python中的另一個排序列表中刪除排序列表的快速和pythonic / clean方式?

[英]What's a fast and pythonic/clean way of removing a sorted list from another sorted list in python?

我正在創建一個生成范圍(0,限制+ 1)中的素數列表的快速方法。 在函數中,我最終從名為primes的列表中刪除名為removable的列表中的所有整數。 我正在尋找一種快速和pythonic的方法去除整數,知道兩個列表總是排序。

我可能錯了,但我相信list.remove(n)遍歷列表,將每個元素與n進行比較。 意味着以下代碼在O(n ^ 2)時間內運行。

# removable and primes are both sorted lists of integers
for composite in removable:
    primes.remove(composite)

基於我的假設(這可能是錯誤的並且請確認這是否正確)以及兩個列表總是排序的事實,我認為以下代碼運行得更快,因為它只在O列表上循環一次(n)時間。 然而,它根本不是pythonic或干凈。

i = 0
j = 0
while i < len(primes) and j < len(removable):
    if primes[i] == removable[j]:
        primes = primes[:i] + primes[i+1:]
        j += 1
    else:
        i += 1

是否有內置函數或更簡單的方法? 什么是最快的方式?

附注:我實際上沒有對上面的函數或代碼進行計時。 此外,如果在過程中更改/銷毀可移除列表也無關緊要。

對於任何感興趣的人,全部功能如下:

import math

# returns a list of primes in range(0, limit+1)
def fastPrimeList(limit):
    if limit < 2:
        return list()
    sqrtLimit = int(math.ceil(math.sqrt(limit)))
    primes = [2] + range(3, limit+1, 2)
    index = 1
    while primes[index] <= sqrtLimit:
        removable = list()
        index2 = index
        while primes[index] * primes[index2] <= limit:
            composite = primes[index] * primes[index2]
            removable.append(composite)
            index2 += 1
        for composite in removable:
            primes.remove(composite)
        index += 1
    return primes

這是非常快速和干凈的,它進行O(n)集成員資格檢查,並且在攤銷時間內它以O(n)運行(第一行是O(n)攤銷,第二行是O(n * 1)攤銷,因為會員資格檢查是O(1)攤銷):

removable_set = set(removable)
primes = [p for p in primes if p not in removable_set]

以下是您的第二個解決方案的修改。 它執行O(n)基本操作(最壞情況):

tmp = []
i = j = 0
while i < len(primes) and j < len(removable):
    if primes[i] < removable[j]:
        tmp.append(primes[i])
        i += 1
    elif primes[i] == removable[j]:
        i += 1
    else:
        j += 1
primes[:i] = tmp
del tmp

請注意常量也很重要。 Python解釋器執行Python代碼非常慢(即使用大常量)。 第二種解決方案有很多Python代碼,對於n的小實際值,它確實比使用set s的解決方案慢,因為set操作是用C實現的,因此它們很快(即具有小的常量)。

如果您有多個工作解決方案,請在典型輸入大小上運行它們,並測量時間。 你可能會對他們的相對速度感到驚訝,通常這不是你預測的。

這里最重要的是刪除二次行為。 你有這個有兩個原因。

首先,調用remove在整個列表remove搜索要刪除的值。 這樣做需要線性時間,並且您對removable每個元素執行一次,因此您的總時間為O(NM) (其中Nprimes的長度, Mremovable的長度)。

其次,從列表中間刪除元素會強制您將列表的其余部分向上移動一個插槽。 所以,每一個都需要線性時間,你再做M次,所以再次是O(NM)


你怎么能避免這些?

首先,您需要利用排序,或者只使用允許您執行常量查找而不是線性時間的內容,例如set

對於第二個,您需要創建要刪除的索引列表,然后執行第二次傳遞以將每個元素一次性移動到適當數量的索引,或者只是構建新列表而不是嘗試改變原始 - 地點。

所以,這里有各種各樣的選擇。 哪一個最好? 幾乎可以肯定無關緊要; 將你的O(NM)時間改為O(N+M)可能足以讓你對結果滿意的優化。 但是如果你需要擠出更多性能,那么你必須實現所有這些並在真實數據上測試它們。

我認為其中唯一不明顯的是如何“使用排序”。 我們的想法是使用您在合並排序中使用的相同類型的staggered-zip迭代,如下所示:

def sorted_subtract(seq1, seq2):
    i1, i2 = 0, 0
    while i1 < len(seq1):
        if seq1[i1] != seq2[i2]:
            i2 += 1
            if i2 == len(seq2):
                yield from seq1[i1:]
                return
        else:
            yield seq1[i1]
            i1 += 1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM