Search code examples
pythonnested-loopscontinue

Check number not a sum of 2 ints on a list


Given a list of integers, I want to check a second list and remove from the first only those which can not be made from the sum of two numbers from the second. So given a = [3,19,20] and b = [1,2,17], I'd want [3,19].

Seems like a a cinch with two nested loops - except that I've gotten stuck with break and continue commands.

Here's what I have:

def myFunction(list_a, list_b):
    for i in list_a:
        for a in list_b:
            for b in list_b:
                    if a + b == i:
                        break
                    else:
                        continue
                    break
                else:
                    continue
            list_a.remove(i)
    return list_a

I know what I need to do, just the syntax seems unnecessarily confusing. Can someone show me an easier way? TIA!


Solution

  • Comments on code:

    • It's very dangerous to delete elements from a list while iterating over it. Perhaps you could append items you want to keep to a new list, and return that.
    • Your current algorithm is O(nm^2), where n is the size of list_a, and m is the size of list_b. This is pretty inefficient, but a good start to the problem.
    • Thee's also a lot of unnecessary continue and break statements, which can lead to complicated code that is hard to debug.
    • You also put everything into one function. If you split up each task into different functions, such as dedicating one function to finding pairs, and one for checking each item in list_a against list_b. This is a way of splitting problems into smaller problems, and using them to solve the bigger problem.

    Overall I think your function is doing too much, and the logic could be condensed into much simpler code by breaking down the problem.

    Another approach:

    Since I found this task interesting, I decided to try it myself. My outlined approach is illustrated below.

    1. You can first check if a list has a pair of a given sum in O(n) time using hashing:

    def check_pairs(lst, sums):
        lookup = set()
    
        for x in lst:
            current = sums - x
            if current in lookup:
                return True
            lookup.add(x)
    
        return False
    

    2. Then you could use this function to check if any any pair in list_b is equal to the sum of numbers iterated in list_a:

    def remove_first_sum(list_a, list_b):
        new_list_a = []
    
        for x in list_a:
            check = check_pairs(list_b, x)
            if check:
                new_list_a.append(x)
    
        return new_list_a
    

    Which keeps numbers in list_a that contribute to a sum of two numbers in list_b.

    3. The above can also be written with a list comprehension:

    def remove_first_sum(list_a, list_b):
        return [x for x in list_a if check_pairs(list_b, x)]
    

    Both of which works as follows:

    >>> remove_first_sum([3,19,20], [1,2,17])
    [3, 19]
    >>> remove_first_sum([3,19,20,18], [1,2,17])
    [3, 19, 18]
    >>> remove_first_sum([1,2,5,6],[2,3,4])
    [5, 6]
    

    Note: Overall the algorithm above is O(n) time complexity, which doesn't require anything too complicated. However, this also leads to O(n) extra auxiliary space, because a set is kept to record what items have been seen.