From 6aee127071b9be4fed4cec6951991f775cfd2f4c Mon Sep 17 00:00:00 2001 From: timeshifter Date: Tue, 11 Mar 2025 18:36:38 +0100 Subject: [PATCH] add 95, 17 --- lib/primes/benches/benchmark.rs | 12 ++- lib/proper_divisors/Cargo.toml | 10 ++ lib/proper_divisors/src/lib.rs | 82 +++++++++++++++ src/euler17/Cargo.toml | 9 ++ src/euler17/src/main.rs | 110 ++++++++++++++++++++ src/euler95/Cargo.toml | 7 +- src/euler95/src/main.rs | 178 ++++++++++++++++++-------------- 7 files changed, 330 insertions(+), 78 deletions(-) create mode 100644 lib/proper_divisors/Cargo.toml create mode 100644 lib/proper_divisors/src/lib.rs create mode 100644 src/euler17/Cargo.toml create mode 100644 src/euler17/src/main.rs diff --git a/lib/primes/benches/benchmark.rs b/lib/primes/benches/benchmark.rs index 626be48..355bd5a 100644 --- a/lib/primes/benches/benchmark.rs +++ b/lib/primes/benches/benchmark.rs @@ -10,5 +10,15 @@ fn bench_primes_between_10e5_and_10e6(c: &mut Criterion) { }); } -criterion_group!(benches, bench_primes_between_10e5_and_10e6); +fn bench_primes_between_10_and_10e6(c: &mut Criterion) { + c.bench_function("primes 10 to 10e6", |b| { + b.iter(|| Primes::get_between(10, 1_000_000)) + }); +} + +criterion_group!( + benches, + bench_primes_between_10_and_10e6, + bench_primes_between_10e5_and_10e6 +); criterion_main!(benches); diff --git a/lib/proper_divisors/Cargo.toml b/lib/proper_divisors/Cargo.toml new file mode 100644 index 0000000..918300b --- /dev/null +++ b/lib/proper_divisors/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "proper_divisors" +version = "0.1.0" +edition = "2024" + +[dependencies] +#num = "0.4.3" + +[dev-dependencies] +rstest = "0.25.0" diff --git a/lib/proper_divisors/src/lib.rs b/lib/proper_divisors/src/lib.rs new file mode 100644 index 0000000..ee4655f --- /dev/null +++ b/lib/proper_divisors/src/lib.rs @@ -0,0 +1,82 @@ +use std::collections::HashMap; + +#[derive(Debug)] +pub struct ProperDivisors { + map: HashMap>, + pub min: usize, + pub max: usize, +} + +impl ProperDivisors { + pub fn get_between(smaller: usize, larger: usize) -> Self { + let mut result = HashMap::new(); + + for number in smaller..=larger { + let mut divisors = vec![1]; + + let limit = usize::isqrt(number); + + for i in (2..=limit).rev() { + if number % i == 0 { + let div_1 = i; + let div_2 = number / i; + + divisors.push(div_1); + + if div_1 != div_2 { + divisors.push(div_2); + } + } + } + + divisors.sort_unstable(); + result.insert(number, divisors); + } + + Self { + map: result, + min: smaller, + max: larger, + } + } + + pub fn get(&self, number: usize) -> &[usize] { + self.map.get(&number).unwrap() + } +} + +#[cfg(test)] +mod tests { + + use rstest::rstest; + + use crate::ProperDivisors; + + #[rstest] + #[case(6, vec![1, 2, 3])] + #[case(28, vec![1, 2, 4, 7, 14])] + #[case(12_496, vec![1, 2, 4, 8, 11, 16, 22, 44, 71, 88, 142, 176, 284, 568, 781, 1136, 1562, 3124, 6248])] + fn one_proper_divisor(#[case] input: usize, #[case] expected: Vec) { + let pd = ProperDivisors::get_between(input, input + 1); + + assert_eq!(pd.get(input), expected); + } + + #[test] + fn all_proper_divisors() { + let pd = ProperDivisors::get_between(6, 30); + + assert_eq!(pd.get(6), &[1, 2, 3]); + assert_eq!(pd.get(28), &[1, 2, 4, 7, 14]); + + println!("{:?}", pd.get(8)); + + let pd = ProperDivisors::get_between(12_496, 12_497); + assert_eq!( + pd.get(12_496), + &[ + 1, 2, 4, 8, 11, 16, 22, 44, 71, 88, 142, 176, 284, 568, 781, 1136, 1562, 3124, 6248 + ] + ); + } +} diff --git a/src/euler17/Cargo.toml b/src/euler17/Cargo.toml new file mode 100644 index 0000000..befaaf4 --- /dev/null +++ b/src/euler17/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "euler17" +version = "0.1.0" +edition = "2024" + +[dependencies] + +[dev-dependencies] +rstest = "0.25.0" diff --git a/src/euler17/src/main.rs b/src/euler17/src/main.rs new file mode 100644 index 0000000..b0931e1 --- /dev/null +++ b/src/euler17/src/main.rs @@ -0,0 +1,110 @@ +fn main() { + let result: usize = (1..=1000).map(convert_number).sum(); + println!("{result}"); +} + +fn convert_number(number: usize) -> usize { + if number == 1_000 { + return 11; + }; + + let mut sum = 0; + + if has_hundreds_digits(number) { + sum += convert_hundreds(number); + } + + if has_hundreds_digits(number) && has_tens_or_single_digits(number) { + sum += 3; // "and" + } + + if has_tens_or_single_digits(number) { + sum += convert_tens_and_single_digits(number); + } + + sum +} + +fn convert_tens_and_single_digits(number: usize) -> usize { + let number = get_tens_and_single_digits(number); + let mut result = match number { + x if x < 10 => convert_digit_to_letters(x), + 10 => 3, + 11 => 6, + 12 => 6, + 13 => 8, + 14 => 8, + 15 => 7, + 16 => 7, + 17 => 9, + 18 => 8, + 19 => 8, + x if x < 40 => 6, // twenty, thirty are all of length 6 + x if x < 70 => 5, // forty, fifty, sixty + x if x < 80 => 7, // seventy + x if x < 100 => 6, // eighty, ninety + _ => panic!(), + }; + + if number > 20 { + result += convert_digit_to_letters(number % 10); + } + + result +} + +fn get_tens_and_single_digits(number: usize) -> usize { + number % 100 +} + +fn has_tens_or_single_digits(number: usize) -> bool { + get_tens_and_single_digits(number) > 0 +} + +fn convert_hundreds(number: usize) -> usize { + let hundreds = get_hundreds(number); + let letters = convert_digit_to_letters(hundreds); + letters + 7 +} + +fn convert_digit_to_letters(number: usize) -> usize { + match number { + 0 => 0, + 1 => 3, + 2 => 3, + 3 => 5, + 4 => 4, + 5 => 4, + 6 => 3, + 7 => 5, + 8 => 5, + 9 => 4, + _ => unreachable!("should not happen if code is correct"), + } +} + +fn has_hundreds_digits(number: usize) -> bool { + get_hundreds(number) > 0 +} + +fn get_hundreds(number: usize) -> usize { + number / 100 +} + +#[cfg(test)] +mod tests { + + use rstest::rstest; + + #[rstest] + #[case(1, 5, 19)] + #[case(115, 115, 20)] + #[case(342, 342, 23)] + #[case(1000, 1000, 11)] + fn chain(#[case] min: usize, #[case] max: usize, #[case] expected: usize) { + use crate::convert_number; + + let sum: usize = (min..=max).map(convert_number).sum(); + assert_eq!(sum, expected); + } +} diff --git a/src/euler95/Cargo.toml b/src/euler95/Cargo.toml index 269262f..68222a9 100644 --- a/src/euler95/Cargo.toml +++ b/src/euler95/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "euler95" version = "0.1.0" -edition = "2021" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,4 +10,7 @@ rstest = "0.17" [profile.release] debug = true -lto = "fat" +lto = "off" + +[dependencies] +proper_divisors = { version = "*", path = "../../lib/proper_divisors" } diff --git a/src/euler95/src/main.rs b/src/euler95/src/main.rs index 6decb83..7ec32d3 100644 --- a/src/euler95/src/main.rs +++ b/src/euler95/src/main.rs @@ -1,118 +1,146 @@ -use std::collections::HashSet; +use proper_divisors::ProperDivisors; +use std::collections::HashMap; +const MIN: usize = 2; const MAX: usize = 1_000_000; fn main() { - let mut chains = vec![]; + let proper_divisors_map = ProperDivisors::get_between(MIN, MAX); + let mut number_divisor_sum_map = NumberDivisorSumMap::build(proper_divisors_map); - let mut part_of_any_chain = HashSet::new(); + let mut longest_chain = vec![]; - for current in 1..MAX { - if part_of_any_chain.contains(¤t) { + for number in MIN..MAX { + if *number_divisor_sum_map.get(&number).unwrap_or(&1) == 1 { + // if equals 1, then it's prime and useless + // if it's `None`, it was already used continue; } - try_build_chain_from_number(MAX, current, &mut chains, &mut part_of_any_chain); + if let Some(chain) = try_build_chain_from_number(number, &mut number_divisor_sum_map) { + dbg!(&chain, &longest_chain); + if chain.len() > longest_chain.len() { + longest_chain = chain; + } + } } - chains.sort_by_key(|chain| chain.len()); + dbg!(&longest_chain); - let longest = chains.last().unwrap(); - - let smallest = longest.iter().min().unwrap().to_owned(); + let smallest = longest_chain.iter().min().unwrap().to_owned(); println!("{smallest}"); } -fn build_proper_divisors(max: usize) -> Vec> { - todo!() +#[derive(Debug)] +struct NumberDivisorSumMap { + map: HashMap, + max: usize, +} + +impl NumberDivisorSumMap { + fn build(pd: ProperDivisors) -> Self { + let mut number_divisor_sum_map = HashMap::new(); + + for number in pd.min..=pd.max { + let sum: usize = pd.get(number).iter().sum(); + number_divisor_sum_map.insert(number, sum); + } + Self { + map: number_divisor_sum_map, + max: pd.max, + } + } + + fn get(&self, key: &usize) -> Option<&usize> { + self.map.get(key) + } + + fn remove(&mut self, key: &usize) { + self.map.remove(key); + } + + fn remove_all(&mut self, keys: &[usize]) { + for key in keys { + self.remove(key); + } + } } fn try_build_chain_from_number( - max: usize, - mut current: usize, - chains: &mut Vec>, - part_of_any_chain: &mut HashSet, -) { + mut number: usize, + number_divisor_sum_map: &mut NumberDivisorSumMap, +) -> Option> { let mut maybe_chain = vec![]; + let mut debug = false; + if number == 1064 { + debug = true; + } + loop { - maybe_chain.push(current); + if debug { + dbg!(number); + dbg!(&maybe_chain); + } + maybe_chain.push(number); - let next: usize = get_proper_divisors(current).into_iter().sum(); + let next: usize = match number_divisor_sum_map.get(&number) { + Some(number) => *number, + None => break None, + }; - if next > max || next == 0 { - break; + if next > number_divisor_sum_map.max || next == 1 || next == number { + number_divisor_sum_map.remove_all(&maybe_chain); + break None; } - if maybe_chain.contains(&next) { - if maybe_chain.len() > 1 && *maybe_chain.first().unwrap() == next { - for number in &maybe_chain { - part_of_any_chain.insert(*number); - } - println!("{}", maybe_chain.first().unwrap()); - chains.push(maybe_chain); - } - break; + if maybe_chain.contains(&next) && maybe_chain.len() > 1 + //&& *maybe_chain.first().unwrap() == next + { + println!("{}", maybe_chain.first().unwrap()); + + number_divisor_sum_map.remove_all(&maybe_chain); + break Some(maybe_chain); } - current = next; + number = next; } } -fn get_proper_divisors(x: usize) -> Vec { - let mut result = vec![1]; - - let limit = x / 2 + 1; - - for i in 2..limit { - if result.contains(&i) { - break; - } - - if x % i == 0 { - let div_1 = i; - let div_2 = x / i; - result.push(div_1); - - if div_2 != div_1 { - result.push(div_2); - } - } - } - - result.sort_unstable(); - result -} - #[cfg(test)] mod tests { use rstest::rstest; - #[rstest] - #[case(6, vec![1, 2, 3])] - #[case(28, vec![1, 2, 4, 7, 14])] - #[case(12_496, vec![1, 2, 4, 8, 11, 16, 22, 44, 71, 88, 142, 176, 284, 568, 781, 1136, 1562, 3124, 6248])] - fn proper_divisor(#[case] input: usize, #[case] expected: Vec) { - use crate::get_proper_divisors; + use crate::{NumberDivisorSumMap, try_build_chain_from_number}; + use proper_divisors::ProperDivisors; - assert_eq!(get_proper_divisors(input), expected); + #[rstest] + #[case(220, vec![220, 284])] + #[case(12_496, vec![12_496, 14_288, 15_472, 14_536, 14_264])] + fn chain(#[case] input: usize, #[case] expected: Vec) { + let larger = expected.iter().max().unwrap().to_owned() + 1; + + let pd = ProperDivisors::get_between(2, larger); + let mut number_divisor_sum_map = NumberDivisorSumMap::build(pd); + + let chain = try_build_chain_from_number(input, &mut number_divisor_sum_map).unwrap(); + + assert_eq!(chain, expected); } - #[rstest] - #[case(220, vec![vec![220, 284]])] - #[case(12_496, vec![vec![12_496, 14_288, 15_472, 14_536, 14_264]])] - fn chain(#[case] input: usize, #[case] expected: Vec>) { - use std::collections::HashSet; + #[test] + fn all_chains() { + let smaller = 2; + let larger = 16_000; - use crate::try_build_chain_from_number; + let pd = ProperDivisors::get_between(smaller, larger); + let mut number_divisor_sum_map = NumberDivisorSumMap::build(pd); - let max = expected.iter().flatten().max().unwrap().to_owned(); - let mut part_of_any_chain = HashSet::new(); + let chain = try_build_chain_from_number(220, &mut number_divisor_sum_map).unwrap(); + assert_eq!(chain, vec![220, 284]); - let mut chains = vec![]; - try_build_chain_from_number(max, input, &mut chains, &mut part_of_any_chain); - - assert_eq!(chains, expected); + let chain = try_build_chain_from_number(12_496, &mut number_divisor_sum_map).unwrap(); + assert_eq!(chain, vec![12_496, 14_288, 15_472, 14_536, 14_264]); } }