Skip to content

Commit 0a816f5

Browse files
New exercise ✨: Knapsack (#478)
Co-authored-by: Derk-Jan Karrenbeld <derk-jan+github@karrenbeld.info>
1 parent 5a1ce7e commit 0a816f5

File tree

14 files changed

+455
-33
lines changed

14 files changed

+455
-33
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ If there is no such issue, you may open one. The baseline of work is as follows:
7474
1. Create a `<slug>.test.ts` test file. Here add the tests, per canonical data if possible (more on canonical data below).
7575
1. Create a `.meta/proof.ci.ts` file. Place a working implementation, assuming it's renamed to `<slug>.ts`
7676
1. Create `.meta/tests.toml`. If the exercise that is being implemented has test data in the [problem specifications repository][problem-specifications], the contents of this file **must** be a list of UUIDs of the tests that are implemented or not implemented. Scroll down to [tools](#tools) to find the configlet application which aids generating this file _interactively_.
77+
1. Create a `.meta/config.json`. Copy the structure from any other `.meta/config.json`. Fill the `blurb`, `source` and `source_url` according to the `metadata.yml` in the [problem specifications repository][problem-specifications]. Add yourself as author.
78+
1. Create a `.docs/instructions.md` file. Copy the instructions from the [problem specifications repository][problem-specifications]
7779
1. Run the tests locally, using `scripts/test`: `ASSIGNMENT=slug yarn babel-node scripts/test`.
7880
1. Run the linter locally, using `scripts/lint`: `ASSIGNMENT=slug yarn babel-node scripts/lint`.
7981
1. Create an entry in `config.json`: a unique _new_ UUID (you can use the `configlet uuid` tool to generate one, scroll down to [tools](#tools) to see how you can get it), give it a difficulty (should be similar to similar exercises), and make sure the _order_ of the file is sane. Currently the file is ordered first on core - non core, then on difficulty low to high, and finally lexographically.

config.json

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -973,24 +973,6 @@
973973
"math"
974974
]
975975
},
976-
{
977-
"slug": "crypto-square",
978-
"name": "Crypto Square",
979-
"uuid": "e92b7444-a5c3-4452-82fd-8c70cf14085a",
980-
"practices": [],
981-
"prerequisites": [],
982-
"difficulty": 9,
983-
"topics": [
984-
"algorithms",
985-
"arrays",
986-
"conditionals",
987-
"loops",
988-
"regular_expressions",
989-
"sorting",
990-
"text_formatting",
991-
"transforming"
992-
]
993-
},
994976
{
995977
"slug": "robot-simulator",
996978
"name": "Robot Simulator",
@@ -1007,21 +989,7 @@
1007989
"strings"
1008990
]
1009991
},
1010-
{
1011-
"slug": "kindergarten-garden",
1012-
"name": "Kindergarten Garden",
1013-
"uuid": "d70d3579-999c-452c-9243-98908e24b47e",
1014-
"practices": [],
1015-
"prerequisites": [],
1016-
"difficulty": 7,
1017-
"topics": [
1018-
"arrays",
1019-
"conditionals",
1020-
"loops",
1021-
"strings",
1022-
"text_formatting"
1023-
]
1024-
},
992+
1025993
{
1026994
"slug": "armstrong-numbers",
1027995
"name": "Armstrong Numbers",
@@ -1126,6 +1094,21 @@
11261094
"difficulty": 7,
11271095
"topics": ["algorithms", "arrays", "games"]
11281096
},
1097+
{
1098+
"slug": "kindergarten-garden",
1099+
"name": "Kindergarten Garden",
1100+
"uuid": "d70d3579-999c-452c-9243-98908e24b47e",
1101+
"practices": [],
1102+
"prerequisites": [],
1103+
"difficulty": 7,
1104+
"topics": [
1105+
"arrays",
1106+
"conditionals",
1107+
"loops",
1108+
"strings",
1109+
"text_formatting"
1110+
]
1111+
},
11291112
{
11301113
"slug": "queen-attack",
11311114
"name": "Queen Attack",
@@ -1151,6 +1134,33 @@
11511134
"prerequisites": [],
11521135
"difficulty": 8,
11531136
"topics": ["algorithms", "closures", "reactive_programming"]
1137+
},
1138+
{
1139+
"slug": "crypto-square",
1140+
"name": "Crypto Square",
1141+
"uuid": "e92b7444-a5c3-4452-82fd-8c70cf14085a",
1142+
"practices": [],
1143+
"prerequisites": [],
1144+
"difficulty": 9,
1145+
"topics": [
1146+
"algorithms",
1147+
"arrays",
1148+
"conditionals",
1149+
"loops",
1150+
"regular_expressions",
1151+
"sorting",
1152+
"text_formatting",
1153+
"transforming"
1154+
]
1155+
},
1156+
{
1157+
"slug": "knapsack",
1158+
"name": "Knapsack",
1159+
"uuid": "b80ebb8d-9416-4fc8-91ee-3db99411810c",
1160+
"practices": [],
1161+
"prerequisites": [],
1162+
"difficulty": 9,
1163+
"topics": ["algorithms"]
11541164
}
11551165
]
11561166
},
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Description
2+
3+
In this exercise, let's try to solve a classic problem.
4+
5+
Bob is a thief. After months of careful planning, he finally manages to
6+
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). Bob,
9+
of course, wants to maximize the total value he can get; he would gladly
10+
take all of the items if he could. However, to his horror, he realizes that
11+
the knapsack he carries with him can only hold so much weight (W).
12+
13+
Given a knapsack with a specific carrying capacity (W), help Bob determine
14+
the maximum value he can get from the items in the house. Note that Bob can
15+
take only one of each item.
16+
17+
All values given will be strictly positive. Items will be represented as a
18+
list of pairs, `wi` and `vi`, where the first element `wi` is the weight of
19+
the *i*th item and `vi` is the value for that item.
20+
21+
For example:
22+
23+
Items: [
24+
{ "weight": 5, "value": 10 },
25+
{ "weight": 4, "value": 40 },
26+
{ "weight": 6, "value": 30 },
27+
{ "weight": 4, "value": 50 }
28+
]
29+
30+
Knapsack Limit: 10
31+
32+
For the above, the first item has weight 5 and value 10, the second item has
33+
weight 4 and value 40, and so on.
34+
35+
In this example, Bob should take the second and fourth item to maximize his
36+
value, which, in this case, is 90. He cannot get more than 90 as his
37+
knapsack has a weight limit of 10.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
!.meta
2+
3+
# Protected or generated
4+
.git
5+
.vscode
6+
7+
# When using npm
8+
node_modules/*
9+
10+
# Configuration files
11+
babel.config.js
12+
jest.config.js

exercises/practice/knapsack/.eslintrc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"root": true,
3+
"parser": "@typescript-eslint/parser",
4+
"parserOptions": {
5+
"project": "./tsconfig.json",
6+
"ecmaFeatures": {
7+
"jsx": true
8+
},
9+
"ecmaVersion": 2020,
10+
"sourceType": "module"
11+
},
12+
"extends": "@exercism/eslint-config-typescript",
13+
"env": {
14+
"jest": true
15+
},
16+
"overrides": [
17+
{
18+
"files": [".meta/proof.ci.ts", ".meta/exemplar.ts", "*.test.ts"],
19+
"excludedFiles": ["custom.test.ts"],
20+
"extends": "@exercism/eslint-config-typescript/maintainers"
21+
}
22+
]
23+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"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.",
3+
"authors": ["JoshiRaez"],
4+
"contributors": ["SleeplessByte"],
5+
"files": {
6+
"solution": ["knapsack.ts"],
7+
"test": ["knapsack.test.ts"],
8+
"example": [".meta/proof.ci.ts"]
9+
},
10+
"source": "Wikipedia",
11+
"source_url": "https://en.wikipedia.org/wiki/Knapsack_problem"
12+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
type Item = {
2+
weight: number
3+
value: number
4+
}
5+
6+
export function maximumValue({
7+
maximumWeight,
8+
items,
9+
}: {
10+
maximumWeight: number
11+
items: Item[]
12+
}): number {
13+
const maxValueForItemsSizeToWeight: number[][] = new Array(items.length + 1)
14+
.fill(undefined)
15+
.map(() => new Array(maximumWeight + 1))
16+
17+
return solve(items.length, maximumWeight, items, maxValueForItemsSizeToWeight)
18+
}
19+
20+
function solve(
21+
setItemsSize: number,
22+
maxWeight: number,
23+
items: Item[],
24+
maxValueForItemsSizeToWeight: number[][]
25+
): number {
26+
return memoizedMax(
27+
setItemsSize,
28+
maxWeight,
29+
items,
30+
maxValueForItemsSizeToWeight
31+
)
32+
}
33+
34+
function memoizedMax(
35+
setItemsSize: number,
36+
maxWeight: number,
37+
items: Item[],
38+
maxValueForItemsSizeToWeight: number[][]
39+
): number {
40+
const alreadyKnownMaxValue =
41+
maxValueForItemsSizeToWeight[setItemsSize][maxWeight]
42+
if (alreadyKnownMaxValue || alreadyKnownMaxValue === 0)
43+
return alreadyKnownMaxValue
44+
45+
const max = calculateMax(
46+
setItemsSize,
47+
maxWeight,
48+
items,
49+
maxValueForItemsSizeToWeight
50+
)
51+
maxValueForItemsSizeToWeight[setItemsSize][maxWeight] = max
52+
53+
return max
54+
}
55+
56+
function calculateMax(
57+
setItemsSize: number,
58+
maxWeight: number,
59+
items: Item[],
60+
maxValueForItemsSizeToWeight: number[][]
61+
): number {
62+
if (!setItemsSize) return 0
63+
64+
return Math.max(
65+
...Array.from(Array(maxWeight + 1).keys()).map(
66+
(checkingWeight) =>
67+
memoizedMax(
68+
setItemsSize - 1,
69+
checkingWeight,
70+
items,
71+
maxValueForItemsSizeToWeight
72+
) +
73+
valueOfItemIfFits(maxWeight, checkingWeight, items[setItemsSize - 1])
74+
)
75+
)
76+
}
77+
78+
function valueOfItemIfFits(
79+
maxWeight: number,
80+
alreadyInBagWeight: number,
81+
itemToFit: Item
82+
): number {
83+
if (alreadyInBagWeight + itemToFit.weight <= maxWeight) return itemToFit.value
84+
85+
return 0
86+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[a4d7d2f0-ad8a-460c-86f3-88ba709d41a7]
2+
description = "No items"
3+
include = true
4+
5+
[1d39e98c-6249-4a8b-912f-87cb12e506b0]
6+
description = "One item, but too heavy"
7+
include = true
8+
9+
[833ea310-6323-44f2-9d27-a278740ffbd8]
10+
description = "Five items. Can\'t be greedy by weight"
11+
include = true
12+
13+
[277cdc52-f835-4c7d-872b-bff17bab2456]
14+
description = "Five items. Can\'t be greedy by value"
15+
include = true
16+
17+
[81d8e679-442b-4f7a-8a59-7278083916c9]
18+
description = "Example knapsack"
19+
include = true
20+
21+
[f23a2449-d67c-4c26-bf3e-cde020f27ecc]
22+
description = "8 items"
23+
include = true
24+
25+
[7c682ae9-c385-4241-a197-d2fa02c81a11]
26+
description = "15 items"
27+
include = true
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module.exports = {
2+
presets: [
3+
[
4+
'@babel/preset-env',
5+
{
6+
targets: {
7+
node: 'current',
8+
},
9+
useBuiltIns: 'entry',
10+
corejs: '3.17',
11+
},
12+
],
13+
'@babel/preset-typescript',
14+
],
15+
plugins: [
16+
'@babel/proposal-class-properties',
17+
'@babel/proposal-object-rest-spread',
18+
'@babel/plugin-syntax-bigint',
19+
],
20+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module.exports = {
2+
verbose: true,
3+
projects: ['<rootDir>'],
4+
testMatch: [
5+
'**/__tests__/**/*.[jt]s?(x)',
6+
'**/test/**/*.[jt]s?(x)',
7+
'**/?(*.)+(spec|test).[jt]s?(x)',
8+
],
9+
testPathIgnorePatterns: [
10+
'/(?:production_)?node_modules/',
11+
'.d.ts$',
12+
'<rootDir>/test/fixtures',
13+
'<rootDir>/test/helpers',
14+
'__mocks__',
15+
],
16+
transform: {
17+
'^.+\\.[jt]sx?$': 'babel-jest',
18+
},
19+
}

0 commit comments

Comments
 (0)