-
-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add exercise knapsack #563
base: main
Are you sure you want to change the base?
Changes from 4 commits
9b4d1df
0c849bf
5621fc5
d4ad759
4c2fe85
529f48d
ccb1cf9
428e6a9
a60de78
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# 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 items. | ||
Each item will have a weight and value. | ||
|
||
For example: | ||
|
||
```none | ||
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. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"authors": [ | ||
"siebenschlaefer" | ||
], | ||
"files": { | ||
"solution": [ | ||
"knapsack.nim" | ||
], | ||
"test": [ | ||
"test_knapsack.nim" | ||
], | ||
"example": [ | ||
".meta/example.nim" | ||
] | ||
}, | ||
"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" | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,8 @@ | ||||||
type Item = tuple[weight: int, value: int] | ||||||
|
||||||
proc maximumValue*(maximumWeight: int, items: openArray[Item]): int = | ||||||
var dp = newSeq[int](maximumWeight + 1) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I don't think this is needed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great, happy to make the change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now the five jobs of the CI fail. I will roll this back. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Weird! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ynfle did you have the time to look at it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was a mistaken suggestion. The
siebenschlaefer marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
for item in items: | ||||||
for weight in countdown(maximumWeight, item.weight): | ||||||
dp[weight] = max(dp[weight], item.value + dp[weight - item.weight]) | ||||||
result = dp[maximumWeight] | ||||||
siebenschlaefer marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
type Item = tuple[weight: int, value: int] | ||
|
||
proc maximumValue*(maximumWeight: int, items: openArray[Item]): int = | ||
discard | ||
siebenschlaefer marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import unittest | ||
import knapsack | ||
|
||
suite "knapsack": | ||
test "no items": | ||
let maximumWeight = 100 | ||
let items: array[0, tuple[weight: int, value: int]] = [] | ||
let expected = 0 | ||
siebenschlaefer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
check maximumValue(maximumWeight, items) == expected | ||
|
||
test "one item, too heavy": | ||
let maximumWeight = 10 | ||
let items = [(weight: 100, value: 1)] | ||
let expected = 0 | ||
check maximumValue(maximumWeight, items) == expected | ||
|
||
test "five items (cannot be greedy by weight)": | ||
let maximumWeight = 10 | ||
let items = [ | ||
(weight: 2, value: 5), (weight: 2, value: 5), (weight: 2, value: 5), | ||
(weight: 2, value: 5), (weight: 10, value: 21) | ||
] | ||
let expected = 21 | ||
check maximumValue(maximumWeight, items) == expected | ||
|
||
test "five items (cannot be greedy by value)": | ||
let maximumWeight = 10 | ||
let items = [ | ||
(weight: 2, value: 20), (weight: 2, value: 20), (weight: 2, value: 20), | ||
(weight: 2, value: 20), (weight: 10, value: 50) | ||
] | ||
let expected = 80 | ||
check maximumValue(maximumWeight, items) == expected | ||
|
||
test "example knapsack": | ||
let maximumWeight = 10 | ||
let items = [ | ||
(weight: 5, value: 10), (weight: 4, value: 40), (weight: 6, value: 30), | ||
(weight: 4, value: 50) | ||
] | ||
let expected = 90 | ||
check maximumValue(maximumWeight, items) == expected | ||
|
||
test "8 items": | ||
let maximumWeight = 104 | ||
let items = [ | ||
(weight: 25, value: 350), (weight: 35, value: 400), (weight: 45, value: 450), | ||
(weight: 5, value: 20), (weight: 25, value: 70), (weight: 3, value: 8), | ||
(weight: 2, value: 5), (weight: 2, value: 5) | ||
] | ||
let expected = 900 | ||
check maximumValue(maximumWeight, items) == expected | ||
|
||
test "15 items": | ||
let maximumWeight = 750 | ||
let items = [ | ||
(weight: 70, value: 135), (weight: 73, value: 139), (weight: 77, value: 149), | ||
(weight: 80, value: 150), (weight: 82, value: 156), (weight: 87, value: 163), | ||
(weight: 90, value: 173), (weight: 94, value: 184), (weight: 98, value: 192), | ||
(weight: 106, value: 201), (weight: 110, value: 210), (weight: 113, value: 214), | ||
(weight: 115, value: 221), (weight: 118, value: 229), (weight: 120, value: 240) | ||
] | ||
siebenschlaefer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let expected = 1458 | ||
check maximumValue(maximumWeight, items) == expected |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should go with an
object
here, although it'll require some other changes.To reduce the verbosity this would otherwise produce in the test file, I think I'd suggest adding a
func
there that takes anopenArray[(int, int)]
and returns a seq ofItem
. Thoughts?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not against this change, but as an inexperienced Nim programmer I want to learn:
Why do you prefer an
object
over atuple
in this case?As for the verbosity: I don't think it's too bad currently but I'm fine with all the alternatives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For myself, the main reason to avoid tuples is because tuples are duck-typed. Consider the following example:
This would compile because a tuple just means a specific ordering of elements. The names of the field (or even if they have names) and the name of the type is not part of the type of the tuple.
The equivalent code for objects, however, would not compile because, even if they do have the same field names and types, because they are 2 different and separate definitions.
This can lead to confusion.
Consider a tuple representing an RGB color and a point in 3-D space. They are interchangeable with each other but don't represent equal values. An object, however, is not interchangeable as such and is therefore not subject to being mistakenly converted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, tuples can be problematic if they get constructed without field names or accessed with an index: With
(12, 34)
it's not immediately obvious which of the two is the weight and which the value, anditem[1]
doesn't tell the reader that it accesses the value.But otherwise, frankly, I'm not convinced: The two tuples in your example can only be assigned to each other because they have the same number of fields, with the same types and names.
If I have an RGB color
tuple[r, g, b: int]
and a 3D pointtuple[x, y, z: int]
they are not interchangeable because their fields have different names.And wouldn't that argument apply to all uses of tuples, anywhere?
But I'm OK with making them objects if you think that's better. The only downside would be that the tests would need the typename for each construction of an
Item
.Creating a helper function would allow us to shorten that but wouldn't we bring back the ambiguity in return?