Skip to content
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

Refactor Rod Cutting Implementation #774

Merged
merged 5 commits into from
Aug 6, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 50 additions & 40 deletions src/dynamic_programming/rod_cutting.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,65 @@
//! Solves the rod-cutting problem
//! This module provides functions for solving the rod-cutting problem using dynamic programming.
use std::cmp::max;

/// `rod_cut(p)` returns the maximum possible profit if a rod of length `n` = `p.len()`
/// is cut into up to `n` pieces, where the profit gained from each piece of length
/// `l` is determined by `p[l - 1]` and the total profit is the sum of the profit
/// gained from each piece.
/// Calculates the maximum possible profit from cutting a rod into pieces of varying lengths.
///
/// # Arguments
/// - `p` - profit for rods of length 1 to n inclusive
/// Returns the maximum profit achievable by cutting a rod into pieces such that the profit from each
/// piece is determined by its length and predefined prices.
///
/// # Complexity
/// - time complexity: O(n^2),
/// - space complexity: O(n^2),
/// - Time complexity: `O(n^2)`
/// - Space complexity: `O(n)`
///
/// where n is the length of `p`.
pub fn rod_cut(p: &[usize]) -> usize {
let n = p.len();
// f is the dynamic programming table
let mut f = vec![0; n];

for i in 0..n {
let mut max_price = p[i];
for j in 1..=i {
max_price = max(max_price, p[j - 1] + f[i - j]);
}
f[i] = max_price;
/// where `n` is the number of different rod lengths considered.
pub fn rod_cut(prices: &[usize]) -> usize {
if prices.is_empty() {
return 0;
}

// accomodate for input with length zero
if n != 0 {
f[n - 1]
} else {
0
}
(1..=prices.len()).fold(vec![0; prices.len() + 1], |mut max_profit, rod_length| {
max_profit[rod_length] = (1..=rod_length)
.map(|cut_position| prices[cut_position - 1] + max_profit[rod_length - cut_position])
.fold(prices[rod_length - 1], |max_price, current_price| {
max(max_price, current_price)
});
max_profit
})[prices.len()]
}

#[cfg(test)]
mod tests {
use super::rod_cut;
use super::*;

macro_rules! rod_cut_tests {
($($name:ident: $test_case:expr,)*) => {
$(
#[test]
fn $name() {
let (input, expected_output) = $test_case;
assert_eq!(expected_output, rod_cut(input));
}
)*
};
}

#[test]
fn test_rod_cut() {
assert_eq!(0, rod_cut(&[]));
assert_eq!(15, rod_cut(&[5, 8, 2]));
assert_eq!(10, rod_cut(&[1, 5, 8, 9]));
assert_eq!(25, rod_cut(&[5, 8, 2, 1, 7]));
assert_eq!(87, rod_cut(&[0, 0, 0, 0, 0, 87]));
assert_eq!(49, rod_cut(&[7, 6, 5, 4, 3, 2, 1]));
assert_eq!(22, rod_cut(&[1, 5, 8, 9, 10, 17, 17, 20]));
assert_eq!(60, rod_cut(&[6, 4, 8, 2, 5, 8, 2, 3, 7, 11]));
assert_eq!(30, rod_cut(&[1, 5, 8, 9, 10, 17, 17, 20, 24, 30]));
assert_eq!(12, rod_cut(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]));
rod_cut_tests! {
test_empty_prices: (&[], 0),
test_example_with_three_prices: (&[5, 8, 2], 15),
test_example_with_four_prices: (&[1, 5, 8, 9], 10),
test_example_with_five_prices: (&[5, 8, 2, 1, 7], 25),
test_all_zeros_except_last: (&[0, 0, 0, 0, 0, 87], 87),
test_descending_prices: (&[7, 6, 5, 4, 3, 2, 1], 49),
test_varied_prices: (&[1, 5, 8, 9, 10, 17, 17, 20], 22),
test_complex_prices: (&[6, 4, 8, 2, 5, 8, 2, 3, 7, 11], 60),
test_increasing_prices: (&[1, 5, 8, 9, 10, 17, 17, 20, 24, 30], 30),
test_large_range_prices: (&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 12),
test_single_length_price: (&[5], 5),
test_zero_length_price: (&[0], 0),
test_repeated_prices: (&[5, 5, 5, 5], 20),
test_no_profit: (&[0, 0, 0, 0], 0),
test_large_input: (&[1; 1000], 1000),
test_all_zero_input: (&[0; 100], 0),
test_very_large_prices: (&[1000000, 2000000, 3000000], 3000000),
test_greedy_does_not_work: (&[2, 5, 7, 8], 10),
}
}