Я пытаюсь найти лучший способ сравнить два двумерных массива и написать элементы, которых нет в первом массиве.
В случае одномерного массива все легко:
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'
Как я могу реализовать то же, что и в случае одномерного массива? Длина массивов может быть разной.
Вам нужно преобразовать списки в формат, который можно использовать для хэширования, например, 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
Вы можете использовать понимание списка для итерации через 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]]
set
.
a
и b
или их содержимое становятся большими, но для достаточно небольших случаев я думаю, что простота кода оправдывает его.
[x for x in b if x not in a]
?