0

Assume that L is a list of Boolean values, True and False. Write a function longestFalse(L) which returns a tuple (start, end) representing the start and end indices of the longest run of False values in L. If there is a tie, then return the first such run. For example, if

L is False False True False False False False True True False False

then the function would return (3, 6), since the longest run of False is from 3 to 6

L = [False, False, True, False, False, False, False, True, True, False, False]

inRun = False

run = ""
for i in range(len(L)):
    if inRun:
        if L[i] != L[i-1]:
            runs.append(run)

            run = ""
            inRun = False
            continue
        else:
            run += str(L[i])


    if not inRun:
        if i != len(L)-1:
            if L[i] == L[i+1]:
                run += str(L[i])

                inRun = True

I have this code so far but I am completely lost on how to proceed.

Peri
  • 101
  • 1
  • 8
  • For `numpy` solutions, you can also look at [find length of sequences of identical values in a numpy array (run length encoding)](https://stackoverflow.com/q/1066758/364696) and [More efficient solution to find longest series based on boolean in NumPy ndArray](https://stackoverflow.com/q/53978707/364696). – ShadowRanger Nov 18 '19 at 19:07
  • 1
    I've posted a simple and robust solution in the linked question if you're interested. – blhsing Nov 18 '19 at 20:14

3 Answers3

3

I suggest you use itertools.groupby:

from itertools import groupby
from operator import itemgetter

L = [False, False, True, False, False, False, False, True, True, False, False]


def longest_false_run(lst):
    """Finds the longest false run in a list of boolean"""

    # find False runs only
    groups = [[i for i, _ in group] for key, group in groupby(enumerate(lst), key=itemgetter(1)) if not key]

    # get the one of maximum length
    group = max(groups, key=len, default=[-1, -1])

    start, end = group[0], group[-1]

    return start, end


print(longest_false_run(L))
print(longest_false_run([True, True, True]))
print(longest_false_run([True, False]))
print(longest_false_run([False]))

Output

(3, 6)
(-1, -1)
(1, 1)
(0, 0)
Dani Mesejo
  • 61,499
  • 6
  • 49
  • 76
  • This is a natural Python solution (+1), but this seems to be homework for an intro to programming course for which this would likely not be an acceptable solution. – John Coleman Nov 18 '19 at 18:30
  • Yes you are correct. I have no idea what any of these solutions mean. Appreciate the solutions though – Peri Nov 18 '19 at 18:33
  • 1
    This will fail for e.g. `[True, True, True]`, `[True, False]`, `[False]` and other similar cases. – ekhumoro Nov 18 '19 at 19:59
  • @ekhumoro What would be the output for `[True, True, True]`, for this this throws exception so I wouldn't call it a fail. – Dani Mesejo Nov 18 '19 at 20:16
  • @DanielMesejo I suppose it would have to be something like `(-1, -1)`, or perhaps `None`. I don't think it should raise an exception, since the input is perfectly valid. – ekhumoro Nov 18 '19 at 20:20
  • @ekhumoro Updated the answer! – Dani Mesejo Nov 18 '19 at 20:24
2

Here is one possible solution. First, encapsulate the logic into a function, as the problem asks. Then, just iterate through the list and keep track of the indices as needed:

def longestFalse(L):
    # Initialization
    in_run = False
    start = 0
    end = -1
    current_start = 0
    # Iterate through list - plus an extra "fake" boundary at the end
    for i, b in enumerate(L + [True]):
        # Element is false
        if not b:
            # Are we starting a new run?
            if not in_run:
                # Save the starting position of this run
                current_start = i
        # Element is true
        else:
            # Have we just finished a run?
            if in_run:
                # If the run we just finished was longer than our previously saved run
                if i - current_start > end - start + 1:
                    # Save it
                    start = current_start
                    end = i - 1
        # Update run status
        in_run = not b
    return start, end

L = [False, False, True, False, False, False, False, True, True, False, False]
print(longestFalse(L))
# (3, 6)
jdehesa
  • 58,456
  • 7
  • 77
  • 121
0
L = [False, False, True, False, False, False, False, True, True, False, False]
LL = [1*i for i in L]

def myranges(a):  
    iszero = np.concatenate(([0], np.equal(a, 0).view(np.int8), [0]))
    absdiff = np.abs(np.diff(iszero))
    ranges = np.where(absdiff == 1)[0].reshape(-1, 2)
    return ranges

myranges = myranges(np.diff(LL))
myranges[myranges[:,1]- myranges[:,0] > max(myranges[:,1]- myranges[:,0])-1]

#array([[3, 6]])
seralouk
  • 30,938
  • 9
  • 118
  • 133
  • 1
    This will fail for e.g. `[True, True, True]`, `[True, False]`, `[False]` and other similar cases. – ekhumoro Nov 18 '19 at 19:59