|
| 1 | +//! Solves the knapsack problem |
| 2 | +use std::cmp::max; |
| 3 | + |
| 4 | +/// knapsack_table(w, weights, values) returns the knapsack table (`n`, `m`) with maximum values, where `n` is number of items |
| 5 | +/// |
| 6 | +/// Arguments: |
| 7 | +/// * `w` - knapsack capacity |
| 8 | +/// * `weights` - set of weights for each item |
| 9 | +/// * `values` - set of values for each item |
| 10 | +fn knapsack_table(w: &usize, weights: &[usize], values: &[usize]) -> Vec<Vec<usize>> { |
| 11 | + // Initialize `n` - number of items |
| 12 | + let n: usize = weights.len(); |
| 13 | + // Initialize `m` |
| 14 | + // m[i, w] - the maximum value that can be attained with weight less that or equal to `w` using items up to `i` |
| 15 | + let mut m: Vec<Vec<usize>> = vec![vec![0; w + 1]; n + 1]; |
| 16 | + |
| 17 | + for i in 0..=n { |
| 18 | + for j in 0..=*w { |
| 19 | + // m[i, j] compiled according to the following rule: |
| 20 | + if i == 0 || j == 0 { |
| 21 | + m[i][j] = 0; |
| 22 | + } else if weights[i - 1] <= j { |
| 23 | + // If `i` is in the knapsack |
| 24 | + // Then m[i, j] is equal to the maximum value of the knapsack, |
| 25 | + // where the weight `j` is reduced by the weight of the `i-th` item and the set of admissible items plus the value `k` |
| 26 | + m[i][j] = max(values[i - 1] + m[i - 1][j - weights[i - 1]], m[i - 1][j]); |
| 27 | + } else { |
| 28 | + // If the item `i` did not get into the knapsack |
| 29 | + // Then m[i, j] is equal to the maximum cost of a knapsack with the same capacity and a set of admissible items |
| 30 | + m[i][j] = m[i - 1][j] |
| 31 | + } |
| 32 | + } |
| 33 | + } |
| 34 | + m |
| 35 | +} |
| 36 | + |
| 37 | +/// knapsack_items(weights, m, i, j) returns the indices of the items of the optimal knapsack (from 1 to `n`) |
| 38 | +/// |
| 39 | +/// Arguments: |
| 40 | +/// * `weights` - set of weights for each item |
| 41 | +/// * `m` - knapsack table with maximum values |
| 42 | +/// * `i` - include items 1 through `i` in knapsack (for the initial value, use `n`) |
| 43 | +/// * `j` - maximum weight of the knapsack |
| 44 | +fn knapsack_items(weights: &[usize], m: &[Vec<usize>], i: usize, j: usize) -> Vec<usize> { |
| 45 | + if i == 0 { |
| 46 | + return vec![]; |
| 47 | + } |
| 48 | + if m[i][j] > m[i - 1][j] { |
| 49 | + let mut knap: Vec<usize> = knapsack_items(weights, m, i - 1, j - weights[i - 1]); |
| 50 | + knap.push(i); |
| 51 | + knap |
| 52 | + } else { |
| 53 | + knapsack_items(weights, m, i - 1, j) |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +/// knapsack(w, weights, values) returns the tuple where first value is `optimal profit`, |
| 58 | +/// second value is `knapsack optimal weight` and the last value is `indices of items`, that we got (from 1 to `n`) |
| 59 | +/// |
| 60 | +/// Arguments: |
| 61 | +/// * `w` - knapsack capacity |
| 62 | +/// * `weights` - set of weights for each item |
| 63 | +/// * `values` - set of values for each item |
| 64 | +/// |
| 65 | +/// Complexity |
| 66 | +/// - time complexity: O(nw), |
| 67 | +/// - space complexity: O(nw), |
| 68 | +/// |
| 69 | +/// where `n` and `w` are `number of items` and `knapsack capacity` |
| 70 | +pub fn knapsack(w: usize, weights: Vec<usize>, values: Vec<usize>) -> (usize, usize, Vec<usize>) { |
| 71 | + // Checks if the number of items in the list of weights is the same as the number of items in the list of values |
| 72 | + assert_eq!(weights.len(), values.len(), "Number of items in the list of weights doesn't match the number of items in the list of values!"); |
| 73 | + // Initialize `n` - number of items |
| 74 | + let n: usize = weights.len(); |
| 75 | + // Find the knapsack table |
| 76 | + let m: Vec<Vec<usize>> = knapsack_table(&w, &weights, &values); |
| 77 | + // Find the indices of the items |
| 78 | + let items: Vec<usize> = knapsack_items(&weights, &m, n, w); |
| 79 | + // Find the total weight of optimal knapsack |
| 80 | + let mut total_weight: usize = 0; |
| 81 | + for i in items.iter() { |
| 82 | + total_weight += weights[i - 1]; |
| 83 | + } |
| 84 | + // Return result |
| 85 | + (m[n][w], total_weight, items) |
| 86 | +} |
| 87 | + |
| 88 | +#[cfg(test)] |
| 89 | +mod tests { |
| 90 | + // Took test datasets from https://people.sc.fsu.edu/~jburkardt/datasets/bin_packing/bin_packing.html |
| 91 | + use super::*; |
| 92 | + |
| 93 | + #[test] |
| 94 | + fn test_p02() { |
| 95 | + assert_eq!( |
| 96 | + (51, 26, vec![2, 3, 4]), |
| 97 | + knapsack(26, vec![12, 7, 11, 8, 9], vec![24, 13, 23, 15, 16]) |
| 98 | + ); |
| 99 | + } |
| 100 | + |
| 101 | + #[test] |
| 102 | + fn test_p04() { |
| 103 | + assert_eq!( |
| 104 | + (150, 190, vec![1, 2, 5]), |
| 105 | + knapsack( |
| 106 | + 190, |
| 107 | + vec![56, 59, 80, 64, 75, 17], |
| 108 | + vec![50, 50, 64, 46, 50, 5] |
| 109 | + ) |
| 110 | + ); |
| 111 | + } |
| 112 | + |
| 113 | + #[test] |
| 114 | + fn test_p01() { |
| 115 | + assert_eq!( |
| 116 | + (309, 165, vec![1, 2, 3, 4, 6]), |
| 117 | + knapsack( |
| 118 | + 165, |
| 119 | + vec![23, 31, 29, 44, 53, 38, 63, 85, 89, 82], |
| 120 | + vec![92, 57, 49, 68, 60, 43, 67, 84, 87, 72] |
| 121 | + ) |
| 122 | + ); |
| 123 | + } |
| 124 | + |
| 125 | + #[test] |
| 126 | + fn test_p06() { |
| 127 | + assert_eq!( |
| 128 | + (1735, 169, vec![2, 4, 7]), |
| 129 | + knapsack( |
| 130 | + 170, |
| 131 | + vec![41, 50, 49, 59, 55, 57, 60], |
| 132 | + vec![442, 525, 511, 593, 546, 564, 617] |
| 133 | + ) |
| 134 | + ); |
| 135 | + } |
| 136 | + |
| 137 | + #[test] |
| 138 | + fn test_p07() { |
| 139 | + assert_eq!( |
| 140 | + (1458, 749, vec![1, 3, 5, 7, 8, 9, 14, 15]), |
| 141 | + knapsack( |
| 142 | + 750, |
| 143 | + vec![70, 73, 77, 80, 82, 87, 90, 94, 98, 106, 110, 113, 115, 118, 120], |
| 144 | + vec![135, 139, 149, 150, 156, 163, 173, 184, 192, 201, 210, 214, 221, 229, 240] |
| 145 | + ) |
| 146 | + ); |
| 147 | + } |
| 148 | +} |
0 commit comments