generated from mschoi/template
Add new problem modules and utility functions for combinatorics
- Introduced `prob527` module for a new problem implementation in the Project Euler series. - Enhanced the `combinatoric` module with a new `CombinationTree` struct for efficient combination calculations. - Added utility functions for calculating expected values in binary and random search scenarios. - Implemented unit tests for the new functionalities to ensure correctness and reliability. - Updated the `mod.rs` files to include the new problem module and utility functions.
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
pub mod prob17;
|
||||
pub mod prob49;
|
||||
pub mod prob497;
|
||||
pub mod prob527;
|
||||
pub mod prob61;
|
||||
pub mod prob650;
|
||||
pub mod prob66;
|
||||
pub mod prob684;
|
||||
pub mod prob700;
|
||||
pub mod prob719;
|
||||
pub mod prob808;
|
||||
pub mod prob816;
|
||||
pub mod prob845;
|
||||
pub mod prob885;
|
||||
pub mod prob932;
|
||||
pub mod prob934;
|
||||
|
||||
116
src/project_euler/prob527.rs
Normal file
116
src/project_euler/prob527.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn binary_search(n: usize, memory: &mut HashMap<usize, f64>) -> f64 {
|
||||
if let Some(&value) = memory.get(&n) {
|
||||
return value;
|
||||
}
|
||||
|
||||
match n % 2 {
|
||||
0 => {
|
||||
let k = n / 2;
|
||||
let result = ((k as f64) * binary_search(k, memory)
|
||||
+ (k as f64 - 1.0) * binary_search(k - 1, memory))
|
||||
/ (n as f64)
|
||||
+ 1.0;
|
||||
memory.insert(n, result);
|
||||
result
|
||||
}
|
||||
1 => {
|
||||
let k = n / 2;
|
||||
let result = 2.0 * (k as f64) * binary_search(k, memory) / n as f64 + 1.0;
|
||||
memory.insert(n, result);
|
||||
result
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expected_try_binary_search(n: usize) -> f64 {
|
||||
let mut memory = HashMap::from([(1, 1.0), (2, 1.5)]);
|
||||
binary_search(n, &mut memory)
|
||||
}
|
||||
|
||||
pub fn expected_try_random_search(n: usize) -> f64 {
|
||||
let mut memory = vec![0.0; n + 1];
|
||||
memory[1] = 1.0;
|
||||
|
||||
let mut sum = 1.0;
|
||||
for i in 2..=n {
|
||||
let temp = 1.0 + 2.0 / (i * i) as f64 * sum;
|
||||
memory[i] = temp;
|
||||
sum += i as f64 * temp;
|
||||
}
|
||||
memory[n]
|
||||
}
|
||||
|
||||
pub fn expected_try_random_search_2(n: usize) -> f64 {
|
||||
2.0 * (n as f64 + 1.0) / (n as f64) * (1..=n).map(|i| 1.0 / (i as f64)).sum::<f64>() - 3.0
|
||||
}
|
||||
|
||||
pub fn expected_try_random_search_3(n: usize) -> f64 {
|
||||
const EULER_MASCHERONI: f64 = 0.57721566490153;
|
||||
2.0 * (n as f64 + 1.0) / (n as f64) * (EULER_MASCHERONI + (n as f64).ln()) - 3.0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use approx::assert_abs_diff_eq;
|
||||
|
||||
#[test]
|
||||
fn test_expected_try_binary_search() {
|
||||
assert_abs_diff_eq!(expected_try_binary_search(1), 1.0);
|
||||
assert_abs_diff_eq!(expected_try_binary_search(2), 1.5);
|
||||
assert_abs_diff_eq!(expected_try_binary_search(3), 1.6666, epsilon = 1e-4);
|
||||
assert_abs_diff_eq!(expected_try_binary_search(6), 2.3333, epsilon = 1e-4);
|
||||
|
||||
for i in 4..=10 {
|
||||
let n = 10i64.pow(i);
|
||||
let expected = (n as f64).log2() - 1.0;
|
||||
assert_abs_diff_eq!(
|
||||
expected_try_binary_search(n as usize),
|
||||
expected,
|
||||
epsilon = 1e-1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expected_try_random_search() {
|
||||
assert_abs_diff_eq!(expected_try_random_search(1), 1.0);
|
||||
assert_abs_diff_eq!(expected_try_random_search(2), 1.5);
|
||||
assert_abs_diff_eq!(expected_try_random_search(6), 2.71666667, epsilon = 1e-4);
|
||||
|
||||
for i in 1..=6 {
|
||||
let n = 10i64.pow(i);
|
||||
assert_abs_diff_eq!(
|
||||
expected_try_random_search(n as usize),
|
||||
expected_try_random_search_2(n as usize),
|
||||
epsilon = 1e-8
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_ratio() {
|
||||
let n = 10i64.pow(10);
|
||||
// let expected = (n as f64).log2() - 1.0;
|
||||
// assert_abs_diff_eq!(
|
||||
// expected_try_binary_search(n as usize),
|
||||
// expected,
|
||||
// epsilon = 1e-8
|
||||
// );
|
||||
|
||||
// assert_abs_diff_eq!(
|
||||
// expected_try_random_search_2(n as usize),
|
||||
// expected_try_random_search_3(n as usize),
|
||||
// epsilon = 1e-8
|
||||
// );
|
||||
|
||||
assert_abs_diff_eq!(
|
||||
expected_try_random_search_3(n as usize) - expected_try_binary_search(n as usize),
|
||||
11.92412011,
|
||||
epsilon = 1e-8
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,33 @@ pub fn combination(n: usize, k: usize) -> usize {
|
||||
factorial(n) / (factorial(k) * factorial(n - k))
|
||||
}
|
||||
|
||||
pub struct CombinationTree {
|
||||
max: usize,
|
||||
tree: Vec<Vec<usize>>,
|
||||
}
|
||||
|
||||
impl CombinationTree {
|
||||
pub fn new(max: usize) -> Self {
|
||||
let mut tree = vec![vec![1]];
|
||||
for i in 1..=max {
|
||||
let mut row = vec![1; i + 1];
|
||||
for j in 1..i {
|
||||
row[j] = tree[i - 1][j - 1] + tree[i - 1][j];
|
||||
}
|
||||
tree.push(row);
|
||||
}
|
||||
Self { max, tree }
|
||||
}
|
||||
|
||||
pub fn get(&self, n: usize, k: usize) -> Option<usize> {
|
||||
if n > self.max || k > n {
|
||||
None
|
||||
} else {
|
||||
Some(self.tree[n][k])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn combination_with_repetition(n: usize, k: usize) -> usize {
|
||||
combination(n + k - 1, k)
|
||||
}
|
||||
@@ -133,6 +160,70 @@ pub fn permuted_composition_with_min_max(n: usize, minmax: usize) -> HashSet<Vec
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_combination_tree() {
|
||||
let result = CombinationTree::new(5);
|
||||
assert_eq!(
|
||||
result.tree,
|
||||
vec![
|
||||
vec![1],
|
||||
vec![1, 1],
|
||||
vec![1, 2, 1],
|
||||
vec![1, 3, 3, 1],
|
||||
vec![1, 4, 6, 4, 1],
|
||||
vec![1, 5, 10, 10, 5, 1]
|
||||
]
|
||||
);
|
||||
|
||||
// Additional test case for n = 20, test full record
|
||||
let result_20 = CombinationTree::new(20);
|
||||
let expected_20 = vec![
|
||||
vec![1],
|
||||
vec![1, 1],
|
||||
vec![1, 2, 1],
|
||||
vec![1, 3, 3, 1],
|
||||
vec![1, 4, 6, 4, 1],
|
||||
vec![1, 5, 10, 10, 5, 1],
|
||||
vec![1, 6, 15, 20, 15, 6, 1],
|
||||
vec![1, 7, 21, 35, 35, 21, 7, 1],
|
||||
vec![1, 8, 28, 56, 70, 56, 28, 8, 1],
|
||||
vec![1, 9, 36, 84, 126, 126, 84, 36, 9, 1],
|
||||
vec![1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1],
|
||||
vec![1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1],
|
||||
vec![1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1],
|
||||
vec![
|
||||
1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13, 1,
|
||||
],
|
||||
vec![
|
||||
1, 14, 91, 364, 1001, 2002, 3003, 3432, 3003, 2002, 1001, 364, 91, 14, 1,
|
||||
],
|
||||
vec![
|
||||
1, 15, 105, 455, 1365, 3003, 5005, 6435, 6435, 5005, 3003, 1365, 455, 105, 15, 1,
|
||||
],
|
||||
vec![
|
||||
1, 16, 120, 560, 1820, 4368, 8008, 11440, 12870, 11440, 8008, 4368, 1820, 560, 120,
|
||||
16, 1,
|
||||
],
|
||||
vec![
|
||||
1, 17, 136, 680, 2380, 6188, 12376, 19448, 24310, 24310, 19448, 12376, 6188, 2380,
|
||||
680, 136, 17, 1,
|
||||
],
|
||||
vec![
|
||||
1, 18, 153, 816, 3060, 8568, 18564, 31824, 43758, 48620, 43758, 31824, 18564, 8568,
|
||||
3060, 816, 153, 18, 1,
|
||||
],
|
||||
vec![
|
||||
1, 19, 171, 969, 3876, 11628, 27132, 50388, 75582, 92378, 92378, 75582, 50388,
|
||||
27132, 11628, 3876, 969, 171, 19, 1,
|
||||
],
|
||||
vec![
|
||||
1, 20, 190, 1140, 4845, 15504, 38760, 77520, 125970, 167960, 184756, 167960,
|
||||
125970, 77520, 38760, 15504, 4845, 1140, 190, 20, 1,
|
||||
],
|
||||
];
|
||||
assert_eq!(result_20.tree, expected_20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_composition_of_length() {
|
||||
let result = composition_of_length(10, 2);
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
use num::integer::{Integer, Roots, gcd};
|
||||
use num::{
|
||||
FromPrimitive, PrimInt,
|
||||
integer::{Integer, Roots, gcd},
|
||||
};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
pub fn digit_sum<T>(n: T) -> T
|
||||
where
|
||||
T: PrimInt + FromPrimitive,
|
||||
{
|
||||
let mut sum = T::zero();
|
||||
let mut n = n;
|
||||
while n > T::zero() {
|
||||
sum = sum + n % T::from_usize(10).unwrap();
|
||||
n = n / T::from_usize(10).unwrap();
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
pub fn is_square<T>(n: T) -> bool
|
||||
where
|
||||
T: Roots + Copy,
|
||||
|
||||
@@ -37,6 +37,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn modulo_pow<T>(base: T, exp: T, modulo: T) -> T
|
||||
where
|
||||
T: PrimInt + Euclid + ToBigInt,
|
||||
{
|
||||
let base_big = base.to_bigint().unwrap();
|
||||
let exp_big = exp.to_bigint().unwrap();
|
||||
let modulo_big = modulo.to_bigint().unwrap();
|
||||
let result_big = base_big.modpow(&exp_big, &modulo_big);
|
||||
T::from(result_big.to_u32_digits().1[0]).unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -72,4 +83,12 @@ mod tests {
|
||||
1_110_380_180
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_modulo_pow() {
|
||||
assert_eq!(modulo_pow::<u32>(2, 10, 1000), 24);
|
||||
assert_eq!(modulo_pow::<u32>(3, 4, 17), 13);
|
||||
assert_eq!(modulo_pow::<u32>(123456, 789, 1000000007), 182677862);
|
||||
assert_eq!(modulo_pow::<u32>(2, 31, 4294967291), 2147483648);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user