From 10f0cfed6ee7b696f9306278dc6293e1d1d5c24c Mon Sep 17 00:00:00 2001 From: Tomas Norre Mikkelsen Date: Thu, 22 Feb 2024 18:42:42 +0100 Subject: [PATCH] Add knapsack exercise (#633) --- config.json | 8 ++ .../practice/knapsack/.docs/instructions.md | 35 +++++ exercises/practice/knapsack/.meta/config.json | 19 +++ exercises/practice/knapsack/.meta/example.php | 28 ++++ exercises/practice/knapsack/.meta/tests.toml | 36 +++++ exercises/practice/knapsack/Knapsack.php | 33 +++++ exercises/practice/knapsack/KnapsackTest.php | 124 ++++++++++++++++++ 7 files changed, 283 insertions(+) create mode 100644 exercises/practice/knapsack/.docs/instructions.md create mode 100644 exercises/practice/knapsack/.meta/config.json create mode 100644 exercises/practice/knapsack/.meta/example.php create mode 100644 exercises/practice/knapsack/.meta/tests.toml create mode 100644 exercises/practice/knapsack/Knapsack.php create mode 100644 exercises/practice/knapsack/KnapsackTest.php diff --git a/config.json b/config.json index 27e0cfcf..c452b416 100644 --- a/config.json +++ b/config.json @@ -1172,6 +1172,14 @@ "prerequisites": [], "difficulty": 7 }, + { + "slug": "knapsack", + "name": "Knapsack", + "uuid": "79cdfd22-2c85-46af-ac00-719ac89bd5a9", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "circular-buffer", "name": "Circular Buffer", diff --git a/exercises/practice/knapsack/.docs/instructions.md b/exercises/practice/knapsack/.docs/instructions.md new file mode 100644 index 00000000..fadcee1b --- /dev/null +++ b/exercises/practice/knapsack/.docs/instructions.md @@ -0,0 +1,35 @@ +# Instructions + +In this exercise, let's try to solve a classic problem. + +Bob is a thief. +After months of careful planning, he finally manages to crack the security systems of a high-class apartment. + +In front of him are many items, each with a value (v) and weight (w). +Bob, of course, wants to maximize the total value he can get; he would gladly take all of the items if he could. +However, to his horror, he realizes that the knapsack he carries with him can only hold so much weight (W). + +Given a knapsack with a specific carrying capacity (W), help Bob determine the maximum value he can get from the items in the house. +Note that Bob can take only one of each item. + +All values given will be strictly positive. +Items will be represented as a list of items. +Each item will have a weight and value. + +For example: + +```none +Items: [ + { "weight": 5, "value": 10 }, + { "weight": 4, "value": 40 }, + { "weight": 6, "value": 30 }, + { "weight": 4, "value": 50 } +] + +Knapsack Limit: 10 +``` + +For the above, the first item has weight 5 and value 10, the second item has weight 4 and value 40, and so on. + +In this example, Bob should take the second and fourth item to maximize his value, which, in this case, is 90. +He cannot get more than 90 as his knapsack has a weight limit of 10. diff --git a/exercises/practice/knapsack/.meta/config.json b/exercises/practice/knapsack/.meta/config.json new file mode 100644 index 00000000..7b552c72 --- /dev/null +++ b/exercises/practice/knapsack/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "tomasnorre" + ], + "files": { + "solution": [ + "Knapsack.php" + ], + "test": [ + "KnapsackTest.php" + ], + "example": [ + ".meta/example.php" + ] + }, + "blurb": "Given a knapsack that can only carry a certain weight, determine which items to put in the knapsack in order to maximize their combined value.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Knapsack_problem" +} diff --git a/exercises/practice/knapsack/.meta/example.php b/exercises/practice/knapsack/.meta/example.php new file mode 100644 index 00000000..1a1e6cca --- /dev/null +++ b/exercises/practice/knapsack/.meta/example.php @@ -0,0 +1,28 @@ + $capacity) { + $table[$i + 1][$capacity] = $table[$i][$capacity]; + } else { + $table[$i + 1][$capacity] = max( + $table[$i][$capacity], + $value + $table[$i][$capacity - $weight] + ); + } + } + } + + return $table[count($items)][$maximumWeight]; + } +} diff --git a/exercises/practice/knapsack/.meta/tests.toml b/exercises/practice/knapsack/.meta/tests.toml new file mode 100644 index 00000000..8e013ef1 --- /dev/null +++ b/exercises/practice/knapsack/.meta/tests.toml @@ -0,0 +1,36 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[a4d7d2f0-ad8a-460c-86f3-88ba709d41a7] +description = "no items" +include = false + +[3993a824-c20e-493d-b3c9-ee8a7753ee59] +description = "no items" +reimplements = "a4d7d2f0-ad8a-460c-86f3-88ba709d41a7" + +[1d39e98c-6249-4a8b-912f-87cb12e506b0] +description = "one item, too heavy" + +[833ea310-6323-44f2-9d27-a278740ffbd8] +description = "five items (cannot be greedy by weight)" + +[277cdc52-f835-4c7d-872b-bff17bab2456] +description = "five items (cannot be greedy by value)" + +[81d8e679-442b-4f7a-8a59-7278083916c9] +description = "example knapsack" + +[f23a2449-d67c-4c26-bf3e-cde020f27ecc] +description = "8 items" + +[7c682ae9-c385-4241-a197-d2fa02c81a11] +description = "15 items" diff --git a/exercises/practice/knapsack/Knapsack.php b/exercises/practice/knapsack/Knapsack.php new file mode 100644 index 00000000..9b93173d --- /dev/null +++ b/exercises/practice/knapsack/Knapsack.php @@ -0,0 +1,33 @@ +. + * + * To disable strict typing, comment out the directive below. + */ + +declare(strict_types=1); + +class Knapsack +{ + public function getMaximumValue(int $maximumWeight, array $items): int + { + throw new \BadMethodCallException(sprintf('Implement the %s method', __FUNCTION__)); + } +} diff --git a/exercises/practice/knapsack/KnapsackTest.php b/exercises/practice/knapsack/KnapsackTest.php new file mode 100644 index 00000000..1abe41af --- /dev/null +++ b/exercises/practice/knapsack/KnapsackTest.php @@ -0,0 +1,124 @@ +knapsack = new Knapsack(); + } + + /** + * uuid: 3993a824-c20e-493d-b3c9-ee8a7753ee59 + */ + public function testNoItems(): void + { + $this->assertEquals(0, $this->knapsack->getMaximumValue(100, [])); + } + + /** + * uuid: 1d39e98c-6249-4a8b-912f-87cb12e506b0 + */ + public function testOneItemTooHeavy(): void + { + $items = [[ 'weight' => 100, 'value' => 1 ]]; + $this->assertEquals(0, $this->knapsack->getMaximumValue(10, $items)); + } + + /** + * uuid: 833ea310-6323-44f2-9d27-a278740ffbd8 + */ + public function testFiveItemsCannotBeGreedyByWeight(): void + { + $items = [ + [ 'weight' => 2, 'value' => 5 ], + [ 'weight' => 2, 'value' => 5 ], + [ 'weight' => 2, 'value' => 5 ], + [ 'weight' => 2, 'value' => 5 ], + [ 'weight' => 10, 'value' => 21 ], + ]; + $this->assertEquals(21, $this->knapsack->getMaximumValue(10, $items)); + } + + /** + * uuid: 277cdc52-f835-4c7d-872b-bff17bab2456 + */ + public function testFiveItemsCannotBeGreedyByValue(): void + { + $items = [ + [ 'weight' => 2, 'value' => 20 ], + [ 'weight' => 2, 'value' => 20 ], + [ 'weight' => 2, 'value' => 20 ], + [ 'weight' => 2, 'value' => 20 ], + [ 'weight' => 10, 'value' => 50 ], + ]; + $this->assertEquals(80, $this->knapsack->getMaximumValue(10, $items)); + } + + /** + * uuid: 81d8e679-442b-4f7a-8a59-7278083916c9 + */ + public function testExampleKnapsack(): void + { + $items = [ + [ 'weight' => 5, 'value' => 10 ], + [ 'weight' => 4, 'value' => 40 ], + [ 'weight' => 6, 'value' => 30 ], + [ 'weight' => 4, 'value' => 50 ], + ]; + $this->assertEquals(90, $this->knapsack->getMaximumValue(10, $items)); + } + + /** + * uuid: f23a2449-d67c-4c26-bf3e-cde020f27ecc + */ + public function testEightItems(): void + { + $items = [ + [ 'weight' => 25, 'value' => 350 ], + [ 'weight' => 35, 'value' => 400 ], + [ 'weight' => 45, 'value' => 450 ], + [ 'weight' => 5, 'value' => 20 ], + [ 'weight' => 25, 'value' => 70 ], + [ 'weight' => 3, 'value' => 8 ], + [ 'weight' => 2, 'value' => 5 ], + [ 'weight' => 2, 'value' => 5 ], + ]; + $this->assertEquals(900, $this->knapsack->getMaximumValue(104, $items)); + } + + /** + * uuid: 7c682ae9-c385-4241-a197-d2fa02c81a11 + */ + public function testFifteenItems(): void + { + $items = [ + [ 'weight' => 70, 'value' => 135 ], + [ 'weight' => 73, 'value' => 139 ], + [ 'weight' => 77, 'value' => 149 ], + [ 'weight' => 80, 'value' => 150 ], + [ 'weight' => 82, 'value' => 156 ], + [ 'weight' => 87, 'value' => 163 ], + [ 'weight' => 90, 'value' => 173 ], + [ 'weight' => 94, 'value' => 184 ], + [ 'weight' => 98, 'value' => 192 ], + [ 'weight' => 106, 'value' => 201 ], + [ 'weight' => 110, 'value' => 210 ], + [ 'weight' => 113, 'value' => 214 ], + [ 'weight' => 115, 'value' => 221 ], + [ 'weight' => 118, 'value' => 229 ], + [ 'weight' => 120, 'value' => 240 ], + ]; + $this->assertEquals(1458, $this->knapsack->getMaximumValue(750, $items)); + } +}