KnapSack Problem
Below the program will find the maximum value giving the following weight limit W. For example, having a W=5 (weight limit of 5), what is the maximum value?
Reference: repl.it example video
The answer should be 5+2+2 == 9. These are the values added up. The 5 has a weight of 3 and each 2 has a weight of 1 total weights = 3+1+1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def knapSack(W, wt, val): | |
# Simple Checks | |
if [i for i in wt if i <= 0] != []: | |
print("Weights must be < 0") | |
return False | |
if len(wt) != len(val) and len(val) >0 : | |
print("len(wt) != len(val) or len == 0") | |
return False | |
n = len(val) | |
K = [[0 for x in range(W+1)] for x in range(n+1)] | |
V = [[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: | |
if val[i-1] + K[i-1][w-wt[i-1]] > K[i-1][w]: | |
V[i][w]=val[i-1] | |
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] | |
# Gets the items in our list | |
def minInList(target): | |
return min([ wt[j] for j in [o for o, x in enumerate(val) if x == target ]]) | |
ans=[] | |
N = W | |
for i in range(-1,-(len(V)),-1): | |
if N > 0 and V[i][N] > 0: | |
ans.append(V[i][N]) | |
N = N - minInList(V[i][N]) | |
sum_weight=sum([minInList(i) for i in ans] ) | |
return (K[n][W],sum_weight,ans) | |
# Driver program to test above function | |
val = [5,3,2,3,4,4] | |
wt = [3,2,1,1,1,1] | |
W = 7 | |
print(knapSack(W, wt, val)) | |
# This will return | |
# (18, 7, [4, 4, 3, 2, 5]) | |
# | |
# Where 18 is the cumulative value | |
# 7 is the total weight actually obtained | |
# [4, 4, 3, 2, 5] Are the value items chosen | |
# |
Fractional weights
There’s another problem here, but it uses fractional weight. Note, you need to sort the values and weights, before calling the function.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# knapsack.py | |
# A dynamic programming algorithm for the 0-1 knapsack problem and | |
# a greedy algorithm for the fractional knapsack problem | |
# Modified from: http://personal.denison.edu/~havill/algorithmics/python/knapsack.py | |
# A dynamic programming algorithm for the 0-1 knapsack problem. | |
def Knapsack01(v, w, W): | |
n = len(v) - 1 | |
c = [] # create an empty 2D array c | |
for i in range(n + 1): # c[i][j] = value of the optimal solution using | |
temp = [0] * (W + 1) # items 1 through i and maximum weight j | |
c.append(temp) | |
for i in range(1, n + 1): | |
for j in range(1, W + 1): | |
if w[i] <= j: # if item i will fit within weight j | |
if v[i] + c[i - 1][j - w[i]] > c[i - 1][j]: # add item i if value is better | |
c[i][j] = v[i] + c[i - 1][j - w[i]] # than without item i | |
else: | |
c[i][j] = c[i - 1][j] # otherwise, don't add item i | |
else: | |
c[i][j] = c[i - 1][j] | |
return c[n][W] # final value is in c[n][W] | |
# A greedy algorithm for the fractional knapsack problem. | |
# Note that we sort by v/w without modifying v or w so that we can | |
# output the indices of the actual items in the knapsack at the end | |
def KnapsackFrac(v, w, W): | |
order = bubblesortByRatio(v, w) # sort by v/w (see bubblesort below) | |
weight = 0.0 # current weight of the solution | |
value = 0.0 # current value of the solution | |
knapsack = [] # items in the knapsack - a list of (item, faction) pairs | |
n = len(v) | |
index = 0 # order[index] is the index in v and w of the item we're considering | |
while (weight < W) and (index < n): | |
if weight + w[order[index]] <= W: # if we can fit the entire order[index]-th item | |
knapsack.append((order[index], 1.0)) # add it and update weight and value | |
weight = weight + w[order[index]] | |
value = value + v[order[index]] | |
else: | |
fraction = (W - weight) / w[order[index]] # otherwise, calculate the fraction we can fit | |
knapsack.append((order[index], fraction)) # and add this fraction | |
weight = W | |
value = value + v[order[index]] * fraction | |
index = index + 1 | |
return (knapsack, value) # return the items in the knapsack and their value | |
# sort in descending order by ratio of list1[i] to list2[i] | |
# but instead of rearranging list1 and list2, keep the order in | |
# a separate array | |
def bubblesortByRatio(list1, list2): | |
n = len(list1) | |
order = list(range(n)) | |
for i in range(n - 1, 0, -1): # i ranges from n-1 down to 1 | |
for j in range(0, i): # j ranges from 0 up to i-1 | |
# if ratio of jth numbers > ratio of (j+1)st numbers then | |
if ((1.0 * list1[order[j]]) / list2[order[j]]) < ((1.0 * list1[order[j+1]]) / list2[order[j+1]]): | |
temp = order[j] # exchange "pointers" to these items | |
order[j] = order[j+1] | |
order[j+1] = temp | |
return order | |
v=[5.3,2,90] | |
w=[5.1,2,4.5] | |
W=9 | |
v = [2,6,4,8,9,17.3,17,35,46] | |
w = [2,6,4,8,9,17,17,35,46] | |
W = 50 | |
KnapsackFrac(v, w, W) | |
# Simple Examples above.... | |
v = [2,6,4,8,9,17.3,17,35,46] | |
w = [2,6,4,8,9,17,17,35,46] | |
v = v + v | |
w = w + w | |
print(KnapsackFrac(v, w, W)) | |
# Same value, but doesn't give you fraction. It finds the | |
# 4 and doesn't split the 8, like above. | |
v = [2,6,4,8,9,17.3,17,35,46] | |
w = [2,6,4,8,9,17,17,35,46] | |
v = v + v | |
w = w + w | |
v.sort() | |
w.sort() | |
print("\nNow, no split:") | |
print(KnapsackFrac(v, w, W)) | |