I\'m trying to write code that finds the longest run in a list of Boolean values and return the index of the first and last value of that run. For example, if L is [False, Fals
A more faster iterative version with itertools.groupby
feature:
from itertools import groupby
def get_longest_run(lst):
curr_len, run_span = 0, 0
for k, gr in groupby(lst):
len_ = len(list(gr))
if (run_span and len_ > (run_span[1] + 1) - run_span[0]) or len_ > curr_len:
run_span = (curr_len, curr_len + len_ - 1)
curr_len += len_
return run_span
lst = [False, False, True, False, False, False, False, True, True, False, False]
print(get_longest_run(lst)) # (3, 6)
Time performance comparison (to previous answer):
In [144]: lst = [False, False, True, False, False, False, False, True, True, False, False]
In [145]: def get_longest_run_deniel(lst):
...: start = 0
...: runs = []
...: for key, run in groupby(lst):
...: length = sum(1 for _ in run)
...: runs.append((start, start + length - 1))
...: start += length
...:
...: result = max(runs, key=lambda x: x[1] - x[0])
...: return result
...:
In [146]: def get_longest_run(lst):
...: curr_len, run_span = 0, 0
...: for k, gr in groupby(lst):
...: len_ = len(list(gr))
...: if (run_span and len_ > (run_span[1] + 1) - run_span[0]) or len_ > curr_len:
...: run_span = (curr_len, curr_len + len_ - 1)
...: curr_len += len_
...:
...: return run_span
...:
In [147]: %timeit get_longest_run_deniel(lst)
6.06 µs ± 168 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [148]: %timeit get_longest_run(lst)
3.67 µs ± 131 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Use itertools.groupby:
from itertools import groupby
values = [False, False, True, False, False, False, False, True, True, False, False]
start = 0
runs = []
for key, run in groupby(values):
length = sum(1 for _ in run)
runs.append((start, start + length - 1))
start += length
result = max(runs, key=lambda x: x[1] - x[0])
print(result)
Output
(3, 6)
You can keep track of the starting index and the length of the longest run so far, as well as the starting index of the current run, and if the current index minus the starting index of the current run is greater than the length of the longest run so far, make the current starting index and the said length the new starting index and the length of the longest run:
def longest_false(L):
start = max_start = None
max_size = -1
for i, v in enumerate(L):
if v:
start = None
else:
if start is None:
start = i
if i - start > max_size:
max_start = start
max_size = i - start
if max_start is None:
return None, None # in case there is no False at all in the given list
return max_start, max_start + max_size