确定2个列表是否具有相同的元素,而不pipe顺序如何?
对不起,这个简单的问题,但我很难find答案。
当我比较2个列表时,我想知道它们是否“相等”,因为它们具有相同的内容,但顺序不同。
例如:
x = ['a', 'b'] y = ['b', 'a']
我想x == y
评估为True
。
您可以简单地检查具有x和y元素的多重集是否相等:
import collections collections.Counter(x) == collections.Counter(y)
这要求元素是可散列的; 运行时将在O(n)
,其中n
是列表的大小。
如果元素也是唯一的,你也可以转换成集(相同的渐近运行时,可能在实践中快一点):
set(x) == set(y)
如果元素不可sorting,但可sorting,则另一种替代( O(n log n)
运行时)是
sorted(x) == sorted(y)
如果元素既不可sorting也不可sorting,那么可以使用下面的帮助函数。 请注意,它会很慢( 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个列表是否具有相同的元素,而不pipe顺序如何?
从你的例子推断:
x = ['a', 'b'] y = ['b', 'a']
这些列表中的元素不会被重复(它们是唯一的)以及可散列的(哪些string和其他某些不可变的python对象), 最直接和计算效率最高的答案是使用Python的内置集合(在语义上类似于math你可能已经在学校了解到)。
set(x) == set(y) # prefer this if elements are hashable
在元素可散列但不唯一的情况下, collections.Counter
语义上也可以作为一个multiset使用,但速度要慢得多 :
from collections import Counter Counter(x) == Counter(y)
喜欢使用sorted
:
sorted(x) == sorted(y)
如果元素是可订购的。 这将解释非唯一或不可哈希的情况,但这可能比使用集合要慢得多。
实证实验
经验实验得出结论,人们应该喜欢set
,然后sorted
。 只有selectCounter
如果你需要其他的东西,如计数或进一步使用作为multiset。
第一次设置:
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)
并testing:
>>> min(timeit.repeat(sets_equal)) 13.976069927215576 >>> min(timeit.repeat(counters_equal)) 73.17287588119507 >>> min(timeit.repeat(sorted_lists_equal)) 36.177085876464844
所以我们看到比较集是最快的解决scheme,比较sorting列表是第二快。
这似乎工作,虽然可能繁琐的大型名单。
>>> 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
还有一件事,我在亚伦·霍尔(Aaron Hall)在他的post中所用的相同条件下,用timeit.repeat作为基准。 怀疑,结果令人失望。 我的方法是最后一个。 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
正如上面的评论中所提到的,一般情况是一个痛苦。 如果所有项目都是可sorting的或者所有项目都是可sorting的,则相当容易。 不过,我最近不得不尝试解决一般情况。 这是我的解决scheme。 在发布之后,我意识到这是对第一遍错过的解决scheme的重复。 无论如何,如果你使用片而不是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