Как эффективно сравнить два неупорядоченных списка (не наборы) в Python?


a = [1, 2, 3, 1, 2, 3]
b = [3, 2, 1, 3, 2, 1]

a & b следует считать равными, потому что они имеют точно такие же элементы, только в другом порядке.

дело в том, что мои фактические списки будут состоять из объектов (экземпляров моего класса), а не целых чисел.

9 91

9 ответов:

O (n): The счетчик() метод лучше всего (если ваши объекты хэшируются):

def compare(s, t):
    return Counter(s) == Counter(t)

O (N log n): The отсортированный() метод следующий лучший (если ваши объекты упорядочиваются):

def compare(s, t):
    return sorted(s) == sorted(t)

O (n * n): если объекты не hashable, не доступен, вы можете использовать равенства:

def compare(s, t):
    t = list(t)   # make a mutable copy
    try:
        for elem in s:
            t.remove(elem)
    except ValueError:
        return False
    return not t

Вы можете сортировать как:

sorted(a) == sorted(b)

A подсчет вроде также может быть более эффективным (но и она требует объекта, который должен быть hashable).

>>> from collections import Counter
>>> a = [1, 2, 3, 1, 2, 3]
>>> b = [3, 2, 1, 3, 2, 1]
>>> print (Counter(a) == Counter(b))
True

Если вы знаете, что элементы всегда хэшируются, вы можете использовать Counter() который является O (n)
Если вы знаете, что элементы всегда сортируются, вы можете использовать sorted() который является O (N log n)

В общем случае вы не можете полагаться на возможность сортировки или иметь элементы, поэтому вам нужен такой резерв, который, к сожалению, O (n^2)

len(a)==len(b) and all(a.count(i)==b.count(i) for i in a)

лучший способ сделать это, сортируя списки и сравнивая их. (Используя Counter не работает с объектами, которые не hashable.) Это просто для целых чисел:

sorted(a) == sorted(b)

это становится немного сложнее с произвольными объектами. Если вы заботитесь об идентичности объекта, то есть, является ли то же самое объекты находятся в обоих списках, вы можете использовать

Если список содержит элементы, которые не hashable (например, список объектов), вы могли бы использовать Счетчик Класс и функция id (), такие как:

from collections import Counter
...
if Counter(map(id,a)) == Counter(map(id,b)):
    print("Lists a and b contain the same objects")

если сравнение должно выполняться в контексте тестирования, используйте assertCountEqual(a, b) (py>=3.2) и assertItemsEqual(a, b) (2.7<=py<3.2).

работает на последовательностях недоступных объектов тоже.

https://docs.python.org/3.5/library/unittest.html#unittest.TestCase.assertCountEqual

assertCountEqual(первый, второй, msg=нет)

проверьте, что первая последовательность содержит те же элементы, что и вторая, независимо от их порядка. Когда они этого не делают, будет сгенерировано сообщение об ошибке с перечислением различий между последовательностями.

повторяющиеся элементы не игнорируются при сравнении первого и второго. Он проверяет, имеет ли каждый элемент то же самое количество в обеих последовательностях. Эквивалентно: assertEqual(Counter(list(first)), Counter(list (second))) но также работает с последовательностями недоступных объектов.

новое в версии 3.2.

или в 2.7: https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.assertItemsEqual

пусть a, b перечисляет

def ass_equal(a,b):
try:
    map(lambda x: a.pop(a.index(x)), b) # try to remove all the elements of b from a, on fail, throw exception
    if len(a) == 0: # if a is empty, means that b has removed them all
        return True 
except:
    return False # b failed to remove some items from a

Не нужно делать из них hashable или отсортировать их.

Я надеюсь, что ниже кусок кода может работать в вашем случае :-

if ((len(a) == len(b)) and
   (all(i in a for i in b))):
    print 'True'
else:
    print 'False'

Это гарантирует, что все элементы в обоих списках a & b такие же, независимо от того, находятся ли они в том же порядке или нет.

для лучшего понимания, обратитесь к моему ответу в этот вопрос