Skip to content

Commit

Permalink
Merge branch 'master' into optimize-btree-search
Browse files Browse the repository at this point in the history
  • Loading branch information
vil02 authored Aug 8, 2024
2 parents 9e96a3f + 65ca19d commit a8ef47f
Show file tree
Hide file tree
Showing 16 changed files with 582 additions and 290 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ unwrap_in_result = { level = "allow", priority = 1 }
unwrap_used = { level = "allow", priority = 1 }
use_debug = { level = "allow", priority = 1 }
wildcard_enum_match_arm = { level = "allow", priority = 1 }
renamed_function_params = { level = "allow", priority = 1 }
# nursery-lints:
branches_sharing_code = { level = "allow", priority = 1 }
cognitive_complexity = { level = "allow", priority = 1 }
Expand All @@ -160,5 +161,9 @@ redundant_clone = { level = "allow", priority = 1 }
suboptimal_flops = { level = "allow", priority = 1 }
suspicious_operation_groupings = { level = "allow", priority = 1 }
use_self = { level = "allow", priority = 1 }
while_float = { level = "allow", priority = 1 }
needless_pass_by_ref_mut = { level = "allow", priority = 1 }
# cargo-lints:
cargo_common_metadata = { level = "allow", priority = 1 }
# style-lints:
doc_lazy_continuation = { level = "allow", priority = 1 }
4 changes: 3 additions & 1 deletion DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* [Parentheses Generator](https://github.com/TheAlgorithms/Rust/blob/master/src/backtracking/parentheses_generator.rs)
* [Permutations](https://github.com/TheAlgorithms/Rust/blob/master/src/backtracking/permutations.rs)
* [Rat In Maze](https://github.com/TheAlgorithms/Rust/blob/master/src/backtracking/rat_in_maze.rs)
* [Subset Sum](https://github.com/TheAlgorithms/Rust/blob/master/src/backtracking/subset_sum.rs)
* [Sudoku](https://github.com/TheAlgorithms/Rust/blob/master/src/backtracking/sudoku.rs)
* Big Integer
* [Fast Factorial](https://github.com/TheAlgorithms/Rust/blob/master/src/big_integer/fast_factorial.rs)
Expand Down Expand Up @@ -62,7 +63,6 @@
* [Graph](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/graph.rs)
* [Hash Table](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/hash_table.rs)
* [Heap](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/heap.rs)
* [Infix To Postfix](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/infix_to_postfix.rs)
* [Lazy Segment Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/lazy_segment_tree.rs)
* [Linked List](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/linked_list.rs)
* [Postfix Evaluation](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/postfix_evaluation.rs)
Expand Down Expand Up @@ -155,6 +155,7 @@
* [K Means](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/k_means.rs)
* [Linear Regression](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/linear_regression.rs)
* Loss Function
* [Average Margin Ranking Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/average_margin_ranking_loss.rs)
* [Hinge Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/hinge_loss.rs)
* [Huber Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/huber_loss.rs)
* [Kl Divergence Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/machine_learning/loss_function/kl_divergence_loss.rs)
Expand Down Expand Up @@ -201,6 +202,7 @@
* [Geometric Series](https://github.com/TheAlgorithms/Rust/blob/master/src/math/geometric_series.rs)
* [Greatest Common Divisor](https://github.com/TheAlgorithms/Rust/blob/master/src/math/greatest_common_divisor.rs)
* [Huber Loss](https://github.com/TheAlgorithms/Rust/blob/master/src/math/huber_loss.rs)
* [Infix To Postfix](https://github.com/TheAlgorithms/Rust/blob/master/src/math/infix_to_postfix.rs)
* [Interest](https://github.com/TheAlgorithms/Rust/blob/master/src/math/interest.rs)
* [Interpolation](https://github.com/TheAlgorithms/Rust/blob/master/src/math/interpolation.rs)
* [Interquartile Range](https://github.com/TheAlgorithms/Rust/blob/master/src/math/interquartile_range.rs)
Expand Down
2 changes: 2 additions & 0 deletions src/backtracking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod n_queens;
mod parentheses_generator;
mod permutations;
mod rat_in_maze;
mod subset_sum;
mod sudoku;

pub use all_combination_of_size_k::generate_all_combinations;
Expand All @@ -16,4 +17,5 @@ pub use n_queens::n_queens_solver;
pub use parentheses_generator::generate_parentheses;
pub use permutations::permute;
pub use rat_in_maze::find_path_in_maze;
pub use subset_sum::has_subset_with_sum;
pub use sudoku::sudoku_solver;
55 changes: 55 additions & 0 deletions src/backtracking/subset_sum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//! This module provides functionality to check if there exists a subset of a given set of integers
//! that sums to a target value. The implementation uses a recursive backtracking approach.

/// Checks if there exists a subset of the given set that sums to the target value.
pub fn has_subset_with_sum(set: &[isize], target: isize) -> bool {
backtrack(set, set.len(), target)
}

fn backtrack(set: &[isize], remaining_items: usize, target: isize) -> bool {
// Found a subset with the required sum
if target == 0 {
return true;
}
// No more elements to process
if remaining_items == 0 {
return false;
}
// Check if we can find a subset including or excluding the last element
backtrack(set, remaining_items - 1, target)
|| backtrack(set, remaining_items - 1, target - set[remaining_items - 1])
}

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

macro_rules! has_subset_with_sum_tests {
($($name:ident: $test_case:expr,)*) => {
$(
#[test]
fn $name() {
let (set, target, expected) = $test_case;
assert_eq!(has_subset_with_sum(set, target), expected);
}
)*
}
}

has_subset_with_sum_tests! {
test_small_set_with_sum: (&[3, 34, 4, 12, 5, 2], 9, true),
test_small_set_without_sum: (&[3, 34, 4, 12, 5, 2], 30, false),
test_consecutive_set_with_sum: (&[1, 2, 3, 4, 5, 6], 10, true),
test_consecutive_set_without_sum: (&[1, 2, 3, 4, 5, 6], 22, false),
test_large_set_with_sum: (&[5, 10, 12, 13, 15, 18, -1, 10, 50, -2, 3, 4], 30, true),
test_empty_set: (&[], 0, true),
test_empty_set_with_nonzero_sum: (&[], 10, false),
test_single_element_equal_to_sum: (&[10], 10, true),
test_single_element_not_equal_to_sum: (&[5], 10, false),
test_negative_set_with_sum: (&[-7, -3, -2, 5, 8], 0, true),
test_negative_sum: (&[1, 2, 3, 4, 5], -1, false),
test_negative_sum_with_negatives: (&[-7, -3, -2, 5, 8], -4, true),
test_negative_sum_with_negatives_no_solution: (&[-7, -3, -2, 5, 8], -14, false),
test_even_inputs_odd_target: (&[2, 4, 6, 2, 8, -2, 10, 12, -24, 8, 12, 18], 3, false),
}
}
16 changes: 7 additions & 9 deletions src/big_integer/fast_factorial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,19 @@ pub fn fast_factorial(n: usize) -> BigUint {
// get list of primes that will be factors of n!
let primes = sieve_of_eratosthenes(n);

let mut p_indeces = BTreeMap::new();

// Map the primes with their index
primes.into_iter().for_each(|p| {
p_indeces.insert(p, index(p, n));
});
let p_indices = primes
.into_iter()
.map(|p| (p, index(p, n)))
.collect::<BTreeMap<_, _>>();

let max_bits = p_indeces.get(&2).unwrap().next_power_of_two().ilog2() + 1;
let max_bits = p_indices.get(&2).unwrap().next_power_of_two().ilog2() + 1;

// Create a Vec of 1's
let mut a = Vec::with_capacity(max_bits as usize);
a.resize(max_bits as usize, BigUint::one());
let mut a = vec![BigUint::one(); max_bits as usize];

// For every prime p, multiply a[i] by p if the ith bit of p's index is 1
for (p, i) in p_indeces {
for (p, i) in p_indices {
let mut bit = 1usize;
while bit.ilog2() < max_bits {
if (bit & i) > 0 {
Expand Down
72 changes: 0 additions & 72 deletions src/data_structures/infix_to_postfix.rs

This file was deleted.

2 changes: 0 additions & 2 deletions src/data_structures/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ mod floyds_algorithm;
pub mod graph;
mod hash_table;
mod heap;
mod infix_to_postfix;
mod lazy_segment_tree;
mod linked_list;
mod postfix_evaluation;
Expand All @@ -31,7 +30,6 @@ pub use self::graph::DirectedGraph;
pub use self::graph::UndirectedGraph;
pub use self::hash_table::HashTable;
pub use self::heap::Heap;
pub use self::infix_to_postfix::infix_to_postfix;
pub use self::lazy_segment_tree::LazySegmentTree;
pub use self::linked_list::LinkedList;
pub use self::postfix_evaluation::evaluate_postfix;
Expand Down
132 changes: 78 additions & 54 deletions src/dynamic_programming/coin_change.rs
Original file line number Diff line number Diff line change
@@ -1,70 +1,94 @@
/// Coin change via Dynamic Programming
//! This module provides a solution to the coin change problem using dynamic programming.
//! The `coin_change` function calculates the fewest number of coins required to make up
//! a given amount using a specified set of coin denominations.
//!
//! The implementation leverages dynamic programming to build up solutions for smaller
//! amounts and combines them to solve for larger amounts. It ensures optimal substructure
//! and overlapping subproblems are efficiently utilized to achieve the solution.

/// coin_change(coins, amount) returns the fewest number of coins that need to make up that amount.
/// If that amount of money cannot be made up by any combination of the coins, return `None`.
//! # Complexity
//! - Time complexity: O(amount * coins.length)
//! - Space complexity: O(amount)

/// Returns the fewest number of coins needed to make up the given amount using the provided coin denominations.
/// If the amount cannot be made up by any combination of the coins, returns `None`.
///
/// # Arguments
/// * `coins` - A slice of coin denominations.
/// * `amount` - The total amount of money to be made up.
///
/// # Returns
/// * `Option<usize>` - The minimum number of coins required to make up the amount, or `None` if it's not possible.
///
/// # Arguments:
/// * `coins` - coins of different denominations
/// * `amount` - a total amount of money be made up.
/// # Complexity
/// - time complexity: O(amount * coins.length),
/// - space complexity: O(amount),
/// * Time complexity: O(amount * coins.length)
/// * Space complexity: O(amount)
pub fn coin_change(coins: &[usize], amount: usize) -> Option<usize> {
let mut dp = vec![None; amount + 1];
dp[0] = Some(0);
let mut min_coins = vec![None; amount + 1];
min_coins[0] = Some(0);

// Assume dp[i] is the fewest number of coins making up amount i,
// then for every coin in coins, dp[i] = min(dp[i - coin] + 1).
for i in 0..=amount {
for &coin in coins {
if i >= coin {
dp[i] = match dp[i - coin] {
Some(prev_coins) => match dp[i] {
Some(curr_coins) => Some(curr_coins.min(prev_coins + 1)),
None => Some(prev_coins + 1),
},
None => dp[i],
};
}
}
}
(0..=amount).for_each(|curr_amount| {
coins
.iter()
.filter(|&&coin| curr_amount >= coin)
.for_each(|&coin| {
if let Some(prev_min_coins) = min_coins[curr_amount - coin] {
min_coins[curr_amount] = Some(
min_coins[curr_amount].map_or(prev_min_coins + 1, |curr_min_coins| {
curr_min_coins.min(prev_min_coins + 1)
}),
);
}
});
});

dp[amount]
min_coins[amount]
}

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

#[test]
fn basic() {
// 11 = 5 * 2 + 1 * 1
let coins = vec![1, 2, 5];
assert_eq!(Some(3), coin_change(&coins, 11));

// 119 = 11 * 10 + 7 * 1 + 2 * 1
let coins = vec![2, 3, 5, 7, 11];
assert_eq!(Some(12), coin_change(&coins, 119));
}

#[test]
fn coins_empty() {
let coins = vec![];
assert_eq!(None, coin_change(&coins, 1));
}

#[test]
fn amount_zero() {
let coins = vec![1, 2, 3];
assert_eq!(Some(0), coin_change(&coins, 0));
macro_rules! coin_change_tests {
($($name:ident: $test_case:expr,)*) => {
$(
#[test]
fn $name() {
let (coins, amount, expected) = $test_case;
assert_eq!(expected, coin_change(&coins, amount));
}
)*
}
}

#[test]
fn fail_change() {
// 3 can't be change by 2.
let coins = vec![2];
assert_eq!(None, coin_change(&coins, 3));
let coins = vec![10, 20, 50, 100];
assert_eq!(None, coin_change(&coins, 5));
coin_change_tests! {
test_basic_case: (vec![1, 2, 5], 11, Some(3)),
test_multiple_denominations: (vec![2, 3, 5, 7, 11], 119, Some(12)),
test_empty_coins: (vec![], 1, None),
test_zero_amount: (vec![1, 2, 3], 0, Some(0)),
test_no_solution_small_coin: (vec![2], 3, None),
test_no_solution_large_coin: (vec![10, 20, 50, 100], 5, None),
test_single_coin_large_amount: (vec![1], 100, Some(100)),
test_large_amount_multiple_coins: (vec![1, 2, 5], 10000, Some(2000)),
test_no_combination_possible: (vec![3, 7], 5, None),
test_exact_combination: (vec![1, 3, 4], 6, Some(2)),
test_large_denomination_multiple_coins: (vec![10, 50, 100], 1000, Some(10)),
test_small_amount_not_possible: (vec![5, 10], 1, None),
test_non_divisible_amount: (vec![2], 3, None),
test_all_multiples: (vec![1, 2, 4, 8], 15, Some(4)),
test_large_amount_mixed_coins: (vec![1, 5, 10, 25], 999, Some(45)),
test_prime_coins_and_amount: (vec![2, 3, 5, 7], 17, Some(3)),
test_coins_larger_than_amount: (vec![5, 10, 20], 1, None),
test_repeating_denominations: (vec![1, 1, 1, 5], 8, Some(4)),
test_non_standard_denominations: (vec![1, 4, 6, 9], 15, Some(2)),
test_very_large_denominations: (vec![1000, 2000, 5000], 1, None),
test_large_amount_performance: (vec![1, 5, 10, 25, 50, 100, 200, 500], 9999, Some(29)),
test_powers_of_two: (vec![1, 2, 4, 8, 16, 32, 64], 127, Some(7)),
test_fibonacci_sequence: (vec![1, 2, 3, 5, 8, 13, 21, 34], 55, Some(2)),
test_mixed_small_large: (vec![1, 100, 1000, 10000], 11001, Some(3)),
test_impossible_combinations: (vec![2, 4, 6, 8], 7, None),
test_greedy_approach_does_not_work: (vec![1, 12, 20], 24, Some(2)),
test_zero_denominations_no_solution: (vec![0], 1, None),
test_zero_denominations_solution: (vec![0], 0, Some(0)),
}
}
Loading

0 comments on commit a8ef47f

Please sign in to comment.