如何有效地比较两个无序列表(不是集合) ?

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

A & b 应该被认为是相等的,因为它们具有完全相同的元素,只是顺序不同。

问题是,我的实际列表将包含对象(我的类实例) ,而不是整数。

116037 次浏览

你可以对两者进行排序:

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

最好的方法是对列表进行排序并进行比较。(使用Counter对不可哈希的对象无效。)这对于整数来说很简单:

sorted(a) == sorted(b)

对于任意对象,这就有点棘手了。如果你关心对象的身份,即相同对象是否在两个列表中,你可以使用id()函数作为排序键。

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

(在Python 2。x你实际上不需要key=参数,因为你可以比较任何对象与任何对象。排序是任意的,但很稳定,所以它很适合这个目的;对象的顺序并不重要,重要的是两个列表的顺序是相同的。然而,在Python 3中,在许多情况下不允许比较不同类型的对象——例如,你不能比较字符串和整数——所以如果你有各种类型的对象,最好显式地使用对象的ID。)

另一方面,如果你想通过值,来比较列表中的对象,首先你需要定义“value”对对象的意义。然后需要某种方法将其作为键提供(对于Python 3,作为一致类型)。一种适用于许多任意对象的潜在方法是根据它们的repr()进行排序。当然,这可能会浪费大量额外的时间和内存来为大型列表构建repr()字符串等等。

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

如果对象都是你自己的类型,你可以在它们上定义__lt__(),这样对象就知道如何将自己与其他对象进行比较。然后你可以对它们进行排序,而不用担心key=参数。当然,你也可以定义__hash__()并使用Counter,这样会更快。

如果你知道项目总是可哈希的,你可以使用Counter(),它是O(n)
如果你知道项目总是可排序的,你可以使用sorted(),它是O(n log n)

一般情况下,你不能依赖于排序能力,或者拥有元素,所以你需要一个像这样的后备方案,不幸的是,它是O(n²)

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

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):如果对象既不是可哈希的,也不是可排序的,你可以使用equal:

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

让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

不需要将它们设置为可哈希或排序。

我希望下面的代码可能在你的情况下工作:-

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

这将确保两个列表中的所有元素a &b是相同的,不管它们的顺序是否相同。

为了更好地理解,请参考我在这个问题中的回答

如果要在测试上下文中执行比较,则使用assertCountEqual(a, b) (py>=3.2)和assertItemsEqual(a, b) (2.7<=py<3.2)。

也适用于不可哈希对象的序列。

如果你必须在测试中这样做: https://docs.python.org/3.5/library/unittest.html#unittest.TestCase.assertCountEqual < / p >

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 < / p >

在测试之外,我会推荐Counter方法。

如果列表包含不可哈希的项(例如对象列表),则可以使用计数器类和id()函数,例如:

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

您可以编写自己的函数来比较这些列表。

让我们得到两个列表。

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