RosettaCodeData/Task/Knapsack-problem-0-1/Rust/knapsack-problem-0-1.rust

135 lines
5.5 KiB
Plaintext

#![feature(iter_arith)]
use std::cmp::max;
use std::vec::Vec;
// This struct is used to store our items that we want in our knap-sack.
#[derive(Clone, Debug)]
struct Item<'a> {
name: &'a str,
weight: usize,
value: usize
}
// This is a bottom-up dynamic programming solution to the 0-1 knap-sack problem.
// maximize value
// subject to weights <= max_weight
fn knapsack01_dyn<'a>(items: &[Item<'a>], max_weight: usize) -> Vec<Item<'a>> {
// Imagine we wrote a recursive function(item, max_weight) that returns a
// usize corresponding to the maximum cumulative value by considering a
// subset of items such that the combined weight <= max_weight.
//
// fn best_value(item: usize, max_weight: usize) -> usize {
// if item == 0 {
// return 0;
// }
// if items[item - 1].weight > max_weight {
// return best_value(item - 1, max_weight);
// }
// return max(best_value(item - 1, max_weight),
// best_value(item - 1, max_weight - items[item - 1].weight)
// + items[item - 1].value);
// }
// }
//
// best_value(n_items, max_weight) is equal to the maximum value that
// we can add to the bag.
//
// The problem with using this function is that it performs redundant
// calculations.
//
// The dynamic programming solution is to precompute all of the values
// we need and put them into a 2D array.
//
// In a similar vein, the top-down solution would be to memoize the
// function then compute the results on demand.
let mut best_value = vec![vec![0usize; max_weight + 1]; items.len() + 1];
// Loop over the items.
for (i, it) in items.iter().enumerate() {
// Loop over the weights.
for w in 1 .. max_weight + 1 {
best_value[i + 1][w] =
// do we have room in our knapsack?
if it.weight > w {
// if we don't, then we'll say that the value doesn't change
// when considering this item
best_value[i][w].clone()
} else {
// If we do, then we have to see if the value we gain by adding
// the item, given the weight, is better than not adding the item.
max(best_value[i][w].clone(), best_value[i][w - it.weight] + it.value)
}
}
}
// A possibly over-allocated dynamically sized vector to push results to.
let mut result = Vec::with_capacity(items.len());
// Variable representing the weight left in the bag
let mut left_weight = max_weight.clone();
// We built up the solution space through a forward pass over the data,
// now we have to traverse backwards to get the solution.
for (i, it) in items.iter().enumerate().rev() {
// We can check if an item should be added to the knap-sack by
// comparing best_value with and without this item. If best_value
// added this item then so should we.
if best_value[i + 1][left_weight] != best_value[i][left_weight] {
result.push(it.clone());
// We remove the weight of the object from the remaining weight
// we can add to the bag.
left_weight -= it.weight;
}
}
result
}
fn main () {
const MAX_WEIGHT: usize = 400;
// Static immutable allocation of our items.
static ITEMS: &'static [Item<'static>] = &[
// Too much repetition of field names here!
Item { name: "map", weight: 9, value: 150 },
Item { name: "compass", weight: 13, value: 35 },
Item { name: "water", weight: 153, value: 200 },
Item { name: "sandwich", weight: 50, value: 160 },
Item { name: "glucose", weight: 15, value: 60 },
Item { name: "tin", weight: 68, value: 45 },
Item { name: "banana", weight: 27, value: 60 },
Item { name: "apple", weight: 39, value: 40 },
Item { name: "cheese", weight: 23, value: 30 },
Item { name: "beer", weight: 52, value: 10 },
Item { name: "suntancream", weight: 11, value: 70 },
Item { name: "camera", weight: 32, value: 30 },
Item { name: "T-shirt", weight: 24, value: 15 },
Item { name: "trousers", weight: 48, value: 10 },
Item { name: "umbrella", weight: 73, value: 40 },
Item { name: "waterproof trousers", weight: 42, value: 70 },
Item { name: "waterproof overclothes", weight: 43, value: 75 },
Item { name: "note-case", weight: 22, value: 80 },
Item { name: "sunglasses", weight: 7, value: 20 },
Item { name: "towel", weight: 18, value: 12 },
Item { name: "socks", weight: 4, value: 50 },
Item { name: "book", weight: 30, value: 10 }
];
let items = knapsack01_dyn(ITEMS, MAX_WEIGHT);
// We reverse the order because we solved the problem backward.
for it in items.iter().rev() {
println!("{:?}", it);
}
let tot_weight: usize = items.iter().map(|w| w.weight).sum();
println!("Total weight: {}", tot_weight);
let tot_value: usize = items.iter().map(|w| w.value).sum();
println!("Total value: {}", tot_value);
}