add 95, 17

This commit is contained in:
timeshifter
2025-03-11 18:36:38 +01:00
parent 159830eb3d
commit 6aee127071
7 changed files with 330 additions and 78 deletions
+11 -1
View File
@@ -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); criterion_main!(benches);
+10
View File
@@ -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"
+82
View File
@@ -0,0 +1,82 @@
use std::collections::HashMap;
#[derive(Debug)]
pub struct ProperDivisors {
map: HashMap<usize, Vec<usize>>,
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<usize>) {
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
]
);
}
}
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "euler17"
version = "0.1.0"
edition = "2024"
[dependencies]
[dev-dependencies]
rstest = "0.25.0"
+110
View File
@@ -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);
}
}
+5 -2
View File
@@ -1,7 +1,7 @@
[package] [package]
name = "euler95" name = "euler95"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # 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] [profile.release]
debug = true debug = true
lto = "fat" lto = "off"
[dependencies]
proper_divisors = { version = "*", path = "../../lib/proper_divisors" }
+103 -75
View File
@@ -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; const MAX: usize = 1_000_000;
fn main() { 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 { for number in MIN..MAX {
if part_of_any_chain.contains(&current) { 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; 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_chain.iter().min().unwrap().to_owned();
let smallest = longest.iter().min().unwrap().to_owned();
println!("{smallest}"); println!("{smallest}");
} }
fn build_proper_divisors(max: usize) -> Vec<Vec<usize>> { #[derive(Debug)]
todo!() struct NumberDivisorSumMap {
map: HashMap<usize, usize>,
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( fn try_build_chain_from_number(
max: usize, mut number: usize,
mut current: usize, number_divisor_sum_map: &mut NumberDivisorSumMap,
chains: &mut Vec<Vec<usize>>, ) -> Option<Vec<usize>> {
part_of_any_chain: &mut HashSet<usize>,
) {
let mut maybe_chain = vec![]; let mut maybe_chain = vec![];
let mut debug = false;
if number == 1064 {
debug = true;
}
loop { 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 { if next > number_divisor_sum_map.max || next == 1 || next == number {
break; number_divisor_sum_map.remove_all(&maybe_chain);
break None;
} }
if maybe_chain.contains(&next) { if maybe_chain.contains(&next) && maybe_chain.len() > 1
if maybe_chain.len() > 1 && *maybe_chain.first().unwrap() == next { //&& *maybe_chain.first().unwrap() == next
for number in &maybe_chain { {
part_of_any_chain.insert(*number); println!("{}", maybe_chain.first().unwrap());
}
println!("{}", maybe_chain.first().unwrap()); number_divisor_sum_map.remove_all(&maybe_chain);
chains.push(maybe_chain); break Some(maybe_chain);
}
break;
} }
current = next; number = next;
} }
} }
fn get_proper_divisors(x: usize) -> Vec<usize> {
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)] #[cfg(test)]
mod tests { mod tests {
use rstest::rstest; use rstest::rstest;
#[rstest] use crate::{NumberDivisorSumMap, try_build_chain_from_number};
#[case(6, vec![1, 2, 3])] use proper_divisors::ProperDivisors;
#[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<usize>) {
use crate::get_proper_divisors;
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<usize>) {
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] #[test]
#[case(220, vec![vec![220, 284]])] fn all_chains() {
#[case(12_496, vec![vec![12_496, 14_288, 15_472, 14_536, 14_264]])] let smaller = 2;
fn chain(#[case] input: usize, #[case] expected: Vec<Vec<usize>>) { let larger = 16_000;
use std::collections::HashSet;
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 chain = try_build_chain_from_number(220, &mut number_divisor_sum_map).unwrap();
let mut part_of_any_chain = HashSet::new(); assert_eq!(chain, vec![220, 284]);
let mut chains = vec![]; let chain = try_build_chain_from_number(12_496, &mut number_divisor_sum_map).unwrap();
try_build_chain_from_number(max, input, &mut chains, &mut part_of_any_chain); assert_eq!(chain, vec![12_496, 14_288, 15_472, 14_536, 14_264]);
assert_eq!(chains, expected);
} }
} }