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

Refator Egg Dropping Implementation #776

Merged
merged 3 commits into from
Aug 19, 2024
Merged
Changes from 2 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
147 changes: 70 additions & 77 deletions src/dynamic_programming/egg_dropping.rs
Original file line number Diff line number Diff line change
@@ -1,91 +1,84 @@
/// # Egg Dropping Puzzle
//! This module contains the `egg_drop` function, which determines the minimum number of egg droppings
//! required to find the highest floor from which an egg can be dropped without breaking. It also includes
//! tests for the function using various test cases, including edge cases.

/// `egg_drop(eggs, floors)` returns the least number of egg droppings
/// required to determine the highest floor from which an egg will not
/// break upon dropping
/// Returns the least number of egg droppings required to determine the highest floor from which an egg will not break upon dropping.
///
/// Assumptions: n > 0
pub fn egg_drop(eggs: u32, floors: u32) -> u32 {
assert!(eggs > 0);

// Explicity handle edge cases (optional)
if eggs == 1 || floors == 0 || floors == 1 {
return floors;
}

let eggs_index = eggs as usize;
let floors_index = floors as usize;

// Store solutions to subproblems in 2D Vec,
// where egg_drops[i][j] represents the solution to the egg dropping
// problem with i eggs and j floors
let mut egg_drops: Vec<Vec<u32>> = vec![vec![0; floors_index + 1]; eggs_index + 1];

// Assign solutions for egg_drop(n, 0) = 0, egg_drop(n, 1) = 1
for egg_drop in egg_drops.iter_mut().skip(1) {
egg_drop[0] = 0;
egg_drop[1] = 1;
}

// Assign solutions to egg_drop(1, k) = k
for j in 1..=floors_index {
egg_drops[1][j] = j as u32;
/// # Arguments
///
/// * `eggs` - The number of eggs available.
/// * `floors` - The number of floors in the building.
///
/// # Returns
///
/// * `Some(usize)` - The minimum number of drops required if the number of eggs is greater than 0.
/// * `None` - If the number of eggs 0.
vil02 marked this conversation as resolved.
Show resolved Hide resolved
pub fn egg_drop(eggs: usize, floors: usize) -> Option<usize> {
if eggs == 0 {
return None;
}

// Complete solutions vector using optimal substructure property
for i in 2..=eggs_index {
for j in 2..=floors_index {
egg_drops[i][j] = u32::MAX;

for k in 1..=j {
let res = 1 + std::cmp::max(egg_drops[i - 1][k - 1], egg_drops[i][j - k]);

if res < egg_drops[i][j] {
egg_drops[i][j] = res;
}
}
}
if eggs == 1 || floors == 0 || floors == 1 {
return Some(floors);
}

egg_drops[eggs_index][floors_index]
// Create a 2D vector to store solutions to subproblems
let mut egg_drops: Vec<Vec<usize>> = vec![vec![0; floors + 1]; eggs + 1];

// Base cases: 0 floors -> 0 drops, 1 floor -> 1 drop
(1..=eggs).for_each(|i| {
egg_drops[i][1] = 1;
});

// Base case: 1 egg -> k drops for k floors
(1..=floors).for_each(|j| {
egg_drops[1][j] = j;
});

// Fill the table using the optimal substructure property
(2..=eggs).for_each(|i| {
(2..=floors).for_each(|j| {
egg_drops[i][j] = (1..=j)
.map(|k| 1 + std::cmp::max(egg_drops[i - 1][k - 1], egg_drops[i][j - k]))
.min()
.unwrap();
});
});

Some(egg_drops[eggs][floors])
}

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

#[test]
fn zero_floors() {
assert_eq!(egg_drop(5, 0), 0);
}

#[test]
fn one_egg() {
assert_eq!(egg_drop(1, 8), 8);
}

#[test]
fn eggs2_floors2() {
assert_eq!(egg_drop(2, 2), 2);
}

#[test]
fn eggs3_floors5() {
assert_eq!(egg_drop(3, 5), 3);
}

#[test]
fn eggs2_floors10() {
assert_eq!(egg_drop(2, 10), 4);
}

#[test]
fn eggs2_floors36() {
assert_eq!(egg_drop(2, 36), 8);
use super::*;

macro_rules! egg_drop_tests {
($($name:ident: $test_cases:expr,)*) => {
$(
#[test]
fn $name() {
let (eggs, floors, expected) = $test_cases;
assert_eq!(egg_drop(eggs, floors), expected);
}
)*
}
}

#[test]
fn large_floors() {
assert_eq!(egg_drop(2, 100), 14);
egg_drop_tests! {
test_no_floors: (5, 0, Some(0)),
test_one_egg_multiple_floors: (1, 8, Some(8)),
test_multiple_eggs_one_floor: (5, 1, Some(1)),
test_two_eggs_two_floors: (2, 2, Some(2)),
test_three_eggs_five_floors: (3, 5, Some(3)),
test_two_eggs_ten_floors: (2, 10, Some(4)),
test_two_eggs_thirty_six_floors: (2, 36, Some(8)),
test_many_eggs_one_floor: (100, 1, Some(1)),
test_many_eggs_few_floors: (100, 5, Some(3)),
test_few_eggs_many_floors: (2, 1000, Some(45)),
test_zero_eggs: (0, 10, None::<usize>),
test_no_eggs_no_floors: (0, 0, None::<usize>),
test_one_egg_no_floors: (1, 0, Some(0)),
test_one_egg_one_floor: (1, 1, Some(1)),
test_maximum_floors_one_egg: (1, usize::MAX, Some(usize::MAX)),
}
}