Search code examples
pythondynamic-programmingknapsack-problembrute-force

knapsack problem using brute force method in python


I am applying the following code to obtain the max CBU. Can I also get the list of selected items in a knapsack? Here is my code

def knapsack(CBU, weight, Capacity):
    return knapsacksolution(CBU, weight, Capacity, 0)
def knapsacksolution(CBU, weight, Capacity, currentIndex):
    
    if Capacity <= 0 or currentIndex >= len(CBU):
        return 0
    CBU1 = 0
    #selecting a box at currentindex
    if CBU[currentIndex] <= Capacity:
         CBU1 = CBU[currentIndex] + knapsacksolution(CBU, weight, Capacity - CBU[currentIndex], currentIndex+1)
    #excluding a box at currentindex
    CBU2 = knapsacksolution(CBU, weight, Capacity, currentIndex+1)
    
    return max(CBU1, CBU2)
def main():
    weight = [20,15,17,24]
    CBU = [3,2,3,4]
    Capacity = 8
    
    OptimalCBU = knapsack(CBU, weight, Capacity)
    print('Optimal CBU for CBU:{} with weight:{} for Capacity:{} is:{}'.format(CBU, weight, Capacity, OptimalCBU))
if __name__ == '__main__':
    main()

Result is as follow Optimal CBU for CBU:[3, 2, 3, 4] with weight:[20, 15, 17, 24] for Capacity:8 is:8


Solution

  • Just recently I learned the itertools.combinations method (from reading solutions on SO). It seems like a simple tool for generating all of the various configurations of the knapsack.

    In the code below, we use combinations to find all of the combinations of the knapsack configuration, throwing out all of the combos that are over the capacity.

    Having used brute force to find all of the viable answers, it remains simply to sort the results and present the maximum pay solution.

    Here's the code that does just this (it has not been optimized):

    import itertools
    
    def sum_solution( solutions ):
      pay, load = 0,0
      for block in solutions:
        pay += block[0]
        load += block[1]
    
      return pay, load 
    
    def knapsack( capacity, blocks ):
    
      solutions = []
      for count in range(len(blocks)+1):
        for solution in itertools.combinations( blocks, count ):
          pay,load = sum_solution( solution ) 
          if load <= capacity:
            solutions.append((pay,load,solution))
    
      solutions.sort( reverse = True, key = lambda x : x[0] )
      return solutions
    
    
    solutions = knapsack( 15, [ (4,12) , (2,1) , (10,4), (1,1), (2,2) ])
    solution = solutions[0]
    print( f"Maximum Cash:  {solution[0]}  Weight: {solution[1]} Contents:  {solution[2]}")
    
    
    print(f"Cash  Weight Contents")
    for solution in solutions:
      print(f"{solution[0]:5d} {solution[1]:6d} {solution[2]}")
    

    Here's the results:

    Maximum Cash:  15  Weight: 8 Contents:  ((2, 1), (10, 4), (1, 1), (2, 2))
    Cash  Weight Contents
       15      8 ((2, 1), (10, 4), (1, 1), (2, 2))
       14      7 ((2, 1), (10, 4), (2, 2))
       13      6 ((2, 1), (10, 4), (1, 1))
       13      7 ((10, 4), (1, 1), (2, 2))
       12      5 ((2, 1), (10, 4))
       12      6 ((10, 4), (2, 2))
       11      5 ((10, 4), (1, 1))
       10      4 ((10, 4),)
        8     15 ((4, 12), (2, 1), (2, 2))
        7     14 ((4, 12), (2, 1), (1, 1))
        7     15 ((4, 12), (1, 1), (2, 2))
        6     13 ((4, 12), (2, 1))
        6     14 ((4, 12), (2, 2))
        5     13 ((4, 12), (1, 1))
        5      4 ((2, 1), (1, 1), (2, 2))
        4     12 ((4, 12),)
        4      3 ((2, 1), (2, 2))
        3      2 ((2, 1), (1, 1))
        3      3 ((1, 1), (2, 2))
        2      1 ((2, 1),)
        2      2 ((2, 2),)
        1      1 ((1, 1),)
        0      0 ()
    

    I modified the problem statement to use an example I found at wikipedia. Simply put, the blocks were:

    $4, 12 kg
    $2, 2 kg
    $1, 1 kg
    $2, 1 kg
    $10,4 kg 
    

    You can see that I coded the blocks as this:

    [ (4,12) , (2,1) , (10,4), (1,1), (2,2) ]
    

    Anyway, HTH.