Search code examples
pythonrecursioncombinationsdistinct

Find Distinct parts of List that Sum are equal to the given Number


I want to find the distinct tuples whose sum equals to K, from an input list with no repeated numbers.

Input: A=[1, 2, 3], K = 3 
Output: 
(1, 2)
(2, 1)
(3) 

Note that - (2,3) is not same as (3,2).

What I am doing:

def unique_combination(l, sum, K, local, A):
 
    # If a unique combination is found
    if (sum == K):
        print("(", end="")
        for i in range(len(local)):
            if (i != 0):
                print(" ", end="")
            print(local[i], end="")
            if (i != len(local) - 1):
                print(", ", end="")
        print(")")
        return
 
    # For all other combinations
    for i in range(l, len(A), 1):
 
        # Check if the sum exceeds K
        if (sum + A[i] > K):
            continue
 
        # Check if it is repeated or not
        if (i > l and
                A[i] == A[i - 1]):
            continue
 
        # Take the element into the combination
        local.append(A[i])
 
        # Recursive call
        unique_combination(i+1, sum + A[i],
                           K, local, A)
 
        # Remove element from the combination
        local.remove(local[len(local) - 1])
 

def myFunc(A, K):
 
    # Sort the given elements
    A.sort(reverse=False)
 
    local = []
 
    unique_combination(0, 0, K, local, A)
 

arr = [1,2,3,4,6,7]
target = 8
myFunc(arr, target)

This function Return:

Input: A=[1,2,3,4,6,7], K = 8 
Output: 
(1,  3,  4)
(1,  7)
(2,  6)

I also want the other combination like: (1, 4, 3), (1, 3, 4), (4, 1, 3), (4, 3, 1), (3, 1, 4), (3, 4, 1), (7, 1), (2, 6)

So, What should I do with the code to achieve my result...


Solution

  • Your original function was generating properly the possible combinations. The only problem was that you were printing it, instead of saving it in a list to use later.

    I modified it so it saves the result into a list, that can be input to a function that generates all permutations (also implemented recursively) of a list.

    Overall, the approach is:

    1. Generate all combinations of 1, 2, ... n numbers that sum to less than goal
    2. Generate all the permutations of these combinations (this step seems useless, because sum is commutative, but I'm assuming that is the definition of your problem)

    Note that this is an instance of the Subset Sum Problem, which is a difficult optimisation problem. The solution below does not scale well for large sets.

    def unique_combination(in_list, goal, current_sum, current_path, accumulator):
        # Does a depth-first search of number *combinations* that sum exactly to `goal`
        # in_list is assumed sorted from smaller to largest
    
        for idx, current_element in enumerate(in_list):
            next_sum = current_sum + current_element
            if next_sum < goal:
                next_path = list(current_path)  # list is needed here to create a copy, as Python has a pass by reference semantics for lists
                next_path.append(current_element)
                unique_combination(in_list[idx+1:], goal, next_sum, next_path, accumulator)
            elif next_sum == goal:  # Arrived at a solution
                final_path = list(current_path)
                final_path.append(current_element)
                accumulator.append(final_path)
            else:   # Since in_list is ordered, all other numbers will fail after this
                break
        return accumulator
    
    def permutations(elements):
        # Returns a list of all permutations of `elements`, calculated recursively
        if len(elements) == 1:
            return [elements]
        result = []
        for idx, el in enumerate(elements):
            other_elements = elements[:idx] + elements[idx+1:]
            all_perms = [[el] + sub_perms for sub_perms in permutations(other_elements)]
            result.extend(all_perms)
        return result
    
    def myFunc(input_list, goal):
        # Sort the given elements
        input_list.sort()
        combinations = unique_combination(input_list, goal, 0, [], [])
    
        result = []
        for comb in combinations:
            result.extend(permutations(comb))
        return result
    
    def print_results(results):
        for el in results:
            print(tuple(el)) # to get the parentheses
    
    # Input:
    a = [1,2,3,4,6,7,8]
    k = 8
    all_permutations = myFunc(a, k)
    print_results(all_permutations)
    
    # (1, 3, 4)
    # (1, 4, 3)
    # (3, 1, 4)
    # (3, 4, 1)
    # (4, 1, 3)
    # (4, 3, 1)
    # (1, 7)
    # (7, 1)
    # (2, 6)
    # (6, 2)
    # (8,)