1

Here is the problem I am working on:

Return the sum of the numbers in the array, except ignore sections of numbers starting with a 6 and extending to the next 7 (every 6 will be followed by at least one 7). Return 0 for no numbers.

  • sum67([1, 2, 2]) → 5
  • sum67([1, 2, 2, 6, 99, 99, 7]) → 5
  • sum67([1, 1, 6, 7, 2]) → 4

Here is my solution:

def sum67(nums):
    if len(nums) == 0:
        return 0
    
    elif 6 not in nums:
        return sum(nums)
    
    else:
        for num in nums:
            if num ==6:
                ind_six = nums.index(6)
                ind_seven = nums.index(7,ind_six)
                del nums[ind_six:ind_seven+1]
        else:
            pass
      
    return sum(nums)

My code works for all of the listed inputs, but fails "other tests." Please reference the picture. Could anyone help me figure out why? What am I missing? Thank you!! Expected vs run chart

I have tried brainstorming other possible inputs that are failing.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
  • It says in the question that every 6 will be followed by a 7. I also tried this to double check. elif 6 not in nums or 7 not in nums: Got the same result. – Dan Schumacher Jan 11 '23 at 17:54
  • You shouldn't remove items from a list while iterating over it. See: [How to remove items from a list while iterating?](https://stackoverflow.com/q/1207406) – 001 Jan 11 '23 at 17:58
  • 2
    There's really no need to delete. Walk the list, keeping a running total. But keep track if after a `6`. In that case, don't add to the running total again until you find a `7`. – 001 Jan 11 '23 at 18:00
  • @juanpa.arrivillaga I think the first bug is OK since presumably the OP has removed all 6s at the beginning of the list by the time they've gotten to the first one, so it's just the "removing while iterating" thing. – ggorlen Jan 11 '23 at 21:22

1 Answers1

3

This is a good example of over-reliance on Python's conveniences creating inefficiencies. Your usages of in, .index(), slices and sum() can all walk the whole list, so they're O(n). The overall complexity is O(nn). Take a look at the time complexity of Python lists.

An easier approach is to walk the list and stop summing whenever you see a 6 up to the next 7:

def sum67(nums):
    count = True
    total = 0

    for n in nums:
        if n == 6:
            count = False

        if count:
            total += n

        if n == 7:
            count = True

    return total

One pass, O(n).

Armed with the above reference solution, I ran a fuzz test to see if there were any edge cases your solution misses:

import random

for _ in range(10000):
    L = [random.randint(0, 20) for _ in range(random.randint(0, 20))]

    if all(x != 6 or (7 in L[i:]) for i, x in enumerate(L)):
        if sum67_ref(L[:]) != sum67(L[:]):
            print(L)

A failing input is L = [6, 7, 6, 1, 7, 7]. The correct answer is 7 but your code gives 21. The problem is, when you remove elements from a list while iterating over it, the iterator gets confused and skips elements. In this case, the [6, 7] sublist is deleted, then the iterator skips the next 6 at index 2 and next emits the 1, which is counted, along with the 7 after it when both should be ignored.

Without a reference solution, it can be hard to think of a failing test case (it's good practice to try!), but you can follow a testing principle which says "most incorrect functions can be caused to fail on a small test case". This is true here--we only need a list of length 4 to trigger a failure: [6, 7, 6, 7]. You can generate permutations of 6s and 7s in a small list and inspect them by hand against your code's output to validate it.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
  • I got it working! Thank you! That is much more elegent and simple. Is there a reason that my old code didn't work though? – Dan Schumacher Jan 11 '23 at 18:29
  • I'm not familiar with coding bat, so I'm guessing it failed due to efficiency, because I wasn't able to find any test cases it failed on. That doesn't guarantee that it doesn't fail on some edge case we've both overlooked. One way to check is to spin up a harness that sends random valid inputs and compare the output against my reference solution to find any discrepancies. – ggorlen Jan 11 '23 at 18:34
  • Check the update--I added a fuzzing test and found failing inputs. – ggorlen Jan 11 '23 at 20:57