Knapsack Problem in Python With Various Ways to Solve

Hello Programmers, in this article, we will discuss ways to solve the Knapsack problem in python using different approaches. The different approaches to solving the knapsack problem are – greedy method, dynamic programming, and brute force approach. Before we start with examples of the knapsack problem, let me briefly brief you about the knapsack problem.

What is Knapsack Problem in Python?

 A knapsack problem algorithm is a constructive approach to combinatorial optimization. The problem is basically about a given set of items, each with a specific weight and a value. Therefore the programmer needs to determine each item’s number to include in a collection so that the total weight is less than or equal to a given limit. And also, the total value is maximum. It derives its name from the fixed-size knapsack that must be filled up to its limit with the most valuable items.

Practical Application of Knapsack Problem

The practical application of The knapsack problem algorithm is used in resource allocation. However, the decision-makers have to choose from a set of projects or tasks under a fixed budget or time constraint.

Note: 0/1 knapsack problem is a special case knapsack problem that does not fill the knapsack with fractional items.

Different approaches to solve Knapsack problem in Python

  • Greedy Method
  • Dynamic programming
  • Brute Force

Greedy Method

EXAMPLE:

class KnapsackPackage(object): 
      
    """ Knapsack Package Data Class """
    def __init__(self, weight, value): 
        self.weight = weight 
        self.value = value 
        self.cost = value / weight 
  
    def __lt__(self, other): 
        return self.cost < other.cost

if __name__ == "__main__": 
    W = [15, 10, 2, 4] 
    V = [30, 25, 2, 6] 
    M = 37
    n = 4
    
    proc = FractionalKnapsack()
    proc.knapsackGreProc(W, V, M, n)

class FractionalKnapsack(object):
    def __init__(self):
        
    def knapsackGreProc(self, W, V, M, n):
        packs = []
        for i in range(n): 
            packs.append(KnapsackPackage(W[i], V[i]))
            
        packs.sort(reverse = True)
        
        remain = M
        result = 0
        
        i = 0
        stopProc = False
        
  while (stopProc != True):
            if (packs[i].weight <= remain):
                remain -= packs[i].weight;
                result += packs[i].value;
                
  print("Pack ", i, " - Weight ", packs[i].weight, " - Value ", packs[i].value)
            
            if (packs[i].weight > remain):
                i += 1
                
            if (i == n):
                stopProc = True            
        
 print("Max Value:\t", result)   

OUTPUT:

Pack  0  - Weight  10  - Value  25
Pack  0  - Weight  10  - Value  25
Pack  0  - Weight  10  - Value  25
Pack  2  - Weight  4  - Value  6
Pack  3  - Weight  2  - Value  2
Max Value:	 83

EXPLANATION:

Greedy algorithms are used to solve optimization problems, i.e., find the best solution based upon given criteria. Greedy algorithms implement optimal local selections in the hope that those selections will lead to the best solution. However, the solution to the greedy method is always not optimal. Greedy methods work well for the fractional knapsack problem. However, for the 0/1 knapsack problem, the output is not always optimal.

In conclusion, The greedy method’s idea is to calculate the (value/weight) ratio. Sort the ratios in descending order. Choose the first ratio, which is the maximum package. The capacity of the knapsack can contain that package (remain > weight). Every time a package is put into the knapsack, it will also reduce the knapsack’s capacity.

Dynamic Programming

EXAMPLE:

def knapSack(W, wt, val, n): 
    K = [[0 for x in range(W + 1)] for x in range(n + 1)] 
  
    # Build table K[][] in bottom up manner 
    for i in range(n + 1): 
        for w in range(W + 1): 
            if i == 0 or w == 0: 
                K[i][w] = 0
            elif wt[i-1] <= w: 
                K[i][w] = max(val[i-1] 
                          + K[i-1][w-wt[i-1]],   
                              K[i-1][w]) 
            else: 
                K[i][w] = K[i-1][w] 
  
    return K[n][W] 
  
  
# Driver code 
val = [60, 100, 120] 
wt = [10, 20, 30] 
W = 50
n = len(val) 
print(knapSack(W, wt, val, n)) 

OUTPUT:

220

EXPLANATION:

Dynamic Programming approach divides the problem to be solved into subproblems. The subproblems are further kept on dividing into smaller subproblems. Until you get subproblems that can be solved easily. The idea of Knapsack dynamic programming is to use a table to store the solutions of solved subproblems.

In the table, all the possible weights from ‘1’ to ‘W’ serve as the columns and weights are kept as the rows. 
The state DP[i][j] in the above example denotes the maximum value of ‘j-weight’ considering all values from ‘1 to ith’. So if we consider ‘wi’ (weight in ‘ith’ row) it is put in all columns which have ‘weight values > wi’. Two possibilities occur – to fill or not to fill ‘wi’ in the given column. If we do not fill ‘ith’ weight in ‘jth’ column then DP[i][j] state will be same as DP[i-1][j].But if we fill the weight, DP[i][j] will be equal to the value of ‘wi’+ value of the column weighing ‘j-wi’ in the previous row. We therefore take the maximum of these two possibilities to fill the current state. 

Brute Force Approach

EXAMPLE:

def knapSack(W, wt, val, n):
   # initial conditions
   if n == 0 or W == 0 :
      return 0
   # If weight is higher than capacity then it is not included
   if (wt[n-1] > W):
      return knapSack(W, wt, val, n-1)
   # return either nth item being included or not
   else:
      return max(val[n-1] + knapSack(W-wt[n-1], wt, val, n-1),
         knapSack(W, wt, val, n-1))
# To test above function
val = [60, 100, 120]
wt = [10, 20, 30]
W = 50
n = len(val)
print (knapSack(W, wt, val, n))

OUTPUT:

220

EXPLANATION:

Brute force is a very straightforward approach to solving the Knapsack problem. For n items to
choose from, then there will be 2n possible combinations of items for the knapsack. An item is either chosen or not. A bit string of 0’s and 1’s is generated, which is a length equal to the number of items, i.e., n. If the ith symbol of a bit string is 0, then that item is not chosen. And if it is 1, the item is chosen.

Must Read

Conclusion

In this article, we discussed various approaches to implement the knapsack problem algorithm. Above all three ways, the Dynamic Programing approach is the best method to solve Python’s knapsack problem. In conclusion, we can say it is very straightforward and simple to understand code. The time and space complexity of Dynamic Programming is also more efficient than the others.

Still have any doubts or questions, do let me know in the comment section below. I will try to help you as soon as possible.

Happy Pythoning!

5 1 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
x