'Python: Recursive Function. How to return all subsets of targetsum

My code is not showing the shortest subset eg [7] or it is not reading all the subsets, [7], [3,4] to return the shortest subset. Can explain why only 1 set of result is return and how should I modify it to show all subset? Thanks

Image of Code that i wanted to follow as below

enter image description here


def howsum(targetsum,numbers,combo=None):
    
    if combo == None:
        combo = list()
    if targetsum == 0: return [ ]
    if targetsum < 0: return None
    shortcombo = None
    
    for number in numbers:
        remainder = targetsum - number      
        
        combo = howsum(remainder,numbers,combo)
        if combo != None:
            combo.append(number)
            if shortcombo == None or len(shortcombo) > len(combo):
                shortcombo = combo
                return shortcombo
                                    
    return shortcombo
     
print(howsum(7,[4,3,7]))


Solution 1:[1]

Wrote code that closely matches the original JavaScript.

Although JavaScript names will work, I refactored function and variable names to agree with Python style, namely:

  • Function names should be lowercase, with words separated by underscores as necessary to improve readability.
  • Variable names follow the same convention as function names.

Code

def best_sum(target_sum, numbers):
    if target_sum == 0: return []
    if target_sum < 0: return None
    
    shortest_combination = None

    for num in numbers:
        remainder = target_sum - num
        remainder_combination = best_sum(remainder, numbers)
        if remainder_combination != None:
            combination = [*remainder_combination, num] # Python * equivalent to JavaSscript ...
            if shortest_combination == None or len(combination) < len(shortest_combination):
                shortest_combination = combination
    
    return shortest_combination

Test

print(bestSum(7, [3, 4, 7])) # Output: [7]

Using Memoization (i.e. caching)

def best_sum(target_sum, numbers, memo = None):
    if memo is None:
        memo = {0:[]}
    if target_sum < 0:
        return None
    if target_sum in memo:
        return memo[target_sum]

    shortest_combination = None

    for num in numbers:
        remainder = target_sum - num

        remainder_combination = best_sum(remainder, numbers, memo)
        if remainder_combination != None:
            combination = [*remainder_combination, num] # Python * equivalent to JavaSscript ...
            if shortest_combination == None or len(combination) < len(shortest_combination):
                shortest_combination = combination

    memo[target_sum] = shortest_combination

    return memo[target_sum]

print(best_sum(7, [3, 4, 7]))  # Output: 7
# Following very slow on non-memoized version
print(best_sum(100,[10,1,25])) # Output: [25, 25, 25, 25]

Solution 2:[2]

I am not sure about this but while defining a function in python and giving 2 sets of codes it is not actually performing the second set of the code when calling out the function. I got the same problem with some other project. And I got no answers but saying I did some wrong in the code!!!

Solution 3:[3]

Added memoization, however with 1 inside sets of num. the results gone haywire.

def best_sum(target_sum, numbers,memo=None):
    if memo == None:
      memo = dict()
    
    if target_sum in memo:
      return memo[target_sum]
  
    if target_sum == 0: return []
    if target_sum < 0: return None
    
    shortest_combination = None

    for num in numbers:
        remainder = target_sum - num
        remainder_combination = best_sum(remainder, numbers,memo)
        if remainder_combination != None:
            remainder_combination.append(num)
            combination = remainder_combination
            if shortest_combination == None or len(combination) < len(shortest_combination):
                shortest_combination = combination
    memo[target_sum] = shortest_combination
    return shortest_combination

print(best_sum(100,[5,1,25]))

This code by answer posted by DarylG works. By changing the .append to [*list,var] However I dont understand why is the result different between function append and *

def best_sum(target_sum, numbers,memo=None):
    if memo == None:
      memo = dict()
    
    if target_sum in memo:
      return memo[target_sum]
  
    if target_sum == 0: return []
    if target_sum < 0: return None
    
    shortest_combination = None

    for num in numbers:
        remainder = target_sum - num
        remainder_combination = best_sum(remainder, numbers,memo)
        if remainder_combination != None:
            combination = [*remainder_combination,num] #this line from append to *
            if shortest_combination == None or len(combination) < len(shortest_combination):
                shortest_combination = combination
    memo[target_sum] = shortest_combination
    return shortest_combination

print(best_sum(100,[5,1,25]))

Solution 4:[4]

I checked out your code. What I felt missing was

  • Incorrect placement of return and storing the values inside of combo (set instead of list).

     def howsum(targetsum,numbers,combo=None):
         if combo == None:
             combo = set()
         if targetsum == 0: 
             return [ ]
         if targetsum < 0: 
             return None
    
         for idx,number in enumerate(numbers):
             remainder = targetsum - number
             if howsum(remainder,numbers[idx:],combo) != None:
                 combo.add(number)
         else:
             return combo
         return None
    

print(howsum(7,[7,3,4,2,5]))

Hope this helps. While this gives you a solution I would recommend using a different way to solve such issues (twoSum solutions) or two pointer approach with O(n).

Solution 5:[5]

In addition to using memo/any variable for memoization, you can employ functools.lru_cache method. Just remember to typecast the input while testing.

import functools

@functools.lru_cache(maxsize=4)
def howsum(targetnum, num_list):
    if targetnum == 0:
        return []
    if targetnum < 0:
        return None
    for num in num_list:
        rem = targetnum - num
        rem_val = howsum(rem, num_list)
        if rem_val is not None:
            rem_val.append(num)
            return rem_val
    return None

print(howsum(7, tuple([5, 3, 4, 7])))
print(howsum(8, tuple([3, 4, 2])))
print(howsum(300, tuple([7, 14])))

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Sasidhar Mankala
Solution 3
Solution 4
Solution 5 Vishnuraj Rajendran