确定2个列表是否具有相同的元素,而不管顺序如何?

很抱歉问了这么简单的问题,但我很难找到答案。

当我比较两个列表时,我想知道它们是否“相等”,因为它们的内容相同,但顺序不同。

例如:

x = ['a', 'b']
y = ['b', 'a']

我要 x == y评估到 True

239409 次浏览

你可以简单地检查 x 和 y 元素的多重集是否相等:

import collections
collections.Counter(x) == collections.Counter(y)

这要求元素是散列的; 运行时在 O(n)中,其中 n是列表的大小。

如果元素也是唯一的,你也可以转换成集合(相同的渐近运行时,实际上可能会快一点) :

set(x) == set(y)

如果元素不是散列的,而是可排序的,那么另一种选择(O(n log n)中的运行时)是

sorted(x) == sorted(y)

如果元素既不是散列的也不是可排序的,那么可以使用以下 helper 函数。请注意,它将非常慢(O(n²)) ,通常应该在不可散列和不可排序元素这种深奥的情况之外使用 没有

def equal_ignore_order(a, b):
""" Use only when elements are neither hashable nor sortable! """
unmatched = list(b)
for element in a:
try:
unmatched.remove(element)
except ValueError:
return False
return not unmatched

确定2个列表是否具有相同的元素,而不管顺序如何?

从你的例子推断:

x = ['a', 'b']
y = ['b', 'a']

列表中的元素不会被重复(它们是唯一的) ,也不会被散列(字符串和其他一些不可变的 Python 对象是什么) ,最直接和计算效率最高的答案使用 Python 的内建集(语义上类似于你在学校里学过的数学集)。

set(x) == set(y) # prefer this if elements are hashable

如果元素是散列的,但不是唯一的,那么 collections.Counter在语义上也可以作为一个多集,但是 慢得多:

from collections import Counter
Counter(x) == Counter(y)

更喜欢使用 sorted:

sorted(x) == sorted(y)

如果元素是有序的。这将解释非唯一或非散列的情况,但这可能比使用集慢得多。

经验实验

实验结果表明,人们更喜欢 set,而不是 sorted。如果您需要其他东西,如计数或进一步使用作为一个多集,那么只选择 Counter

第一步:

import timeit
import random
from collections import Counter


data = [str(random.randint(0, 100000)) for i in xrange(100)]
data2 = data[:]     # copy the list into a new one


def sets_equal():
return set(data) == set(data2)


def counters_equal():
return Counter(data) == Counter(data2)


def sorted_lists_equal():
return sorted(data) == sorted(data2)

测试:

>>> min(timeit.repeat(sets_equal))
13.976069927215576
>>> min(timeit.repeat(counters_equal))
73.17287588119507
>>> min(timeit.repeat(sorted_lists_equal))
36.177085876464844

所以我们看到比较集是最快的解决方案,比较排序列表是第二快的。

这似乎有效,尽管对于大型列表来说可能有些麻烦。

>>> A = [0, 1]
>>> B = [1, 0]
>>> C = [0, 2]
>>> not sum([not i in A for i in B])
True
>>> not sum([not i in A for i in C])
False
>>>

但是,如果每个列表 必须的都包含其他列表的所有元素,那么上面的代码就有问题了。

>>> A = [0, 1, 2]
>>> not sum([not i in A for i in B])
True

len(A) != len(B)和本例中的 len(A) > len(B)出现问题时,可以再添加一条语句来避免这个问题。

>>> not sum([not i in A for i in B]) if len(A) == len(B) else False
False

还有一件事,我用时间对我的解决方案进行了基准测试。正如所怀疑的那样,结果令人失望。我的方法是最后一个。那就 set(x) == set(y)吧。

>>> def foocomprehend(): return not sum([not i in data for i in data2])
>>> min(timeit.repeat('fooset()', 'from __main__ import fooset, foocount, foocomprehend'))
25.2893661496
>>> min(timeit.repeat('foosort()', 'from __main__ import fooset, foocount, foocomprehend'))
94.3974742993
>>> min(timeit.repeat('foocomprehend()', 'from __main__ import fooset, foocount, foocomprehend'))
187.224562545

正如上面的评论所提到的,一般情况下是一个痛苦的问题。如果所有项目都是散列的,或者所有项目都是可分类的,那么这是相当容易的。然而,我最近不得不尝试解决一般情况。这是我的解决办法。我意识到后张贴,这是一个复制的解决方案以上,我错过了第一次通过。无论如何,如果使用切片而不是 list.move () ,就可以比较不可变序列。

def sequences_contain_same_items(a, b):
for item in a:
try:
i = b.index(item)
except ValueError:
return False
b = b[:i] + b[i+1:]
return not b