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

时间:2012-01-15 00:39:10

标签: python list equality python-2.x

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

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

例如:

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

我希望x == y评估为True

4 个答案:

答案 0 :(得分:134)

您可以简单地检查具有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)

如果元素既不可散列也不可排序,则可以使用以下辅助函数。请注意,它会非常缓慢(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

答案 1 :(得分:17)

  

确定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

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

答案 2 :(得分:1)

这似乎有效,但对于大型列表来说可能很麻烦。

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

还有一件事,我用timeit.repeat对我的解决方案进行了基准测试,其条件与Aaron Hall在他的帖子中使用的条件相同。怀疑,结果令人失望。我的方法是最后一个。 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

答案 3 :(得分:0)

如上文评论中所述,一般情况是痛苦。如果所有物品都可以清洗或所有物品都可以分类,那就相当容易了。但是我最近不得不尝试解决一般情况。这是我的解决方案。我在发帖后意识到这是一个重复的解决方案,我在第一次通过时错过了。无论如何,如果你使用切片而不是list.remove(),你可以比较不可变序列。

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