Skip to content

Commit 10f0cfe

Browse files
authored
Add knapsack exercise (#633)
1 parent 1144087 commit 10f0cfe

File tree

7 files changed

+283
-0
lines changed

7 files changed

+283
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,14 @@
11721172
"prerequisites": [],
11731173
"difficulty": 7
11741174
},
1175+
{
1176+
"slug": "knapsack",
1177+
"name": "Knapsack",
1178+
"uuid": "79cdfd22-2c85-46af-ac00-719ac89bd5a9",
1179+
"practices": [],
1180+
"prerequisites": [],
1181+
"difficulty": 5
1182+
},
11751183
{
11761184
"slug": "circular-buffer",
11771185
"name": "Circular Buffer",
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Instructions
2+
3+
In this exercise, let's try to solve a classic problem.
4+
5+
Bob is a thief.
6+
After months of careful planning, he finally manages to crack the security systems of a high-class apartment.
7+
8+
In front of him are many items, each with a value (v) and weight (w).
9+
Bob, of course, wants to maximize the total value he can get; he would gladly take all of the items if he could.
10+
However, to his horror, he realizes that the knapsack he carries with him can only hold so much weight (W).
11+
12+
Given a knapsack with a specific carrying capacity (W), help Bob determine the maximum value he can get from the items in the house.
13+
Note that Bob can take only one of each item.
14+
15+
All values given will be strictly positive.
16+
Items will be represented as a list of items.
17+
Each item will have a weight and value.
18+
19+
For example:
20+
21+
```none
22+
Items: [
23+
{ "weight": 5, "value": 10 },
24+
{ "weight": 4, "value": 40 },
25+
{ "weight": 6, "value": 30 },
26+
{ "weight": 4, "value": 50 }
27+
]
28+
29+
Knapsack Limit: 10
30+
```
31+
32+
For the above, the first item has weight 5 and value 10, the second item has weight 4 and value 40, and so on.
33+
34+
In this example, Bob should take the second and fourth item to maximize his value, which, in this case, is 90.
35+
He cannot get more than 90 as his knapsack has a weight limit of 10.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"tomasnorre"
4+
],
5+
"files": {
6+
"solution": [
7+
"Knapsack.php"
8+
],
9+
"test": [
10+
"KnapsackTest.php"
11+
],
12+
"example": [
13+
".meta/example.php"
14+
]
15+
},
16+
"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.",
17+
"source": "Wikipedia",
18+
"source_url": "https://en.wikipedia.org/wiki/Knapsack_problem"
19+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
class Knapsack
6+
{
7+
public function getMaximumValue(int $maximumWeight, array $items): int
8+
{
9+
$table = array_fill(0, count($items) + 1, array_fill(0, $maximumWeight + 1, 0));
10+
11+
for ($i = 0, $iMax = count($items); $i < $iMax; $i++) {
12+
for ($capacity = 1; $capacity < $maximumWeight + 1; $capacity++) {
13+
$value = $items[$i]['value'];
14+
$weight = $items[$i]['weight'];
15+
if ($weight > $capacity) {
16+
$table[$i + 1][$capacity] = $table[$i][$capacity];
17+
} else {
18+
$table[$i + 1][$capacity] = max(
19+
$table[$i][$capacity],
20+
$value + $table[$i][$capacity - $weight]
21+
);
22+
}
23+
}
24+
}
25+
26+
return $table[count($items)][$maximumWeight];
27+
}
28+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[a4d7d2f0-ad8a-460c-86f3-88ba709d41a7]
13+
description = "no items"
14+
include = false
15+
16+
[3993a824-c20e-493d-b3c9-ee8a7753ee59]
17+
description = "no items"
18+
reimplements = "a4d7d2f0-ad8a-460c-86f3-88ba709d41a7"
19+
20+
[1d39e98c-6249-4a8b-912f-87cb12e506b0]
21+
description = "one item, too heavy"
22+
23+
[833ea310-6323-44f2-9d27-a278740ffbd8]
24+
description = "five items (cannot be greedy by weight)"
25+
26+
[277cdc52-f835-4c7d-872b-bff17bab2456]
27+
description = "five items (cannot be greedy by value)"
28+
29+
[81d8e679-442b-4f7a-8a59-7278083916c9]
30+
description = "example knapsack"
31+
32+
[f23a2449-d67c-4c26-bf3e-cde020f27ecc]
33+
description = "8 items"
34+
35+
[7c682ae9-c385-4241-a197-d2fa02c81a11]
36+
description = "15 items"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* By adding type hints and enabling strict type checking, code can become
5+
* easier to read, self-documenting and reduce the number of potential bugs.
6+
* By default, type declarations are non-strict, which means they will attempt
7+
* to change the original type to match the type specified by the
8+
* type-declaration.
9+
*
10+
* In other words, if you pass a string to a function requiring a float,
11+
* it will attempt to convert the string value to a float.
12+
*
13+
* To enable strict mode, a single declare directive must be placed at the top
14+
* of the file.
15+
* This means that the strictness of typing is configured on a per-file basis.
16+
* This directive not only affects the type declarations of parameters, but also
17+
* a function's return type.
18+
*
19+
* For more info review the Concept on strict type checking in the PHP track
20+
* <link>.
21+
*
22+
* To disable strict typing, comment out the directive below.
23+
*/
24+
25+
declare(strict_types=1);
26+
27+
class Knapsack
28+
{
29+
public function getMaximumValue(int $maximumWeight, array $items): int
30+
{
31+
throw new \BadMethodCallException(sprintf('Implement the %s method', __FUNCTION__));
32+
}
33+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
class KnapsackTest extends TestCase
8+
{
9+
private Knapsack $knapsack;
10+
11+
public static function setUpBeforeClass(): void
12+
{
13+
require_once 'Knapsack.php';
14+
}
15+
16+
protected function setUp(): void
17+
{
18+
$this->knapsack = new Knapsack();
19+
}
20+
21+
/**
22+
* uuid: 3993a824-c20e-493d-b3c9-ee8a7753ee59
23+
*/
24+
public function testNoItems(): void
25+
{
26+
$this->assertEquals(0, $this->knapsack->getMaximumValue(100, []));
27+
}
28+
29+
/**
30+
* uuid: 1d39e98c-6249-4a8b-912f-87cb12e506b0
31+
*/
32+
public function testOneItemTooHeavy(): void
33+
{
34+
$items = [[ 'weight' => 100, 'value' => 1 ]];
35+
$this->assertEquals(0, $this->knapsack->getMaximumValue(10, $items));
36+
}
37+
38+
/**
39+
* uuid: 833ea310-6323-44f2-9d27-a278740ffbd8
40+
*/
41+
public function testFiveItemsCannotBeGreedyByWeight(): void
42+
{
43+
$items = [
44+
[ 'weight' => 2, 'value' => 5 ],
45+
[ 'weight' => 2, 'value' => 5 ],
46+
[ 'weight' => 2, 'value' => 5 ],
47+
[ 'weight' => 2, 'value' => 5 ],
48+
[ 'weight' => 10, 'value' => 21 ],
49+
];
50+
$this->assertEquals(21, $this->knapsack->getMaximumValue(10, $items));
51+
}
52+
53+
/**
54+
* uuid: 277cdc52-f835-4c7d-872b-bff17bab2456
55+
*/
56+
public function testFiveItemsCannotBeGreedyByValue(): void
57+
{
58+
$items = [
59+
[ 'weight' => 2, 'value' => 20 ],
60+
[ 'weight' => 2, 'value' => 20 ],
61+
[ 'weight' => 2, 'value' => 20 ],
62+
[ 'weight' => 2, 'value' => 20 ],
63+
[ 'weight' => 10, 'value' => 50 ],
64+
];
65+
$this->assertEquals(80, $this->knapsack->getMaximumValue(10, $items));
66+
}
67+
68+
/**
69+
* uuid: 81d8e679-442b-4f7a-8a59-7278083916c9
70+
*/
71+
public function testExampleKnapsack(): void
72+
{
73+
$items = [
74+
[ 'weight' => 5, 'value' => 10 ],
75+
[ 'weight' => 4, 'value' => 40 ],
76+
[ 'weight' => 6, 'value' => 30 ],
77+
[ 'weight' => 4, 'value' => 50 ],
78+
];
79+
$this->assertEquals(90, $this->knapsack->getMaximumValue(10, $items));
80+
}
81+
82+
/**
83+
* uuid: f23a2449-d67c-4c26-bf3e-cde020f27ecc
84+
*/
85+
public function testEightItems(): void
86+
{
87+
$items = [
88+
[ 'weight' => 25, 'value' => 350 ],
89+
[ 'weight' => 35, 'value' => 400 ],
90+
[ 'weight' => 45, 'value' => 450 ],
91+
[ 'weight' => 5, 'value' => 20 ],
92+
[ 'weight' => 25, 'value' => 70 ],
93+
[ 'weight' => 3, 'value' => 8 ],
94+
[ 'weight' => 2, 'value' => 5 ],
95+
[ 'weight' => 2, 'value' => 5 ],
96+
];
97+
$this->assertEquals(900, $this->knapsack->getMaximumValue(104, $items));
98+
}
99+
100+
/**
101+
* uuid: 7c682ae9-c385-4241-a197-d2fa02c81a11
102+
*/
103+
public function testFifteenItems(): void
104+
{
105+
$items = [
106+
[ 'weight' => 70, 'value' => 135 ],
107+
[ 'weight' => 73, 'value' => 139 ],
108+
[ 'weight' => 77, 'value' => 149 ],
109+
[ 'weight' => 80, 'value' => 150 ],
110+
[ 'weight' => 82, 'value' => 156 ],
111+
[ 'weight' => 87, 'value' => 163 ],
112+
[ 'weight' => 90, 'value' => 173 ],
113+
[ 'weight' => 94, 'value' => 184 ],
114+
[ 'weight' => 98, 'value' => 192 ],
115+
[ 'weight' => 106, 'value' => 201 ],
116+
[ 'weight' => 110, 'value' => 210 ],
117+
[ 'weight' => 113, 'value' => 214 ],
118+
[ 'weight' => 115, 'value' => 221 ],
119+
[ 'weight' => 118, 'value' => 229 ],
120+
[ 'weight' => 120, 'value' => 240 ],
121+
];
122+
$this->assertEquals(1458, $this->knapsack->getMaximumValue(750, $items));
123+
}
124+
}

0 commit comments

Comments
 (0)