From 1dbc7d5daf0131d0ddeec84f55f9c74d9d404094 Mon Sep 17 00:00:00 2001 From: Nenad Date: Wed, 30 Oct 2024 08:36:05 +0100 Subject: [PATCH] Add knapsack --- config.json | 8 ++ .../practice/knapsack/.docs/instructions.md | 25 ++++ .../practice/knapsack/.docs/introduction.md | 8 ++ exercises/practice/knapsack/.meta/config.json | 22 ++++ .../practice/knapsack/.meta/example.cairo | 58 +++++++++ exercises/practice/knapsack/.meta/tests.toml | 36 ++++++ exercises/practice/knapsack/Scarb.toml | 7 ++ exercises/practice/knapsack/src/lib.cairo | 9 ++ .../practice/knapsack/tests/knapsack.cairo | 112 ++++++++++++++++++ 9 files changed, 285 insertions(+) create mode 100644 exercises/practice/knapsack/.docs/instructions.md create mode 100644 exercises/practice/knapsack/.docs/introduction.md create mode 100644 exercises/practice/knapsack/.meta/config.json create mode 100644 exercises/practice/knapsack/.meta/example.cairo create mode 100644 exercises/practice/knapsack/.meta/tests.toml create mode 100644 exercises/practice/knapsack/Scarb.toml create mode 100644 exercises/practice/knapsack/src/lib.cairo create mode 100644 exercises/practice/knapsack/tests/knapsack.cairo diff --git a/config.json b/config.json index 1516f84d..2229d4a4 100644 --- a/config.json +++ b/config.json @@ -819,6 +819,14 @@ "practices": [], "prerequisites": [], "difficulty": 4 + }, + { + "slug": "knapsack", + "name": "Knapsack", + "uuid": "ba9a3b81-aa52-4311-8e98-79d589b5091a", + "practices": [], + "prerequisites": [], + "difficulty": 4 } ], "foregone": [ diff --git a/exercises/practice/knapsack/.docs/instructions.md b/exercises/practice/knapsack/.docs/instructions.md new file mode 100644 index 00000000..3411db98 --- /dev/null +++ b/exercises/practice/knapsack/.docs/instructions.md @@ -0,0 +1,25 @@ +# Instructions + +Your task is to determine which items to take so that the total value of his selection is maximized, taking into account the knapsack's carrying capacity. + +Items will be represented as a list of items. +Each item will have a weight and value. +All values given will be strictly positive. +Bob can take only one of each item. + +For example: + +```text +Items: [ + { "weight": 5, "value": 10 }, + { "weight": 4, "value": 40 }, + { "weight": 6, "value": 30 }, + { "weight": 4, "value": 50 } +] + +Knapsack Maximum Weight: 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/.docs/introduction.md b/exercises/practice/knapsack/.docs/introduction.md new file mode 100644 index 00000000..9b2bed8b --- /dev/null +++ b/exercises/practice/knapsack/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +Bob is a thief. +After months of careful planning, he finally manages to crack the security systems of a fancy store. + +In front of him are many items, each with a value and weight. +Bob would gladly take all of the items, but his knapsack can only hold so much weight. +Bob has to carefully consider which items to take so that the total value of his selection is maximized. diff --git a/exercises/practice/knapsack/.meta/config.json b/exercises/practice/knapsack/.meta/config.json new file mode 100644 index 00000000..08ceb623 --- /dev/null +++ b/exercises/practice/knapsack/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "0xNeshi" + ], + "files": { + "solution": [ + "src/lib.cairo" + ], + "test": [ + "tests/knapsack.cairo" + ], + "example": [ + ".meta/example.cairo" + ], + "invalidator": [ + "Scarb.toml" + ] + }, + "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.cairo b/exercises/practice/knapsack/.meta/example.cairo new file mode 100644 index 00000000..b90d33e9 --- /dev/null +++ b/exercises/practice/knapsack/.meta/example.cairo @@ -0,0 +1,58 @@ +use core::dict::Felt252Dict; + +#[derive(Drop)] +pub struct Item { + pub weight: u32, + pub value: u32, +} + +/// Matrix implemented as a 1D array. +#[derive(Destruct)] +struct Matrix { + // Use Felt252Dict to simulate a modifiable array. + dict: Felt252Dict, + num_of_cols: usize, +} + +#[generate_trait] +impl MatrixImpl of MatrixTrait { + fn new(num_of_cols: usize) -> Matrix { + Matrix { dict: Default::default(), num_of_cols } + } + + fn at(ref self: Matrix, row: usize, col: usize) -> u32 { + self.dict.get((row * self.num_of_cols + col).into()) + } + + fn set(ref self: Matrix, row: usize, col: usize, value: u32) { + self.dict.insert((row * self.num_of_cols + col).into(), value); + } +} + +pub fn maximum_value(maximum_weight: u32, items: Span) -> u32 { + let mut max_values = MatrixTrait::new(num_of_cols: maximum_weight + 1); + + for row in 1 + ..items.len() + + 1 { + let item_index = row - 1; + let item_weight = *items[item_index].weight; + let item_value = *items[item_index].value; + + for w in 0 + ..maximum_weight + + 1 { + if item_weight <= w { + let max_val = core::cmp::max( + max_values.at(item_index, w), + max_values.at(item_index, w - item_weight) + item_value, + ); + max_values.set(row, w, max_val); + } else { + max_values.set(row, w, max_values.at(item_index, w)); + } + } + }; + + max_values.at(items.len(), maximum_weight) +} 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/Scarb.toml b/exercises/practice/knapsack/Scarb.toml new file mode 100644 index 00000000..16914b50 --- /dev/null +++ b/exercises/practice/knapsack/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "knapsack" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.8.2" diff --git a/exercises/practice/knapsack/src/lib.cairo b/exercises/practice/knapsack/src/lib.cairo new file mode 100644 index 00000000..4472fa8a --- /dev/null +++ b/exercises/practice/knapsack/src/lib.cairo @@ -0,0 +1,9 @@ +#[derive(Drop)] +pub struct Item { + pub weight: u32, + pub value: u32, +} + +pub fn maximum_value(maximum_weight: u32, items: Span) -> u32 { + panic!("implement `maximum_value`") +} diff --git a/exercises/practice/knapsack/tests/knapsack.cairo b/exercises/practice/knapsack/tests/knapsack.cairo new file mode 100644 index 00000000..78c2aab3 --- /dev/null +++ b/exercises/practice/knapsack/tests/knapsack.cairo @@ -0,0 +1,112 @@ +use knapsack::{maximum_value, Item}; + +#[test] +fn no_items() { + let max_weight = 100; + let items = array![]; + let output = maximum_value(max_weight, items.span()); + let expected = 0; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +fn one_item_too_heavy() { + let max_weight = 10; + let items = array![Item { weight: 100, value: 1, }]; + let output = maximum_value(max_weight, items.span()); + let expected = 0; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +fn five_items_cannot_be_greedy_by_weight() { + let max_weight = 10; + let items = array![ + 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, }, + ]; + let output = maximum_value(max_weight, items.span()); + let expected = 21; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +fn five_items_cannot_be_greedy_by_value() { + let max_weight = 10; + let items = array![ + 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, }, + ]; + let output = maximum_value(max_weight, items.span()); + let expected = 80; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +fn example_knapsack() { + let max_weight = 10; + let items = array![ + Item { weight: 5, value: 10, }, + Item { weight: 4, value: 40, }, + Item { weight: 6, value: 30, }, + Item { weight: 4, value: 50, }, + ]; + let output = maximum_value(max_weight, items.span()); + let expected = 90; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +fn test_8_items() { + let max_weight = 104; + let items = array![ + 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, }, + ]; + let output = maximum_value(max_weight, items.span()); + let expected = 900; + assert_eq!(output, expected); +} + +#[test] +#[ignore] +fn test_15_items() { + let max_weight = 750; + let items = array![ + 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, }, + ]; + let output = maximum_value(max_weight, items.span()); + let expected = 1458; + assert_eq!(output, expected); +}