diff --git a/src/project_euler/mod.rs b/src/project_euler/mod.rs index 2f4da85..94dd2e2 100644 --- a/src/project_euler/mod.rs +++ b/src/project_euler/mod.rs @@ -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; diff --git a/src/project_euler/prob527.rs b/src/project_euler/prob527.rs new file mode 100644 index 0000000..028b401 --- /dev/null +++ b/src/project_euler/prob527.rs @@ -0,0 +1,116 @@ +use std::collections::HashMap; + +fn binary_search(n: usize, memory: &mut HashMap) -> 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::() - 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 + ); + } +} diff --git a/src/utils/combinatoric.rs b/src/utils/combinatoric.rs index c9d8147..b11a826 100644 --- a/src/utils/combinatoric.rs +++ b/src/utils/combinatoric.rs @@ -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>, +} + +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 { + 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(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(n: T) -> bool where T: Roots + Copy, diff --git a/src/utils/modulo.rs b/src/utils/modulo.rs index 68f65f1..7a76dae 100644 --- a/src/utils/modulo.rs +++ b/src/utils/modulo.rs @@ -37,6 +37,17 @@ where } } +pub fn modulo_pow(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::(2, 10, 1000), 24); + assert_eq!(modulo_pow::(3, 4, 17), 13); + assert_eq!(modulo_pow::(123456, 789, 1000000007), 182677862); + assert_eq!(modulo_pow::(2, 31, 4294967291), 2147483648); + } }