{
"cells": [
{
"cell_type": "markdown",
"id": "96a73f12",
"metadata": {},
"source": [
"---\n",
"title: \"The knapsack problem\"\n",
"type: docs\n",
"date: 2023-06-23\n",
"math: \"true\"\n",
"weight: 103\n",
"description: >\n",
" A model for the knapsack problem.\n",
"---"
]
},
{
"cell_type": "markdown",
"id": "45355298",
"metadata": {},
"source": [
"_This file can be downloaded as a [jupyter notebook](https://jupyter.org/) and executed with a [Java kernel](https://github.com/SpencerPark/IJava). The next cell is then used to add the dependency to choco and can be ignored otherwise._ \n",
"\n",
"[>> ipynb <<]()"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "2a90fd6a",
"metadata": {},
"outputs": [],
"source": [
"// Add maven dependencies at runtime \n",
"%maven org.choco-solver:choco-solver:4.10.13"
]
},
{
"cell_type": "markdown",
"id": "6acd1cce",
"metadata": {},
"source": [
"------ \n",
"Wikipedia:
\n",
"> Given a set of items, each with a weight and a value, determine the count of each item to include in a collection so that the total weight is less than or equal to a given limit and the total value is as large as possible. It derives its name from the problem faced by someone who is constrained by a fixed-size knapsack and must fill it with the most useful items.\""
]
},
{
"cell_type": "markdown",
"id": "aad1b076",
"metadata": {},
"source": [
"First manage imports."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "4381af83",
"metadata": {},
"outputs": [],
"source": [
"import org.chocosolver.solver.Model;\n",
"import org.chocosolver.solver.Solver;\n",
"import org.chocosolver.solver.exception.ContradictionException;\n",
"import org.chocosolver.solver.variables.IntVar;\n",
"\n",
"import java.util.Arrays;\n",
"\n",
"import static org.chocosolver.solver.search.strategy.Search.inputOrderLBSearch;\n",
"import static org.chocosolver.solver.search.strategy.Search.inputOrderUBSearch;"
]
},
{
"cell_type": "markdown",
"id": "f7715dc8",
"metadata": {},
"source": [
"## Input data\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e84eb32a",
"metadata": {},
"outputs": [],
"source": [
"int n = 10; // number of different items\n",
"int W = 67; // a maximum weight capacity \n",
"int[] w = new int[]{23, 26,20,18,32, 27, 29, 26, 30, 27}; // weight of items\n",
"int[] v = new int[]{505, 352, 458, 220, 354, 414, 498, 545, 473, 543}; // value of items"
]
},
{
"cell_type": "markdown",
"id": "231df9b7",
"metadata": {},
"source": [
"## The model"
]
},
{
"cell_type": "markdown",
"id": "e0a5f734",
"metadata": {},
"source": [
"Then, we can start modelling the problem with choco.\n",
"The first step is to defined a `Model` instance.\n",
"It is required to declare and store the variables and the constraints.\n",
"For convenience, an instance can be declared with a name."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "a78f8a49",
"metadata": {},
"outputs": [],
"source": [
"Model model = new Model(\"Knapsack\");"
]
},
{
"cell_type": "markdown",
"id": "6c85faba",
"metadata": {},
"source": [
"For each object, a variable is declared to count the number of times it appears in the knapsack."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "02a3b9c2",
"metadata": {},
"outputs": [],
"source": [
"IntVar[] items = new IntVar[n];\n",
"for (int i = 0; i < n; i++) {\n",
" items[i] = model.intVar(\"item_\" + (i + 1), 0, (int) Math.ceil(W*1. / w[i]));\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "964bd213",
"metadata": {},
"source": [
"The paramaters are:\n",
"- the prefix for setting the variables' name,\n",
"- the lower bound and the upper bound of each variable.\n",
"\n",
"**Remark:**\n",
"*To model 0-1 knapsack problem, the upper bound of each variable must be set to $1$.*\n",
"\n",
"The problem is to maximize the sum of the values of the items in the knapsack.\n",
"This amount is maintained in a variable:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "9232a895",
"metadata": {},
"outputs": [],
"source": [
"IntVar value = model.intVar(\"value\", 0, Arrays.stream(v).max().orElse(999) * n);"
]
},
{
"cell_type": "markdown",
"id": "50b4b5ae",
"metadata": {},
"source": [
"The sum of the weights is less than or equal to the knapsack's capacity:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "581d3ffe",
"metadata": {},
"outputs": [],
"source": [
"IntVar weight = model.intVar(\"weight\", 0, W);"
]
},
{
"cell_type": "markdown",
"id": "d93b09e8",
"metadata": {},
"source": [
"All variables are now declared, the `knapsack` constraint can be declared:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "a5417e4b",
"metadata": {},
"outputs": [],
"source": [
"model.knapsack(items, weight, value, w, v).post();"
]
},
{
"cell_type": "markdown",
"id": "ae59c44b",
"metadata": {},
"source": [
"The `value` variable has to be maximized:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "d09123e1",
"metadata": {},
"outputs": [],
"source": [
"model.setObjective(Model.MAXIMIZE, value);"
]
},
{
"cell_type": "markdown",
"id": "c90deecb",
"metadata": {},
"source": [
"## Finding the optimal solution"
]
},
{
"cell_type": "markdown",
"id": "a1e74a10",
"metadata": {},
"source": [
"Now that the model is ready, the solving step can be set up.\n",
"Here we define a top-down maximization:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "cfcc4900",
"metadata": {},
"outputs": [],
"source": [
"Solver solver = model.getSolver();\n",
"solver.setSearch(\n",
" inputOrderUBSearch(value), \n",
" inputOrderLBSearch(items));"
]
},
{
"cell_type": "markdown",
"id": "69977a2f",
"metadata": {},
"source": [
"Let's execute the solving:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "d1fc77de",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Knapsack -- 10 items\n",
"\tItem: Count\n",
"\tItem #1: 2\n",
"\tItem #2: 0\n",
"\tItem #3: 1\n",
"\tItem #4: 0\n",
"\tItem #5: 0\n",
"\tItem #6: 0\n",
"\tItem #7: 0\n",
"\tItem #8: 0\n",
"\tItem #9: 0\n",
"\tItem #10: 0\n",
"\n",
"\tWeight: 66\n",
"\n",
"\tValue: 1468\n"
]
}
],
"source": [
"while (solver.solve()) {\n",
" System.out.printf(\"Knapsack -- %d items\\n\", n);\n",
" System.out.println(\"\\tItem: Count\");\n",
" for (int i = 0; i < items.length; i++) {\n",
" System.out.printf(\"\\tItem #%d: %d\\n\", (i+1), items[i].getValue());\n",
" }\n",
" System.out.printf(\"\\n\\tWeight: %d\\n\", weight.getValue());\n",
" System.out.printf(\"\\n\\tValue: %d\\n\", value.getValue());\n",
"}\n",
"solver.reset(); // to solve the model several times"
]
},
{
"cell_type": "markdown",
"id": "095b4d94",
"metadata": {},
"source": [
"The optimal value for this instance of the knapsack problem is $1468$ with a total weight of $66$.\n",
"\n",
"When turned to a 0-1 knapsack problem, the optimal value is $1270$ with a total weight of $67$."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Java",
"language": "java",
"name": "java"
},
"language_info": {
"codemirror_mode": "java",
"file_extension": ".jshell",
"mimetype": "text/x-java-source",
"name": "Java",
"pygments_lexer": "java",
"version": "11.0.1+13-LTS"
}
},
"nbformat": 4,
"nbformat_minor": 5
}