對不起,這個簡單的問題,但我很難找到答案。確定2個列表是否具有相同的元素,而不管順序如何?
當我比較2個列表時,我想知道它們是否「相等」,因爲它們具有相同的內容,但順序不同。
例:
x = ['a', 'b']
y = ['b', 'a']
我想x == y
,以評估True
。
對不起,這個簡單的問題,但我很難找到答案。確定2個列表是否具有相同的元素,而不管順序如何?
當我比較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)
如果元素不是可哈希,但排序,另一個替代(在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
確定是否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
還有一兩件事,我基準我與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
正如上面評論所述,一般情況下是一種痛苦。如果所有項目都是可排序的或所有項目都可排序,則相當容易。不過,我最近不得不嘗試解決一般情況。這是我的解決方案。發佈後我意識到這是對第一遍錯過的解決方案的重複。無論如何,如果你使用片而不是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
因爲您的方法是O(N^2),這應該不會讓您感到驚訝,它比O(N)或O(N * log N)大得多。 對於B(N個元素)的每個元素,它檢查A(N個元素)的所有元素。然後檢查的次數是N * N。 – RobMcZag 2016-03-03 18:52:15