title: "Lecture 3: Integer Linear Programming" author: "Junaid Hasan" date: "June 25, 2021"¶
Knapsack Problem¶
Alice wants to pack a set of items into her knapsack (backpack). However she can carry at most 100 pounds. The weights and values are given below
| Item | Weight | Value | |---------|---------|---------| | 1 | 30 | 240 | | 2 | 35 | 300 | | 3 | 10 | 100 | | 4 | 15 | 150 | | 5 | 35 | 360 | | 6 | 22 | 180 | | 7 | 29 | 220 | | 8 | 18 | 140 | | 9 | 11 | 90 |
Which items must she pack to maximize total value if there is just one of each item.
For instance the items could be paintings at an art show.
Thoughts¶
- Which items seem more enticing?
Mathematical Model¶
- Let us introduce a variable $ x_i $ such that
- $ x_i = 0 $ if object i is not in the knapsack.
- $ x_i = 1 $ if object i is in the knapsack.
- Can you formulate the problem?
- Note that $x_i$ is known as a binary variable.
(contd..)¶
- Then the LP is
- Maximize $$ 240x_1 + 300 x_2 + 100x_3 + 150 x_4 + 360x_5 + 180x_6 + 220x_7 + 140x_8 + 90x_9 $$ subject to
- $$ 30 x_1 + 35 x_2 + 10 x_3 + 15 x_4 + 35 x_5 + 22 x_6 + 29 x_7 + 18 x_8 + 11 x_9 \leq 100 $$
- $$ x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8, x_9 \in \{0,1 \} $$.
- Note that the variables are constrained to be integers (more specifically binary in this case).
- This program is called an integer linear program.
Solution (code)¶
In [31]:
objects = [1,2,3,4,5,6,7,8,9]
weights = {1:30,
2:35,
3:10,
4:15,
5:35,
6:22,
7:29,
8:18,
9:11}
value = {1:240,
2:300,
3:100,
4:150,
5:360,
6:180,
7:220,
8:140,
9:90}
maxweight = 100
In [32]:
def knapsackModel(objects, weights, value, maxweight):
n = len(weights)
from pyscipopt import Model, quicksum
model = Model("Alice's knapsack")
x = {}
for i in objects:
x[i] = model.addVar(vtype="B", name="x%s"%i)
model.addCons(quicksum(weights[i]*x[i] for i in objects) <= maxweight, "weight constraint")
model.setObjective(quicksum(value[i]*x[i] for i in objects), "maximize")
model.data = x
return model
In [33]:
def solveKnapsack(objects, weights, value, maxweight):
model = knapsackModel(objects, weights, value, maxweight)
model.optimize()
x = model.data
print("Total value", model.getObjVal())
for i in objects:
print("Object %s "%i, model.getVal(x[i]))
return model.getObjVal()
In [34]:
solveKnapsack(objects, weights, value, maxweight)
Total value 930.0 Object 1 -0.0 Object 2 -0.0 Object 3 1.0 Object 4 1.0 Object 5 1.0 Object 6 1.0 Object 7 -0.0 Object 8 1.0 Object 9 -0.0
Out[34]:
930.0
In [17]:
M = sum(weights[i] for i in objects)
totalWeightsList = [i for i in range(10, M+1)]
totalValueList = [solveKnapsack(objects, weights, value, i) for i in range(10, M+1)]
In [20]:
import matplotlib.pyplot as graph
graph.plot(totalWeightsList, totalValueList)
graph.xlabel("Max Weights of Knapsack")
graph.ylabel("Total Value gathered")
graph.show()
In [ ]:
Thoughts¶
- What other questions can we ask?
- Any other ideas?
Variation¶
- Suppose now that the objects are not limited to 1 in quantity, but rather there is an unlimited number of each.
- This can happen if its not an art show but say a grocery store.
- What should we change in the LP?
- Answer: Now $ x_i $ is not just 0 or 1, but $ x_i $ can be any non-negative integer.
Solution¶
In [37]:
def unlimitedknapsackModel(objects, weight, value, maxweight):
n = len(weight)
from pyscipopt import Model, quicksum, multidict
model = Model("knapsack")
x = {}
for i in objects:
x[i] = model.addVar(vtype="I", name="x(%s)"%i)
model.addCons(quicksum(weight[i]*x[i] for i in objects) <= maxweight, "weight limit")
model.setObjective(quicksum(value[i]*x[i] for i in objects), "maximize")
model.data = x
return model
In [38]:
def solveKnapsack2(objects, weight, value, maxweight):
model = unlimitedknapsackModel(objects, weight, value, maxweight)
model.optimize()
x = model.data
print("Total Price: ", model.getObjVal())
for i in objects:
print("%s" %i, model.getVal(x[i]))
return model.getObjVal()
Let change the name of the items. This can be done because we were using a dictionary.
In [39]:
from pyscipopt import Model, multidict, quicksum
objects, weight, value = multidict({"almonds":[30, 240],
"walnuts":[35, 300],
"cashew":[10,100],
"pistachio":[15,150],
"plum":[35, 360],
"cherry":[22,180],
"mango":[29,220],
"apple":[18, 140],
"banana":[11, 90]})
# objects = [1,2,3,4,5,6,7,8,9]
# weight = {1:30, 2:35, 3:10, 4:15, 5:35, 6:22, 7:29, 8:18, 9:11}
# value = {1:240, 2:300, 3:100, 4:150, 5:360, 6:180, 7:220, 8:140, 9:90}
maxweight = 100
In [40]:
solveKnapsack2(objects, weight, value, maxweight)
Total Price: 1020.0 almonds 0.0 walnuts 0.0 cashew 3.0 pistachio 0.0 plum 2.0 cherry 0.0 mango 0.0 apple 0.0 banana -0.0
Out[40]:
1020.0
Remarks¶
- Now it seems that object 3 is picked 3 times and object 5 twice.
- Why?
Ideas¶
- Where can you apply the Knapsack problem idea?
- An example can be to determine the best movies to show in a film festival of a total time of 24 hours, with weights being the movie lengths and the price being the ratings (obtained from movielens).
- Similarly what songs to sing in a Karaoke.
- Another example can be if you were planning a trip, and the places you want to go are valued by some method and the time spent is the weight.
In [ ]: