5

There is a classic Knapsack problem. My version of this problem is a little different.

Given set of items, each with a mass, determine the number of combinations to pack items so that the total weight is less than or equal to a given limit.

For example, there is 5 items with mass: 1, 1, 3, 4, 5. There is a bug with limit = 7. There are the following combinations:

1 + 3
1 + 4
1 + 5
1 + 1 + 3
1 + 1 + 4
1 + 1 + 5
3
3 + 4
4
5

Is there a way to count number of combinations without brute?

David
  • 674
  • 6
  • 19
  • 1
    This question appears to be off-topic because it is not about programming or development. See [What topics can I ask about here](http://stackoverflow.com/help/on-topic) in the Help Center. Perhaps [Mathematics Stack Exchange](http://math.stackexchange.com/) would be a better place to ask. – jww May 02 '15 at 20:29
  • 1
    If you know how to do the original problem using DP / a table, you should be able to extend this to your problem - for example you have two choices with 5: either you take it or not - if you take it you have limit 2 left and so you should recursively look for how many ways you can do this - if not you still have limit 7 but only 1,1,3,4 to look for .... – Random Dev May 02 '15 at 20:32
  • @jw, It's strange, because my question relates to the stackoverflow tags. – David May 02 '15 at 20:32
  • @David it's fine I guess it's borderline and everyone here has different opinions on stuff like this - if you like you can go to math/cs SE sites but there they might point you back here ;) – Random Dev May 02 '15 at 20:33
  • @CarstenKönig, thank for the idea of solution. – David May 02 '15 at 20:34
  • 1
    if you are interested I put a very basic version of this (using haskell) [in this gist](https://gist.github.com/CarstenKoenig/0fc1ff33b7f92cbb1b13) - but it will find 17 solutions: `[[5,1,1],[5,1],[5,1],[5],[4,3],[4,1,1],[4,1],[4,1],[4],[3,1,1],[3,1],[3,1],[3],[1,1],[1],[1],[]]` ;) – Random Dev May 02 '15 at 20:42
  • @CarstenKönig, it is a wonderful example, thank you! – David May 02 '15 at 20:44
  • 2
    This is a question about a software algorithm to solve a specific problem. Such questions are definitely on-topic here at StackOverflow and always have been. – RBarryYoung May 02 '15 at 21:38

3 Answers3

4

This is one solution:

items = [1,1,3,4,5]
knapsack = []
limit = 7

def print_solutions(current_item, knapsack, current_sum):
    #if all items have been processed print the solution and return:
    if current_item == len(items):
        print knapsack
        return

    #don't take the current item and go check others
    print_solutions(current_item + 1, list(knapsack), current_sum)

    #take the current item if the value doesn't exceed the limit
    if (current_sum + items[current_item] <= limit):
        knapsack.append(items[current_item])
        current_sum += items[current_item]
        #current item taken go check others
        print_solutions(current_item + 1, knapsack, current_sum )

print_solutions(0,knapsack,0)

prints:

[]
[5]
[4]
[3]
[3, 4]
[1]
[1, 5]
[1, 4]
[1, 3]
[1]
[1, 5]
[1, 4]
[1, 3]
[1, 1]
[1, 1, 5]
[1, 1, 4]
[1, 1, 3]
pjsofts
  • 170
  • 4
  • 18
3

EACH ITEM USED UNLIMITED TIMES

This is rather straightforward extension of original problem.

As you probably know you use DP to solve original problem, where weight of items must be exactly W. When you finish with DP, you will also have solution for all weights < W. To get your solution simply sum up solutions for weights from 1 to W (and +1 for empty set).

EACH ITEM USED ONCE

In this case, there is not other way, but to brute-force all possible combinations. This problem is NP-hard and will have time complexity of O(2^n).

To brute-force use next code (borrowed from @pjsofts):

items = [1,1,3,4,5]
knapsack = []
limit = 7

def print_solutions(current_item, knapsack, current_sum):
    #if all items have been processed print the solution and return:
    if current_item == len(items):
        print knapsack
        return

    #don't take the current item and go check others
    print_solutions(current_item + 1, list(knapsack), current_sum)

    #take the current item if the value doesn't exceed the limit
    if (current_sum + items[current_item] <= limit):
        knapsack.append(items[current_item])
        current_sum += items[current_item]
        #current item taken go check others
        print_solutions(current_item + 1, knapsack, current_sum )

print_solutions(0,knapsack,0)
Neithrik
  • 2,002
  • 2
  • 20
  • 33
  • unfortunately, this way doesn't work. This solution gives an incorrect result. – David May 02 '15 at 22:18
  • Add +1 for empty set as well. – Neithrik May 03 '15 at 08:11
  • maybe I don't understand you. There is a example of solution for `0/1 knapsack problem` - http://easycaptures.com/fs/uploaded/823/0662191493.png For my problem, correct answer is `15`. How to sum numbers to get the same answer? – David May 03 '15 at 09:09
  • I think there is also one solution using dynamic programming. Each item can be used as many times as that item is repeated. Solve the unlimited problem above with limit equal to number of repetition of each item. – pjsofts May 03 '15 at 15:40
3

well as others posted some solutions too, here is a translation of the naive extension of the problem using Haskell and simple recursion:

combinations :: Int -> [Int] -> [[Int]]
combinations _ [] = [[]]
combinations w (x:xs)
  | w >= x = [ x:ys | ys <- combinations (w-x) xs] ++ combinations w xs
  | otherwise = combinations w xs

test-run

λ> combinations 7 [5,4,3,1,1]
[[5,1,1],[5,1],[5,1],[5],[4,3],[4,1,1],[4,1],[4,1],[4],[3,1,1],[3,1],[3,1],[3],[1,1],[1],[1],[]]

what's going on?

starting with 5 you have two choices: either you take it or not.

  • if you take it you have limit 2 left and so you should recursively look for how many ways you can do this
  • if not you still have limit 7 but only 1,1,3,4 to look for ....

the algorithm translates this into basic Haskell in a hopeful readable way - feel free to ask for details

remarks

I did not look at the performance at all - but it should be easy doing the same stuff you would do with the original problem (rewrite columns of the table, ...)

Random Dev
  • 51,810
  • 9
  • 92
  • 119