如何在Python中有效地比较两个无序列表(而不是集合)?
a和b应该被认为是相等的,因为它们有完全相同的元素,只是顺序不同 问题是,我的实际列表将由对象(我的类实例)组成,而不是整数。您可以对两者进行排序:如何在Python中有效地比较两个无序列表(而不是集合)?,python,algorithm,list,comparison,Python,Algorithm,List,Comparison,a和b应该被认为是相等的,因为它们有完全相同的元素,只是顺序不同 问题是,我的实际列表将由对象(我的类实例)组成,而不是整数。您可以对两者进行排序: a = [1, 2, 3, 1, 2, 3] b = [3, 2, 1, 3, 2, 1] A也可能更有效(但它要求对象是可散列的) 最好的方法是对列表进行排序并进行比较。(使用计数器无法处理不可散列的对象。)这对于整数很简单: >>> from collections import Counter >>> a
a = [1, 2, 3, 1, 2, 3]
b = [3, 2, 1, 3, 2, 1]
A也可能更有效(但它要求对象是可散列的)
最好的方法是对列表进行排序并进行比较。(使用
计数器
无法处理不可散列的对象。)这对于整数很简单:
>>> from collections import Counter
>>> a = [1, 2, 3, 1, 2, 3]
>>> b = [3, 2, 1, 3, 2, 1]
>>> print (Counter(a) == Counter(b))
True
使用任意对象会变得有点棘手。如果您关心对象标识,即两个列表中是否有相同的对象,则可以使用id()
函数作为排序键
sorted(a) == sorted(b)
(在Python2.x中,您实际上不需要key=
参数,因为您可以将任何对象与任何对象进行比较。排序是任意的,但很稳定,因此它可以很好地用于此目的;不管对象的顺序是什么,只是两个列表的排序是相同的。不过,在Python3中,比较不同类型的对象pes在许多情况下是不允许的——例如,您不能将字符串与整数进行比较——因此,如果您将拥有各种类型的对象,最好显式使用对象的ID。)
另一方面,如果要按值比较列表中的对象,首先需要定义“值”对对象的含义。然后需要某种方法将其作为键提供(对于Python 3,作为一致类型)。一种可能适用于许多任意对象的方法是按其repr()排序
。当然,这可能会浪费大量额外的时间和内存来构建大型列表的repr()
字符串等等
sorted(a, key=id) == sorted(b, key==id)
如果对象都是您自己的类型,您可以在它们上定义
\uu lt\uu()
,以便对象知道如何将自己与其他对象进行比较。然后您可以对它们进行排序,而不必担心键=
参数。当然,您也可以定义\uu散列()
并使用计数器
,这将更快。如果您知道项目总是可散列的,您可以使用一个计数器()
,它是O(n)如果您知道项目总是可排序的,那么可以使用
sorted()
,它是O(n log n)
在一般情况下,您不能依赖于能够排序或拥有元素,因此您需要这样的回退,不幸的是O(n^2)
O(n):该方法是最好的(如果您的对象是可散列的):
O(n log n):该方法是次优方法(如果您的对象是可订购的):
O(n*n):如果对象既不可散列也不可排序,则可以使用相等:
def compare(s, t):
return sorted(s) == sorted(t)
让a,b列出
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
无需对它们进行散列或排序。我希望下面的代码可能适用于您的情况:-
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
这将确保两个列表a
和b
中的所有元素都相同,无论它们的顺序是否相同
为了更好地理解,请参考我在中的答案。如果要在测试环境中执行比较,请使用(
py>=3.2
)和(2.7,如果必须在测试中执行此操作:
assertCountEqual(第一,第二,msg=None)
测试序列first是否包含与second相同的元素,而不考虑它们的顺序。如果它们不包含,则会生成一条错误消息,列出序列之间的差异
比较第一个和第二个时不会忽略重复的元素。它验证两个序列中的每个元素是否具有相同的计数。等效于:assertEqual(计数器(列表(第一)),计数器(列表(第二)),但也适用于不可损坏对象的序列
3.2版中的新版本
或在2.7中:
在测试之外,我建议使用计数器方法。如果列表包含不可散列的项(例如对象列表),则可以使用和id()函数,例如:
if ((len(a) == len(b)) and
(all(i in a for i in b))):
print 'True'
else:
print 'False'
您可以编写自己的函数来比较列表
我们来拿两份清单
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
对象是如何比较的?实际列表的预期大小是多少?被比较的列表的大小是可比的还是非常不同的?您希望大多数列表匹配还是不匹配?可以选中len()
s首先。计数器确实使用哈希,但对象本身并不是不可损坏的。您只需实现一个合理的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu也不适用于子集/超集测试中已覆盖比较运算符的集合。是的,但正如其他一些海报所述,这是O(n**2),因此仅当其他方法不起作用时才应使用。它还假设a
支持pop
(是可变的)和索引
(是序列).Raymond的都不假设,而gnibbler的只假设序列。谢谢。我将每个对象转换为字符串,然后使用Counter()方法。嘿@Raymond,我最近在一次采访中遇到了这个问题,我使用了sorted()
,不可否认不知道计数器
。面试官坚持认为有一种更有效的方法,很明显我画了一张空白。在python 3中使用timeit
模块进行了大量测试后,排序在整数列表中的速度始终更快。在1k个项目的列表中,速度大约慢1.5%,在短列表中,速度慢10%tems,慢7.5%。想法?对于短名单,大O分析通常是不相关的,因为时间由恒定因素决定。对于长名单,我怀疑你的基准测试有问题。对于100个整数,每个重复5次,我得到:127 usec
if ((len(a) == len(b)) and
(all(i in a for i in b))):
print 'True'
else:
print 'False'
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)