Определите, имеют ли 2 списка одинаковые элементы, независимо от порядка? [дубликат]
этот вопрос уже есть ответ здесь:
извините за простой вопрос, но мне трудно найти ответ.
когда я сравниваю 2 списка, я хочу знать, если они "равны" в том, что они имеют то же самое содержание, но в другом порядке.
Ex:
x = ['a', 'b']
y = ['b', 'a']
Я хочу x == y
оценка для True
.
4 ответа:
вы можете просто проверить, равны ли мультинаборы с элементами x и y:
import collections collections.Counter(x) == collections.Counter(y)
для этого требуется элементы, которые должны быть hashable; во время выполнения будет в
O(n)
, гдеn
- размер списков.если элементы также уникальны, вы также можете конвертировать в наборы (то же асимптотическое время выполнения, может быть немного быстрее на практике):
set(x) == set(y)
если элементы не хэшируются, но сортируются, другая альтернатива (время выполнения в
O(n log n)
) этоsorted(x) == sorted(y)
если элементы не hashable, ни сортировки, можно использовать следующую вспомогательную функцию. Обратите внимание, что это будет довольно медленно (
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
в случае, если элементы 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.повторяю, при тех же условиях, которые использовал Аарон Холл в своей должность. Как и предполагалось, результаты неутешительны. Мой метод-последний.
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
Как упоминалось в комментариях выше, общий случай-это боль. Это довольно легко, если все элементы хэшируются или все элементы сортируются. Однако мне недавно пришлось попробовать решить общий случай. Вот мое решение. Я понял после публикации, что это дубликат решения выше, которое я пропустил на первом проходе. Во всяком случае, если вы используете срезы, а не список.удалить() вы можете сравнить неизменяемые последовательности.
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