Во-первых, в состоянии if
вы уже делаете тяжелую работу, поэтому нет необходимости в предварительном тестировании. Во-вторых, неясно, какова ваша отправная точка, если это идентификаторы или электронные письма или что-то еще, и ваши конечные точки.
Но кажется, что самым чистым подходом было бы использовать set
полностью .
Я полагаю, вас устраивают идентификаторы (но тот же код будет работать для адресов электронной почты):
n = 3
a = set(range(1, n)) # *old* items
b = set(range(n - 1)) # *new* items
c = a - b # items present in b but not in a (added)
# {0}
d = b - a # items present in a but not in b (deleted)
# {2}
Теперь предположим, что отправной точкой является два list
(снова идентификаторы или адрес электронной почты не имеют значения, я просто предполагаю идентификаторы для простоты), и позвольте нам предположить, что вы хотите знать как добавленные, так и удаленные элементы. Есть несколько возможных подходов:
def diffs_set(a, b):
a = set(a)
b = set(b)
return a - b, b - a
def diffs_loop(a, b):
a = set(a)
b = set(b)
return [x for x in a if x in b], [x for x in b if x in a]
def diffs_loop2(a, b):
return [x for x in a if x in b], [x for x in b if x in a]
def diffs_np(a, b):
return np.setdiff1d(a, b, assume_unique=True), np.setdiff1d(b, a, assume_unique=True)
чьи тайминги для некоторых размеров ввода выглядят следующим образом:
funcs = diffs_set, diffs_loop, diffs_loop2, diffs_np
for n in (10, 100, 1000, 10000):
print(n)
old_items = list(range(1, n))
new_items = list(range(n - 1))
for func in funcs:
print(func.__name__)
%timeit func(old_items, new_items)
print()
# 10
# diffs_set
# The slowest run took 4.52 times longer than the fastest. This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 914 ns per loop
# diffs_loop
# 1000000 loops, best of 3: 1.97 µs per loop
# diffs_loop2
# 100000 loops, best of 3: 2.09 µs per loop
# diffs_np
# 10000 loops, best of 3: 65.6 µs per loop
# 100
# diffs_set
# 100000 loops, best of 3: 5.23 µs per loop
# diffs_loop
# 100000 loops, best of 3: 13.6 µs per loop
# diffs_loop2
# 10000 loops, best of 3: 116 µs per loop
# diffs_np
# The slowest run took 5.74 times longer than the fastest. This could mean that an intermediate result is being cached.
# 10000 loops, best of 3: 65.9 µs per loop
# 1000
# diffs_set
# 10000 loops, best of 3: 57.7 µs per loop
# diffs_loop
# 10000 loops, best of 3: 132 µs per loop
# diffs_loop2
# 100 loops, best of 3: 10.7 ms per loop
# diffs_np
# 1000 loops, best of 3: 374 µs per loop
# 10000
# diffs_set
# 1000 loops, best of 3: 818 µs per loop
# diffs_loop
# 1000 loops, best of 3: 1.6 ms per loop
# diffs_loop2
# 1 loop, best of 3: 1.06 s per loop
# diffs_np
# 100 loops, best of 3: 3.5 ms per loop
Наиболее важное замечание заключается в том, что при использовании наборов достигается самый быстрый и чистый подход. Следует отметить, что set
s полезны даже для понимания list
, потому что условие if
становится O(1)
(что приводит к общему значению O(n)
) вместо O(n)
(приводит к общему значению * 1027). *). Поскольку самой дорогой операцией является создание set
в самом начале, вполне возможно, что понимание списка станет конкурентоспособным по сравнению с использованием только наборов, если необходимы только a - b
или b - a
, потому что тогда только один set()
звонок необходим. Напротив, подход, основанный на NumPy, здесь неконкурентоспособен.