Solutions
Instead of checking equality of the l1
and l2
elements, check for membership of the l1
elements in the l2
elements.
Some possible implementations (Try it online!):
def subseq(l1, l2):
it1 = iter(l1)
return all(any(x in pool for x in it1)
for pool in l2)
def subseq(l1, l2):
it1 = iter(l1)
return all(not pool.isdisjoint(it1)
for pool in l2)
def subseq(l1, l2):
it1 = iter(l1)
return not any(pool.isdisjoint(it1)
for pool in l2)
def subseq(l1, l2):
return not any(map(set.isdisjoint, l2, repeat(iter(l1))))
def subseq(l1, l2):
it1 = iter(l1)
for pool in l2:
for x in it1:
if x in pool:
break
else:
return
return True
Benchmarks
Testing with
l1 = [0, 1, 2, 3, ..., 39]
l2 = [{0, 1}, {2, 3}, ..., {40, 41}]
Times:
5254.183 ms subseq_product
0.020 ms subseq_all_any
0.006 ms subseq_all_not_disjoint
0.005 ms subseq_not_any_disjoint
0.005 ms subseq_map_disjoint
0.003 ms subseq_loops
1279.553 ms subseq_Alain
0.018 ms subseq_Alain_faster
subseq_product
does the idea from the question, going through all 221 possibilities of l2
, doing an ordinary subsequence check for each.
subseq_Alain
tries 220 (?) recursive paths. The _faster
version greedily takes each l2
-element's first match and doesn't try further.
Trying the fast solutions with longer inputs:
l1 = [0, 1, 2, 3, ..., 999]
l2 = [{0, 1}, {2, 3}, ..., {1000, 1001}]
Times:
0.285 ms subseq_all_any
0.058 ms subseq_all_not_disjoint
0.057 ms subseq_not_any_disjoint
0.031 ms subseq_map_disjoint
0.044 ms subseq_loops
1.488 ms subseq_Alain_faster
Full benchmark code (Try it online!):
from timeit import timeit
from itertools import product, repeat
def subseq_product(l1, l2):
def is_subseq(x, y):
# Ordinary subsequence test,
# see https://stackoverflow.com/a/24017747
it = iter(y)
return all(c in it for c in x)
return any(is_subseq(p2, l1)
for p2 in product(*l2))
def subseq_all_any(l1, l2):
it1 = iter(l1)
return all(any(x in pool for x in it1)
for pool in l2)
def subseq_all_not_disjoint(l1, l2):
it1 = iter(l1)
return all(not pool.isdisjoint(it1)
for pool in l2)
def subseq_not_any_disjoint(l1, l2):
it1 = iter(l1)
return not any(pool.isdisjoint(it1)
for pool in l2)
def subseq_map_disjoint(l1, l2):
return not any(map(set.isdisjoint, l2, repeat(iter(l1))))
def subseq_loops(l1, l2):
it1 = iter(l1)
for pool in l2:
for x in it1:
if x in pool:
break
else:
return
return True
def subseq_Alain(A, S):
if not S: return True # all sets matched
for i,n in enumerate(A): # check for membership in 1st set
if n in S[0] and subseq_Alain(A[i+1:],S[1:]): # and matching rest
return True # found full match
return False
def subseq_Alain_faster(A, S):
if not S: return True # all sets matched
for i,n in enumerate(A): # check for membership in 1st set
if n in S[0]:
return subseq_Alain_faster(A[i+1:],S[1:]) # and matching rest
return False
def benchmark(funcs, args, number):
for _ in range(3):
for func in funcs:
t = timeit(lambda: func(*args), number=number) / number
print('%8.3f ms ' % (t * 1e3), func.__name__)
print()
l1 = list(range(40))
l2 = [{i, i+1} for i in range(0, 42, 2)]
funcs = [
subseq_product,
subseq_all_any,
subseq_all_not_disjoint,
subseq_not_any_disjoint,
subseq_map_disjoint,
subseq_loops,
subseq_Alain,
subseq_Alain_faster,
]
benchmark(funcs, (l1, l2), 1)
l1 = list(range(1000))
l2 = [{i, i+1} for i in range(0, 1002, 2)]
funcs = [
subseq_all_any,
subseq_all_not_disjoint,
subseq_not_any_disjoint,
subseq_map_disjoint,
subseq_loops,
subseq_Alain_faster,
]
benchmark(funcs, (l1, l2), 500)