簡體   English   中英

如何有效地比較兩個無序列表(不是集合)?

[英]How to efficiently compare two unordered lists (not sets)?

a = [1, 2, 3, 1, 2, 3]
b = [3, 2, 1, 3, 2, 1]

a & b 應該被認為是相等的,因為它們具有完全相同的元素,只是順序不同。

問題是,我的實際列表將包含對象(我的 class 實例),而不是整數。

O(n)Counter()方法是最好的(如果您的對象是可散列的):

def compare(s, t):
    return Counter(s) == Counter(t)

O(n log n)sorted()方法是次佳的(如果您的對象是可訂購的):

def compare(s, t):
    return sorted(s) == sorted(t)

O(n * n) :如果對象既不是可散列的,也不是可排序的,您可以使用相等:

def compare(s, t):
    t = list(t)   # make a mutable copy
    try:
        for elem in s:
            t.remove(elem)
    except ValueError:
        return False
    return not t

您可以對兩者進行排序:

sorted(a) == sorted(b)

計數排序也可能更有效(但它要求對象是可散列的)。

>>> from collections import Counter
>>> a = [1, 2, 3, 1, 2, 3]
>>> b = [3, 2, 1, 3, 2, 1]
>>> print (Counter(a) == Counter(b))
True

如果您知道這些項目始終是可散列的,則可以使用 O(n) 的Counter() )
如果您知道這些項目始終是可排序的,則可以使用sorted() ,即 O(n log n)

在一般情況下,您不能依賴能夠排序或擁有元素,因此您需要像這樣的后備,不幸的是 O(n^2)

len(a)==len(b) and all(a.count(i)==b.count(i) for i in a)

如果您必須在測試中執行此操作: https ://docs.python.org/3.5/library/unittest.html#unittest.TestCase.assertCountEqual

assertCountEqual(first, second, msg=None)

測試該序列首先包含與第二個相同的元素,無論它們的順序如何。 如果不這樣做,將生成列出序列之間差異的錯誤消息。

比較第一個和第二個時不會忽略重復的元素。 它驗證每個元素在兩個序列中是否具有相同的計數。 等價於:assertEqual(Counter(list(first)), Counter(list(second))) 但也適用於不可散列對象的序列。

3.2 版中的新功能。

或在 2.7 中: https ://docs.python.org/2.7/library/unittest.html#unittest.TestCase.assertItemsEqual

在測試之外,我會推薦Counter方法。

如果要在測試上下文中執行比較,請使用assertCountEqual(a, b) ( py>=3.2 ) 和assertItemsEqual(a, b) ( 2.7<=py<3.2 )。

也適用於不可散列對象的序列。

最好的方法是對列表進行排序並進行比較。 (使用Counter不適用於不可散列的對象。)這對於整數來說很簡單:

sorted(a) == sorted(b)

對於任意對象,它會變得有點棘手。 如果您關心對象標識,即兩個列表中是否有相同的對象,您可以使用id()函數作為排序鍵。

sorted(a, key=id) == sorted(b, key==id)

(在 Python 2.x 中,您實際上不需要key=參數,因為您可以將任何對象與任何對象進行比較。排序是任意但穩定的,因此它可以很好地用於此目的;不管是什么順序對象在, 只是兩個列表的順序相同. 但是, 在 Python 3 中, 在許多情況下不允許比較不同類型的對象 - 例如, 你不能將字符串與整數進行比較 - 所以如果你願意有各種類型的對象,最好顯式使用對象的 ID。)

另一方面,如果要按比較列表中的對象,首先需要定義“值”對對象的含義。 然后你需要一些方法來提供它作為一個鍵(對於 Python 3,作為一個一致的類型)。 適用於許多任意對象的一種潛在方法是按它們的repr()排序。 當然,這可能會浪費大量額外的時間和內存來為大型列表等構建repr()字符串。

sorted(a, key=repr) == sorted(b, key==repr)

如果對象都是您自己的類型,您可以在它們上定義__lt__()以便對象知道如何將自己與其他對象進行比較。 然后你可以對它們進行排序,而不用擔心key=參數。 當然你也可以定義__hash__()並使用Counter ,這樣會更快。

如果列表包含不可散列的項目(例如對象列表),您可能可以使用Counter 類和 id() 函數,例如:

from collections import Counter
...
if Counter(map(id,a)) == Counter(map(id,b)):
    print("Lists a and b contain the same objects")

我希望下面的代碼可能適用於您的情況:-

if ((len(a) == len(b)) and
   (all(i in a for i in b))):
    print 'True'
else:
    print 'False'

這將確保列表ab中的所有元素都相同,無論它們的順序是否相同。

為了更好地理解,請參閱我在這個問題中的回答

讓 a,b 列出

def ass_equal(a,b):
try:
    map(lambda x: a.pop(a.index(x)), b) # try to remove all the elements of b from a, on fail, throw exception
    if len(a) == 0: # if a is empty, means that b has removed them all
        return True 
except:
    return False # b failed to remove some items from a

無需使它們可散列或對它們進行排序。

您可以編寫自己的函數來比較列表。

讓我們得到兩個列表。

list_1=['John', 'Doe'] 
list_2=['Doe','Joe']

首先,我們定義一個空字典,統計列表項並寫入字典。

def count_list(list_items):
    empty_dict={}
    for list_item in list_items:
        list_item=list_item.strip()
        if list_item not in empty_dict:
            empty_dict[list_item]=1
        else:
            empty_dict[list_item]+=1
    return empty_dict


        

之后,我們將使用以下函數比較兩個列表。

def compare_list(list_1, list_2):
    if count_list(list_1)==count_list(list_2):
        return True
    return False
compare_list(list_1,list_2)
from collections import defaultdict

def _list_eq(a: list, b: list) -> bool:
    if len(a) != len(b):
        return False
    b_set = set(b)
    a_map = defaultdict(lambda: 0)
    b_map = defaultdict(lambda: 0)
    for item1, item2 in zip(a, b):
        if item1 not in b_set:
            return False
        a_map[item1] += 1
        b_map[item2] += 1
    return a_map == b_map

如果數據高度無序,排序可能會非常慢(當項目具有某種程度的排序時,timsort 會特別好)。 對兩者進行排序還需要對兩個列表進行完全迭代。

與其改變一個列表,不如分配一個集合並進行左-->右成員資格檢查,同時記錄沿途存在的每個項目的數量:

  • 如果兩個列表的長度不同,您可以短路並立即返回False
  • 如果您點擊列表a中不在列表b中的任何項目,您可以返回False
  • 如果您通過所有項目,那么您可以比較a_mapb_map的值以找出它們是否匹配。

這允許您在迭代兩個列表之前很久就在許多情況下短路。

插入這個:

def lists_equal(l1: list, l2: list) -> bool:
    """

    import collections
    compare = lambda x, y: collections.Counter(x) == collections.Counter(y)
    ref:
        - https://stackoverflow.com/questions/9623114/check-if-two-unordered-lists-are-equal
        - https://stackoverflow.com/questions/7828867/how-to-efficiently-compare-two-unordered-lists-not-sets
    """
    compare = lambda x, y: collections.Counter(x) == collections.Counter(y)
    set_comp = set(l1) == set(l2)  # removes duplicates, so returns true when not sometimes :(
    multiset_comp = compare(l1, l2)  # approximates multiset
    return set_comp and multiset_comp  #set_comp is gere in case the compare function doesn't work

暫無
暫無

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

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