Сравнение двух двумерных массивов

1

Я пытаюсь найти лучший способ сравнить два двумерных массива и написать элементы, которых нет в первом массиве.

В случае одномерного массива все легко:

a = [1, 2]
b = [1, 2, 3]
newArray = list(set(b) - set(a))

newArray в этом случае будет [3]

Трудности в этом случае. Например, у меня есть:

a = [[1, 2, 3], [1, 2, 5], [4, 6, 9]]
b = [[1, 2, 3], [1, 4, 5], [2, 6, 5]]

и я хочу получить newArray = [[1, 4, 5], [2, 6, 5]], но если я попробую newArray = list(set(b) - set(a)) это приведет к ошибке

TypeError: unhashable type: 'list'

Как я могу реализовать то же, что и в случае одномерного массива? Длина массивов может быть разной.

Теги:
arrays
multidimensional-array

2 ответа

2
Лучший ответ

Вам нужно преобразовать списки в формат, который можно использовать для хэширования, например, tuple:

>>> a = [[1, 2, 3], [1, 2, 5], [4, 6, 9]]
>>> b = [[1, 2, 3], [1, 4, 5], [2, 6, 5]]
>>> set(map(tuple, b)) - set(map(tuple, a))
set([(2, 6, 5), (1, 4, 5)])

Если результатом будет список списка, вы можете впоследствии преобразовать кортежи.

>>> list(map(list, set(map(tuple, b)) - set(map(tuple, a))))
[[2, 6, 5], [1, 4, 5]]

Как и ваш первоначальный подход, это имеет сложность O (n + m), n и m - длину списков, что делает его несколько быстрее, чем понимание вложенного списка, которое использует повторный поиск в списках, который имеет O (n), в противоположность для поиска в наборах с помощью O (1), но также с некоторыми накладными расходами для преобразования в кортежи, создания наборов и т.д. Независимо от того, будет ли он на самом деле быстрее и будет ли этот более сложный код, может зависеть от размера списков,

Если списки коротки, например, 10 элементов, понимание по спискам на самом деле является чуть-чуть быстрее, чем использование map и set, и совсем немного быстрее, чем map а также set и преобразование обратно в list:

>>> a = [[randint(0, 3) for _ in range(3)] for _ in range(10)]
>>> b = [[randint(0, 3) for _ in range(3)] for _ in range(10)]
>>> %timeit [x for x in b if x not in a]
100000 loops, best of 3: 13.8 us per loop
>>> %timeit set(map(tuple, b)) - set(map(tuple, a))
100000 loops, best of 3: 14 us per loop
>>> %timeit list(map(list, set(map(tuple, b)) - set(map(tuple, a))))
10000 loops, best of 3: 21.8 us per loop

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

>>> a = [[randint(0, 10) for _ in range(3)] for _ in range(1000)]
>>> b = [[randint(0, 10) for _ in range(3)] for _ in range(1000)]
>>> %timeit [x for x in b if x not in a]
10 loops, best of 3: 82.5 ms per loop
>>> %timeit set(map(tuple, b)) - set(map(tuple, a))
1000 loops, best of 3: 1.16 ms per loop
>>> %timeit list(map(list, set(map(tuple, b)) - set(map(tuple, a))))
1000 loops, best of 3: 1.43 ms per loop
  • 0
    Спасибо за такой подход, но ответ Генри Вуди самый простой. Прав ли я, что этот метод быстрее, чем [x for x in b if x not in a] ?
  • 0
    @SBrain Как часто, ответ "это зависит". Смотрите мое редактирование.
1

Вы можете использовать понимание списка для итерации через b и возвратных списков, не входящих в a. Вот пример:

a = [[1, 2, 3], [1, 2, 5], [4, 6, 9]]
b = [[1, 2, 3], [1, 4, 5], [2, 6, 5]]
new_array = [x for x in b if x not in a]

И new_array равно:

[[1, 4, 5], [2, 6, 5]]
  • 1
    Обратите внимание, что это имеет сложность O (n * m), которая может быть непосильной для очень длинных списков; для коротких списков это, конечно, хорошо, короче и более читабельно, чем использование set .
  • 2
    Да, временная сложность этого метода означает, что его не следует использовать, если a и b или их содержимое становятся большими, но для достаточно небольших случаев я думаю, что простота кода оправдывает его.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню