From 800b4a858bfd8268221ae7ada085e7b8fd042339 Mon Sep 17 00:00:00 2001 From: keiravillekode Date: Thu, 5 Sep 2024 21:34:50 +1000 Subject: [PATCH] Add knapsack exercise (#415) --- config.json | 8 ++ .../practice/knapsack/.docs/instructions.md | 25 +++++ .../practice/knapsack/.docs/introduction.md | 8 ++ exercises/practice/knapsack/.meta/config.json | 19 ++++ exercises/practice/knapsack/.meta/example.zig | 32 ++++++ exercises/practice/knapsack/.meta/tests.toml | 36 +++++++ exercises/practice/knapsack/knapsack.zig | 19 ++++ exercises/practice/knapsack/test_knapsack.zig | 98 +++++++++++++++++++ 8 files changed, 245 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.zig create mode 100644 exercises/practice/knapsack/.meta/tests.toml create mode 100644 exercises/practice/knapsack/knapsack.zig create mode 100644 exercises/practice/knapsack/test_knapsack.zig diff --git a/config.json b/config.json index 9e901c1..858184f 100644 --- a/config.json +++ b/config.json @@ -136,6 +136,14 @@ ], "difficulty": 1 }, + { + "slug": "knapsack", + "name": "Knapsack", + "uuid": "ffa03fc8-0503-4524-ae40-9f3202349003", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "armstrong-numbers", "name": "Armstrong Numbers", diff --git a/exercises/practice/knapsack/.docs/instructions.md b/exercises/practice/knapsack/.docs/instructions.md new file mode 100644 index 0000000..3411db9 --- /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 0000000..9b2bed8 --- /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 0000000..0d45c33 --- /dev/null +++ b/exercises/practice/knapsack/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "knapsack.zig" + ], + "test": [ + "test_knapsack.zig" + ], + "example": [ + ".meta/example.zig" + ] + }, + "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.zig b/exercises/practice/knapsack/.meta/example.zig new file mode 100644 index 0000000..1d599a3 --- /dev/null +++ b/exercises/practice/knapsack/.meta/example.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const mem = std.mem; + +pub const Item = struct { + weight: usize, + value: usize, + + pub fn init(weight: usize, value: usize) Item { + return Item{ + .weight = weight, + .value = value, + }; + } +}; + +pub fn maximumValue(allocator: mem.Allocator, maximumWeight: usize, items: []const Item) !usize { + const table = try allocator.alloc(usize, maximumWeight + 1); + defer allocator.free(table); + @memset(table, 0); + for (items, 0..) |item, itemIndex| { + _ = itemIndex; + var index = maximumWeight + 1; + while (index > item.weight) { + index -= 1; + const value = item.value + table[index - item.weight]; + if (table[index] < value) { + table[index] = value; + } + } + } + return table[maximumWeight]; +} diff --git a/exercises/practice/knapsack/.meta/tests.toml b/exercises/practice/knapsack/.meta/tests.toml new file mode 100644 index 0000000..8e013ef --- /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.zig b/exercises/practice/knapsack/knapsack.zig new file mode 100644 index 0000000..bbc7008 --- /dev/null +++ b/exercises/practice/knapsack/knapsack.zig @@ -0,0 +1,19 @@ +const std = @import("std"); +const mem = std.mem; + +pub const Item = struct { + // This struct, as well as its fields and init method, needs to be implemented. + + pub fn init(weight: usize, value: usize) Item { + _ = weight; + _ = value; + @compileError("please implement the init method"); + } +}; + +pub fn maximumValue(allocator: mem.Allocator, maximumWeight: usize, items: []const Item) !usize { + _ = allocator; + _ = maximumWeight; + _ = items; + @compileError("please implement the maximumValue function"); +} diff --git a/exercises/practice/knapsack/test_knapsack.zig b/exercises/practice/knapsack/test_knapsack.zig new file mode 100644 index 0000000..e99c914 --- /dev/null +++ b/exercises/practice/knapsack/test_knapsack.zig @@ -0,0 +1,98 @@ +const std = @import("std"); +const testing = std.testing; + +const knapsack = @import("knapsack.zig"); +const Item = knapsack.Item; + +test "no items" { + const expected: usize = 0; + const items: [0]Item = .{}; + const actual = try knapsack.maximumValue(testing.allocator, 100, &items); + try testing.expectEqual(expected, actual); +} + +test "one item, too heavy" { + const expected: usize = 0; + const items: [1]Item = .{ + Item.init(100, 1), + }; + const actual = try knapsack.maximumValue(testing.allocator, 10, &items); + try testing.expectEqual(expected, actual); +} + +test "five items (cannot be greedy by weight)" { + const expected: usize = 21; + const items: [5]Item = .{ + Item.init(2, 5), + Item.init(2, 5), + Item.init(2, 5), + Item.init(2, 5), + Item.init(10, 21), + }; + const actual = try knapsack.maximumValue(testing.allocator, 10, &items); + try testing.expectEqual(expected, actual); +} + +test "five items (cannot be greedy by value)" { + const expected: usize = 80; + const items: [5]Item = .{ + Item.init(2, 20), + Item.init(2, 20), + Item.init(2, 20), + Item.init(2, 20), + Item.init(10, 50), + }; + const actual = try knapsack.maximumValue(testing.allocator, 10, &items); + try testing.expectEqual(expected, actual); +} + +test "example knapsack" { + const expected: usize = 90; + const items: [4]Item = .{ + Item.init(5, 10), + Item.init(4, 40), + Item.init(6, 30), + Item.init(4, 50), + }; + const actual = try knapsack.maximumValue(testing.allocator, 10, &items); + try testing.expectEqual(expected, actual); +} + +test "8 items" { + const expected: usize = 900; + const items: [8]Item = .{ + Item.init(25, 350), + Item.init(35, 400), + Item.init(45, 450), + Item.init(5, 20), + Item.init(25, 70), + Item.init(3, 8), + Item.init(2, 5), + Item.init(2, 5), + }; + const actual = try knapsack.maximumValue(testing.allocator, 104, &items); + try testing.expectEqual(expected, actual); +} + +test "15 items" { + const expected: usize = 1458; + const items: [15]Item = .{ + Item.init(70, 135), + Item.init(73, 139), + Item.init(77, 149), + Item.init(80, 150), + Item.init(82, 156), + Item.init(87, 163), + Item.init(90, 173), + Item.init(94, 184), + Item.init(98, 192), + Item.init(106, 201), + Item.init(110, 210), + Item.init(113, 214), + Item.init(115, 221), + Item.init(118, 229), + Item.init(120, 240), + }; + const actual = try knapsack.maximumValue(testing.allocator, 750, &items); + try testing.expectEqual(expected, actual); +}