简体   繁体   中英

How do I write a recursive function that computes ways to sum to a value in a set of numbers?

I'm working on a question that is asked me to compute the numbers of ways to sum to a specific value in a set of numbers

def ways_to_sum(number, set:{int}) -> int:
    blah, blah

Calling

ways_to_sum(7,{2,3,5})

Would give me

2

As, first, 5+2 , and then, 2+2+3 sums to seven. The function must be recursive. I did not write the base case as I'm struggling to set it up.

This is probably not the most efficient solution but it should work, the base case of the recursion is when number = 0, the reduction step reduces number by each value in the set of numbers given (I named it values). It traces the sum using the trace dictionary, one easy way to speed it up would be to use dynamic programming and store the traces for 1,2,3,..., number in an array

def find_sum(number, values, trace = None):
    if trace == None:
        trace = {}
        for i in values:
            trace[i] = 0
    if number == 0:
        return [trace]
    elif number < 0:
        #not a sum
        return None
    stored_trace = []
    for i in values:
        trace_copy = copy.deepcopy(trace)
        trace_copy[i] += 1
        prev_traces = find_sum(number - i, values, trace_copy)
        if prev_traces == None:
            continue
        for j in prev_traces:
            if j not in stored_trace:
                stored_trace.append(j)
    return (stored_trace)

def function(number, values):
    return len(find_sum(number, values))

EDIT: This should be a dynamic programming solution, which constructs the trace of the number from the traces of the number minus each element in values.

def find_sum(number, values, past_traces):
    new_trace = []
    for i in values:
        if number - i < 0:
            #this sum doesn't work
            continue
        if past_traces[number-i] == None:
            find_sum(number - i, values, past_traces)
        for j in past_traces[number - i]:
            curr = copy.deepcopy(j)
            curr[i] += 1
            if curr not in new_trace:
                new_trace.append(curr)
    past_traces[number] = new_trace
    return new_trace

def function(number, values):
    zero_trace = {}
    for i in values:
        zero_trace[i] = 0
    return find_sum(number, values, [[zero_trace]] + [None] * number)

I suppose this is a homework task, so instead of code I'll give you a description that should enable you to write your own code. And actually it's a lot easier to solve this problem recursively than iteratively.

Assumptions

We assume that the numbers in the set S are distinct integers greater than 0. The goal is to write a function countSubsetSum(target, S) that, given an integer target , returns an integer that is equal to the total number of ways to sum up to target (ignoring the order), using only multiples of numbers in S .

Base case

We know that the answer is 0 if the set is empty or if the target is less than or equal to 0:

countSubsetSum(target, S) = 0, if target <= 0
countSubsetSum(target, S) = 0, if S is empty

Inductive case

Otherwise, we go through every element x in S , and for each element, consider two cases:

  1. The number of ways to sum up to target only using multiples of numbers in S\{x} . Here are we are counting solutions not involving x .
  2. The number of ways to sum up to target - k*x (which may be 0 or negative, in which case we hit the base case) only using multiples of number in S\{x} , for k = 1..Floor(target/x) . Here we are counting solutions involving multiples of x .

We can combine both cases like this:

The number of ways to sum up to target - k*x only using multiples of number in S\{x} , for all k = 0..Floor(target/x) .

So we get:

countSubsetSum(target, S)
  = Sum { countSubsetSum(target - k*x, S\{x}) | for all x in S, for all k from 0 .. Floor(target/x) }

or as pseudocode:

result := 0;
foreach (x in S)
    for(k:=0; k < target/x; k++)
        result := result + countSubsetSum(target - k*x, S\{x});    
return result;

Time complexity

The time complexity of this algorithm is at least exponential. Note that we can't expect to do much better than that, since this problem is NP-complete, so assuming P<>NP, there cannot be a polynomial time algorithm.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM