diff --git a/DIRECTORY.md b/DIRECTORY.md index a88d671731b..af06d0aad05 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -104,6 +104,7 @@ * [Egg Dropping](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/egg_dropping.rs) * [Fibonacci](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/fibonacci.rs) * [Fractional Knapsack](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/fractional_knapsack.rs) + * [Integer Partition](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/integer_partition.rs) * [Is Subsequence](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/is_subsequence.rs) * [Knapsack](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/knapsack.rs) * [Longest Common Subsequence](https://github.com/TheAlgorithms/Rust/blob/master/src/dynamic_programming/longest_common_subsequence.rs) diff --git a/src/dynamic_programming/integer_partition.rs b/src/dynamic_programming/integer_partition.rs new file mode 100644 index 00000000000..d8e9b2f6461 --- /dev/null +++ b/src/dynamic_programming/integer_partition.rs @@ -0,0 +1,117 @@ +//! Integer partition using dynamic programming +//! +//! The number of partitions of a number n into at least k parts equals the number of +//! partitions into exactly k parts plus the number of partitions into at least k-1 parts. +//! Subtracting 1 from each part of a partition of n into k parts gives a partition of n-k +//! into k parts. These two facts together are used for this algorithm. +//! +//! More info: +//! * +//! * + +#![allow(clippy::large_stack_arrays)] + +/// Calculates the number of partitions of a positive integer using dynamic programming. +/// +/// # Arguments +/// +/// * `m` - A positive integer to find the number of partitions for +/// +/// # Returns +/// +/// The number of partitions of `m` +/// +/// # Panics +/// +/// Panics if `m` is not a positive integer (0 or negative) +/// +/// # Examples +/// +/// ``` +/// # use the_algorithms_rust::dynamic_programming::partition; +/// assert_eq!(partition(5), 7); +/// assert_eq!(partition(7), 15); +/// assert_eq!(partition(100), 190569292); +/// ``` +#[allow(clippy::large_stack_arrays)] +pub fn partition(m: i32) -> u128 { + // Validate input + assert!(m > 0, "Input must be a positive integer greater than 0"); + + let m = m as usize; + + // Initialize memo table with zeros using iterative construction + // to avoid large stack allocations + let mut memo: Vec> = Vec::with_capacity(m + 1); + for _ in 0..=m { + memo.push(vec![0u128; m]); + } + + // Base case: there's one way to partition into 0 parts (empty partition) + for i in 0..=m { + memo[i][0] = 1; + } + + // Fill the memo table using dynamic programming + for n in 0..=m { + for k in 1..m { + // Add partitions from k-1 (partitions with at least k-1 parts) + memo[n][k] += memo[n][k - 1]; + + // Add partitions from n-k-1 with k parts (subtract 1 from each part) + if n > k { + memo[n][k] += memo[n - k - 1][k]; + } + } + } + + memo[m][m - 1] +} + +#[cfg(test)] +#[allow(clippy::large_stack_arrays)] +mod tests { + use super::*; + + #[test] + fn test_partition_5() { + assert_eq!(partition(5), 7); + } + + #[test] + fn test_partition_7() { + assert_eq!(partition(7), 15); + } + + #[test] + #[allow(clippy::large_stack_arrays)] + fn test_partition_100() { + assert_eq!(partition(100), 190569292); + } + + #[test] + #[allow(clippy::large_stack_arrays)] + fn test_partition_1000() { + assert_eq!(partition(1000), 24061467864032622473692149727991); + } + + #[test] + #[should_panic(expected = "Input must be a positive integer greater than 0")] + fn test_partition_negative() { + partition(-7); + } + + #[test] + #[should_panic(expected = "Input must be a positive integer greater than 0")] + fn test_partition_zero() { + partition(0); + } + + #[test] + fn test_partition_small_values() { + assert_eq!(partition(1), 1); + assert_eq!(partition(2), 2); + assert_eq!(partition(3), 3); + assert_eq!(partition(4), 5); + } +} diff --git a/src/dynamic_programming/mod.rs b/src/dynamic_programming/mod.rs index 7f5de2be42c..c96f0ff820d 100644 --- a/src/dynamic_programming/mod.rs +++ b/src/dynamic_programming/mod.rs @@ -3,6 +3,7 @@ mod coin_change; mod egg_dropping; mod fibonacci; mod fractional_knapsack; +mod integer_partition; mod is_subsequence; mod knapsack; mod longest_common_subsequence; @@ -27,16 +28,13 @@ mod word_break; pub use self::catalan_numbers::catalan_numbers; pub use self::coin_change::coin_change; pub use self::egg_dropping::egg_drop; -pub use self::fibonacci::binary_lifting_fibonacci; -pub use self::fibonacci::classical_fibonacci; -pub use self::fibonacci::fibonacci; -pub use self::fibonacci::last_digit_of_the_sum_of_nth_fibonacci_number; -pub use self::fibonacci::logarithmic_fibonacci; -pub use self::fibonacci::matrix_fibonacci; -pub use self::fibonacci::memoized_fibonacci; -pub use self::fibonacci::nth_fibonacci_number_modulo_m; -pub use self::fibonacci::recursive_fibonacci; +pub use self::fibonacci::{ + binary_lifting_fibonacci, classical_fibonacci, fibonacci, + last_digit_of_the_sum_of_nth_fibonacci_number, logarithmic_fibonacci, matrix_fibonacci, + memoized_fibonacci, nth_fibonacci_number_modulo_m, recursive_fibonacci, +}; pub use self::fractional_knapsack::fractional_knapsack; +pub use self::integer_partition::partition; pub use self::is_subsequence::is_subsequence; pub use self::knapsack::knapsack; pub use self::longest_common_subsequence::longest_common_subsequence;