Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optimal weights subset sum using backtracking

I'm trying to solve a problem. I have some weights. [2,7,20,70,200,700] After a given input, for example 1507, it should return the optimal combination of those weights. Which in this case would be [700,200,200,200,200,7]. Unfortunately my algorithm is returning [700, 700, 70, 20, 7, 2, 2, 2, 2, 2]. When I say optimal I mean that my algorithm should use the fewest number of weights as possible.

func solve(_ targetValue: Int, weights: inout [Int]) -> [Int] {
    // The used weights to store
    var usedWeights: [Int] = []
    // The current total value for the calculation
    var total = targetValue
    // If target value is 0 add it to the array and just return it
    if targetValue == 0 { usedWeights.append(0); return usedWeights }
    // Loop through the weights
    for weight in weights.reversed() {

    while weight <= total {
            total -= weight
            usedWeights.append(weight)
        }
    }    // If still weights are not found call the function recursively again but remove the last element before
    if total != 0 {
        weights.removeLast()
        return solve(targetValue, weights: &weights)
    }
    return usedWeights
}

var newWeights: [Int] = [2,7,20,70,200,700]
print(solve(1507, weights: &newWeights))

How can I fix this? What am I doing wrong? The important thing is to solve it using backtracking. I really appreciate your help.

like image 862
BilalReffas Avatar asked May 17 '18 19:05

BilalReffas


People also ask

Can subset sum problem be solved by backtracking?

Subset sum problem is to find subset of elements that are selected from a given set whose sum adds up to a given number K. We are considering the set contains non-negative values. It is assumed that the input set is unique (no duplicates are presented).

What is the time complexity of sum of subset problem using backtracking?

Subset Sum Problem solved using Backtracking approach 【O(2^N) time complexity】

Does backtracking give optimal solution?

Backtracking algorithms were also discovered to be very effective for solving optimization problems. In some cases, it is used to find all feasible solutions to the enumeration problem. Backtracking, on the other hand, is not regarded as an optimal problem-solving technique.

What do you mean by backtracking discuss in detail explain sum of subset problem with suitable example?

And another some value is also provided, we have to find a subset of the given set whose sum is the same as the given sum value. Here backtracking approach is used for trying to select a valid subset when an item is not valid, we will backtrack to get the previous subset and add another element to get the solution.


1 Answers

Here is a possible recursive solution:

// Find the shortest combination of (possibly repeating) numbers in `values`
// whose sum is exactly `target`, and whose count is less than `limit`.
// Return `nil` if no such combination exist.
//
// `target` must be non-negative, and `values` an array of positive
// numbers in decreasing order.
//
func solveHelper(target: Int, values: ArraySlice<Int>, limit: Int) -> [Int]? {
    if target == 0 {
        return [] // Target reached exactly.
    }
    guard let first = values.first else {
        return nil // No values left, target cannot be reached.
    }
    if target/first >= limit {
        return nil // Target cannot be reached with less than `limit` values.
    }

    var best: [Int]? = nil   // Best solution found so far
    var bestCount = limit // Number of values in best solution

    for n in stride(from: target/first, through: 0, by: -1) {
        if let s = solveHelper(target: target - n * first, values: values.dropFirst(), limit: bestCount - n) {
            best = s + repeatElement(first, count: n)
            bestCount = s.count + n
        }
    }

    return best
}

// Find the shortest combination of (possibly repeating) values in `values`
// whose sum is exactly `target`. Return `nil` if no such combination exist.
//
// `target` must be non-negative, and `values` an array of positive
// numbers.
//
func solve(target: Int, values: [Int]) -> [Int]? {
    return solveHelper(target: target, values: ArraySlice(values.sorted(by: >)), limit: Int.max)
}

Examples:

print(solve(target: 1507, values: [2, 7, 20, 70, 200, 700]) as Any)
// Optional([7, 200, 200, 200, 200, 700])

print(solve(target: 1507, values: [20, 70, 200, 700]) as Any)
// nil

print(solve(target: 6, values: [1, 3, 4]) as Any)
// Optional([3, 3])

print(solve(target: 0, values: [1, 3, 4]) as Any)
// Optional([])

Some explanations:

  • It is assumed that target is non-negative and that all values are positive.
  • solve sorts the array in descending order and passes it as an ArraySlice to the recursive helper function. This helps to avoid further copies of the element storage, when values.dropFirst() is passed to the recursive call.
  • solveHelper starts “greedy” with the maximal possible number of the first (i.e. largest) value, calls itself recursively for the remaining target sum and values, then repeats the process with less copies of the first value, keeping track of the shortest solution found so far.
  • In order to “prune” the recursion tree, a limit is passed to the recursive call. As an example, if 1507 = 700 + 200 + 200 + 200 + 200 + 7 has already been found then there is no need anymore to sum only values in [2, 7, 20, 70], that would only give longer solutions.
  • The function runs reasonably fast in my tests for the given array. For a larger number of possible values you probably need a more sophisticated algorithm, such as the dynamic programming approach described in Change-making problem.
like image 74
Martin R Avatar answered Sep 19 '22 10:09

Martin R