CodingBat sum67 works for all except "other tests"

Question:

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.

Asked By: Dan Schumacher

||

Answers:

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.

Answered By: ggorlen
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.