From 796f2cac587a0ec50f9b410832b3580e635a31eb Mon Sep 17 00:00:00 2001 From: Aron Demeter <66035744+dem4ron@users.noreply.github.com> Date: Fri, 7 Apr 2023 22:57:42 +0200 Subject: [PATCH] Add Knapsack exercise (#1661) * Add knapsack exercise * Underscore unused params, turn string diff to int diff --- config.json | 8 + .../practice/knapsack/.docs/instructions.md | 32 +++ exercises/practice/knapsack/.gitignore | 8 + exercises/practice/knapsack/.meta/config.json | 20 ++ exercises/practice/knapsack/.meta/example.rs | 26 +++ exercises/practice/knapsack/.meta/tests.toml | 31 +++ exercises/practice/knapsack/Cargo.toml | 8 + exercises/practice/knapsack/src/lib.rs | 8 + exercises/practice/knapsack/tests/knapsack.rs | 219 ++++++++++++++++++ 9 files changed, 360 insertions(+) create mode 100644 exercises/practice/knapsack/.docs/instructions.md create mode 100644 exercises/practice/knapsack/.gitignore create mode 100644 exercises/practice/knapsack/.meta/config.json create mode 100644 exercises/practice/knapsack/.meta/example.rs create mode 100644 exercises/practice/knapsack/.meta/tests.toml create mode 100644 exercises/practice/knapsack/Cargo.toml create mode 100644 exercises/practice/knapsack/src/lib.rs create mode 100644 exercises/practice/knapsack/tests/knapsack.rs diff --git a/config.json b/config.json index 3ad25420c..5609f8e10 100644 --- a/config.json +++ b/config.json @@ -1508,6 +1508,14 @@ ], "status": "deprecated" }, + { + "slug": "knapsack", + "name": "Knapsack", + "uuid": "cbccd0c5-eb15-4705-9a4c-0209861f078c", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, { "slug": "kindergarten-garden", "name": "Kindergarten Garden", diff --git a/exercises/practice/knapsack/.docs/instructions.md b/exercises/practice/knapsack/.docs/instructions.md new file mode 100644 index 000000000..1dbbca91c --- /dev/null +++ b/exercises/practice/knapsack/.docs/instructions.md @@ -0,0 +1,32 @@ +# 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 pairs, `wi` and `vi`, where the first element `wi` is the weight of the *i*th item and `vi` is the value for that item. + +For example: + +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/.gitignore b/exercises/practice/knapsack/.gitignore new file mode 100644 index 000000000..e24ae5981 --- /dev/null +++ b/exercises/practice/knapsack/.gitignore @@ -0,0 +1,8 @@ +# Generated by Cargo +# Will have compiled files and executables +/target/ +**/*.rs.bk + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/exercises/practice/knapsack/.meta/config.json b/exercises/practice/knapsack/.meta/config.json new file mode 100644 index 000000000..760ae0258 --- /dev/null +++ b/exercises/practice/knapsack/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "dem4ron" + ], + "files": { + "solution": [ + "src/lib.rs", + "Cargo.toml" + ], + "test": [ + "tests/knapsack.rs" + ], + "example": [ + ".meta/example.rs" + ] + }, + "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.rs b/exercises/practice/knapsack/.meta/example.rs new file mode 100644 index 000000000..74f431bbf --- /dev/null +++ b/exercises/practice/knapsack/.meta/example.rs @@ -0,0 +1,26 @@ +pub struct Item { + pub weight: u32, + pub value: u32, +} + +pub fn maximum_value(max_weight: u32, items: Vec) -> u32 { + let mut max_values = vec![vec![0; (max_weight + 1) as usize]; items.len() + 1]; + + for i in 1..=items.len() { + let item_weight = items[i - 1].weight as usize; + let item_value = items[i - 1].value; + + for w in 0..=(max_weight as usize) { + if item_weight <= w { + max_values[i][w] = std::cmp::max( + max_values[i - 1][w], + max_values[i - 1][w - item_weight] + item_value, + ); + } else { + max_values[i][w] = max_values[i - 1][w]; + } + } + } + + max_values[items.len()][max_weight as usize] +} diff --git a/exercises/practice/knapsack/.meta/tests.toml b/exercises/practice/knapsack/.meta/tests.toml new file mode 100644 index 000000000..febc7b26b --- /dev/null +++ b/exercises/practice/knapsack/.meta/tests.toml @@ -0,0 +1,31 @@ +# 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" + +[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/Cargo.toml b/exercises/practice/knapsack/Cargo.toml new file mode 100644 index 000000000..7b1d5d915 --- /dev/null +++ b/exercises/practice/knapsack/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "knapsack" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/exercises/practice/knapsack/src/lib.rs b/exercises/practice/knapsack/src/lib.rs new file mode 100644 index 000000000..f55b38500 --- /dev/null +++ b/exercises/practice/knapsack/src/lib.rs @@ -0,0 +1,8 @@ +pub struct Item { + pub weight: u32, + pub value: u32, +} + +pub fn maximum_value(_max_weight: u32, _items: Vec) -> u32 { + unimplemented!("Solve the knapsack exercise"); +} diff --git a/exercises/practice/knapsack/tests/knapsack.rs b/exercises/practice/knapsack/tests/knapsack.rs new file mode 100644 index 000000000..1432f7474 --- /dev/null +++ b/exercises/practice/knapsack/tests/knapsack.rs @@ -0,0 +1,219 @@ +use knapsack::*; + +#[test] +fn test_example_knapsack() { + let max_weight = 10; + let items = vec![ + Item { + weight: 5, + value: 10, + }, + Item { + weight: 4, + value: 40, + }, + Item { + weight: 6, + value: 30, + }, + Item { + weight: 4, + value: 50, + }, + ]; + + assert_eq!(maximum_value(max_weight, items), 90); +} + +#[test] +#[ignore] +fn test_no_items() { + let max_weight = 100; + let items = vec![]; + + assert_eq!(maximum_value(max_weight, items), 0); +} + +#[test] +#[ignore] +fn test_one_item_too_heavy() { + let max_weight = 10; + let items = vec![Item { + weight: 100, + value: 1, + }]; + + assert_eq!(maximum_value(max_weight, items), 0); +} + +#[test] +#[ignore] +fn test_five_items_cannot_be_greedy_by_weight() { + let max_weight = 10; + let items = vec![ + Item { + weight: 2, + value: 5, + }, + Item { + weight: 2, + value: 5, + }, + Item { + weight: 2, + value: 5, + }, + Item { + weight: 2, + value: 5, + }, + Item { + weight: 10, + value: 21, + }, + ]; + + assert_eq!(maximum_value(max_weight, items), 21); +} + +#[test] +#[ignore] +fn test_five_items_cannot_be_greedy_by_value() { + let max_weight = 10; + let items = vec![ + Item { + weight: 2, + value: 20, + }, + Item { + weight: 2, + value: 20, + }, + Item { + weight: 2, + value: 20, + }, + Item { + weight: 2, + value: 20, + }, + Item { + weight: 10, + value: 50, + }, + ]; + + assert_eq!(maximum_value(max_weight, items), 80); +} + +#[test] +#[ignore] +fn test_8_items() { + let max_weight = 104; + let items = vec![ + Item { + weight: 25, + value: 350, + }, + Item { + weight: 35, + value: 400, + }, + Item { + weight: 45, + value: 450, + }, + Item { + weight: 5, + value: 20, + }, + Item { + weight: 25, + value: 70, + }, + Item { + weight: 3, + value: 8, + }, + Item { + weight: 2, + value: 5, + }, + Item { + weight: 2, + value: 5, + }, + ]; + + assert_eq!(maximum_value(max_weight, items), 900); +} + +#[test] +#[ignore] +fn test_15_items() { + let max_weight = 750; + let items = vec![ + Item { + weight: 70, + value: 135, + }, + Item { + weight: 73, + value: 139, + }, + Item { + weight: 77, + value: 149, + }, + Item { + weight: 80, + value: 150, + }, + Item { + weight: 82, + value: 156, + }, + Item { + weight: 87, + value: 163, + }, + Item { + weight: 90, + value: 173, + }, + Item { + weight: 94, + value: 184, + }, + Item { + weight: 98, + value: 192, + }, + Item { + weight: 106, + value: 201, + }, + Item { + weight: 110, + value: 210, + }, + Item { + weight: 113, + value: 214, + }, + Item { + weight: 115, + value: 221, + }, + Item { + weight: 118, + value: 229, + }, + Item { + weight: 120, + value: 240, + }, + ]; + + assert_eq!(maximum_value(max_weight, items), 1458); +}