From 4f9d64a987b8b170c9d290e10d0d81391e01e651 Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Wed, 28 Sep 2022 15:19:25 +0200 Subject: [PATCH 01/16] refactor 200 iterator --- src/euler200/src/main.rs | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/euler200/src/main.rs b/src/euler200/src/main.rs index 2797ffa..5db16fe 100644 --- a/src/euler200/src/main.rs +++ b/src/euler200/src/main.rs @@ -13,7 +13,7 @@ fn main() { squbes.iterate(); squbes.iterate(); squbes.iterate(); - println!("{:?}", squbes.output()); + println!("{:?}", squbes.get_output()); } #[derive(Debug)] @@ -43,9 +43,14 @@ impl Squbes { self.calculate_squbes_with_current_prime(); self.save_current_prime(); self.update_current_prime(); + self.save_new_squbes(); } - pub fn output(&self) -> Vec { + fn save_new_squbes(&mut self) { + self.squbes.extend(self.new_squbes.drain(0..)); + } + + pub fn get_output(&self) -> Vec { let mut result: Vec<_> = self.squbes.iter().copied().collect(); result.sort_unstable(); result @@ -60,18 +65,7 @@ impl Squbes { } fn calculate_squbes_with_current_prime(&mut self) { - assert_ne!(self.current_prime, 0); - let iter = self - .used_primes - .iter() - .copied() - .zip([self.current_prime].into_iter().cycle()) - .chain( - [self.current_prime] - .into_iter() - .cycle() - .zip(self.used_primes.iter().copied()), - ) + let iter = get_iterator_over_pairs_both_ways(&self.used_primes, self.current_prime) .map(|(p, q)| Self::core(p, q)); self.new_squbes.extend(iter); } @@ -80,3 +74,14 @@ impl Squbes { p.pow(2) * q.pow(3) } } + +fn get_iterator_over_pairs_both_ways( + vec: &[u64], + value: u64, +) -> impl Iterator + '_ { + iterate_vec_and_scalar(vec, value).flat_map(|(x, y)| [(x, y), (y, x)].into_iter()) +} + +fn iterate_vec_and_scalar(vec: &[u64], value: u64) -> impl Iterator + '_ { + vec.iter().copied().zip([value].into_iter().cycle()) +} From b305da92d0fa31a7b81ef52e5754f499a098b401 Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Wed, 28 Sep 2022 15:26:23 +0200 Subject: [PATCH 02/16] refactor 200 iterator --- lib/primes/src/lib.rs | 7 ++++++- src/euler200/src/main.rs | 27 ++++++++++++++------------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/primes/src/lib.rs b/lib/primes/src/lib.rs index dd5f505..040a978 100644 --- a/lib/primes/src/lib.rs +++ b/lib/primes/src/lib.rs @@ -6,12 +6,13 @@ use std::fs::File; use std::hash::Hash; use std::io::Write; use std::slice::Iter; +use std::vec::IntoIter; use num::FromPrimitive; use num::Num; use num::ToPrimitive; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Primes { vector: Vec, set: HashSet, @@ -110,6 +111,10 @@ where pub fn into_vec(self) -> Vec { self.vector } + + pub fn to_cloned_iter(&self) -> IntoIter { + self.vector.clone().into_iter() + } } #[cfg(test)] diff --git a/src/euler200/src/main.rs b/src/euler200/src/main.rs index 5db16fe..40f328b 100644 --- a/src/euler200/src/main.rs +++ b/src/euler200/src/main.rs @@ -19,35 +19,36 @@ fn main() { #[derive(Debug)] struct Squbes { squbes: HashSet, - new_squbes: Vec, - primes: IntoIter, + primes: Primes, + primes_iter: IntoIter, used_primes: Vec, current_prime: u64, } impl Squbes { pub fn new(max: u64) -> Self { - let mut primes = Primes::get_between(2, max).into_vec().into_iter(); - let first = primes.next().unwrap(); - let second = primes.next().unwrap(); + let primes = Primes::get_between(2, max); + let mut primes_iter = primes.to_cloned_iter(); + let first = primes_iter.next().unwrap(); + let second = primes_iter.next().unwrap(); Self { squbes: HashSet::new(), - new_squbes: vec![], primes, + primes_iter, used_primes: vec![first], current_prime: second, } } pub fn iterate(&mut self) { - self.calculate_squbes_with_current_prime(); + let squbes = self.calculate_squbes_with_current_prime(); self.save_current_prime(); self.update_current_prime(); self.save_new_squbes(); } fn save_new_squbes(&mut self) { - self.squbes.extend(self.new_squbes.drain(0..)); + todo!() } pub fn get_output(&self) -> Vec { @@ -61,13 +62,13 @@ impl Squbes { } fn update_current_prime(&mut self) { - self.current_prime = self.primes.next().unwrap(); + self.current_prime = self.primes_iter.next().unwrap(); } - fn calculate_squbes_with_current_prime(&mut self) { - let iter = get_iterator_over_pairs_both_ways(&self.used_primes, self.current_prime) - .map(|(p, q)| Self::core(p, q)); - self.new_squbes.extend(iter); + fn calculate_squbes_with_current_prime(&mut self) -> Vec { + get_iterator_over_pairs_both_ways(&self.used_primes, self.current_prime) + .map(|(p, q)| Self::core(p, q)) + .collect() } fn core(p: u64, q: u64) -> u64 { From 8756fa7dcc0163c0d29eeac281139fd13b3d9938 Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Wed, 2 Nov 2022 13:50:20 +0100 Subject: [PATCH 03/16] clean up dijkstra --- lib/dijkstra/src/lib.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/dijkstra/src/lib.rs b/lib/dijkstra/src/lib.rs index bc1eed1..8e87a8b 100644 --- a/lib/dijkstra/src/lib.rs +++ b/lib/dijkstra/src/lib.rs @@ -96,18 +96,18 @@ where while !(self.unvisited_without_distance.is_empty() && self.unvisited_with_distance.is_empty()) { - let coord = self.get_unvisited_coord_with_smallest_distance_from_start(); - let neighbours = self.get_neighbours(&coord); - let previous_distance = *self.matrix.get(coord).unwrap().get_distance().unwrap(); + let to_visit = self.get_unvisited_coord_with_smallest_distance_from_start(); + self.visit(to_visit); + let neighbours = self.get_neighbours(&to_visit); + let previous_distance = *self.matrix.get(to_visit).unwrap().get_distance().unwrap(); for neighbour in neighbours { - self.update_neighbour(neighbour, coord, previous_distance); + self.update_neighbour(neighbour, to_visit, previous_distance); } } } fn get_unvisited_coord_with_smallest_distance_from_start(&mut self) -> Coordinate { - let coord = self - .unvisited_with_distance + self.unvisited_with_distance .iter() .filter_map(|c| { let node = self.matrix.get(*c).unwrap(); @@ -115,9 +115,11 @@ where }) .reduce(|(c0, v0), (c1, v1)| if v0 < v1 { (c0, v0) } else { (c1, v1) }) .map(|(c, _)| *c) - .unwrap_or(self.start); + .unwrap_or(self.start) + } + + fn visit(&mut self, coord: (usize, usize)) { self.unvisited_with_distance.remove(&coord); - coord } fn get_neighbours(&self, coordinate: &Coordinate) -> Vec { From 1a34dfda82d43d411d3fc7af58a63836e8e9a8e9 Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Tue, 20 Jun 2023 14:25:54 +0200 Subject: [PATCH 04/16] add 85 and 86 --- src/euler85/Cargo.toml | 8 +++++ src/euler85/src/main.rs | 66 +++++++++++++++++++++++++++++++++++++++++ src/euler86/Cargo.toml | 11 +++++++ src/euler86/src/main.rs | 60 +++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+) create mode 100644 src/euler85/Cargo.toml create mode 100644 src/euler85/src/main.rs create mode 100644 src/euler86/Cargo.toml create mode 100644 src/euler86/src/main.rs diff --git a/src/euler85/Cargo.toml b/src/euler85/Cargo.toml new file mode 100644 index 0000000..d5b1234 --- /dev/null +++ b/src/euler85/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "euler85" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/euler85/src/main.rs b/src/euler85/src/main.rs new file mode 100644 index 0000000..b26c2ab --- /dev/null +++ b/src/euler85/src/main.rs @@ -0,0 +1,66 @@ +const TARGET: usize = 2_000_000; + +fn main() { + let mut x = 1_000; + let mut y = 2; + + let mut grid = Grid::new(x, y); + let mut number; + let mut best_grid = grid.clone(); + let mut best_diff = 5_000_000; + loop { + number = grid.number_rectangles(); + let diff = number.abs_diff(TARGET); + if diff < best_diff { + best_diff = diff; + best_grid = grid.clone(); + } + if number < TARGET { + y += 1; + } else { + x -= 1; + } + if x == 1 { + break; + } + + grid = Grid::new(x, y); + } + + let area = best_grid.area(); + println!("{area}"); +} + +#[derive(Debug, Clone)] +struct Grid { + x: usize, + y: usize, +} + +impl Grid { + fn new(x: usize, y: usize) -> Self { + Self { x, y } + } + + fn number_rectangles(&self) -> usize { + let mut result = 0; + + for x in 1..=self.x { + for y in 1..=self.y { + result += self.fits(x, y); + } + } + + result + } + + fn fits(&self, x: usize, y: usize) -> usize { + let fits_in_x = self.x - (x - 1); + let fits_in_y = self.y - (y - 1); + fits_in_x * fits_in_y + } + + fn area(&self) -> usize { + self.x * self.y + } +} diff --git a/src/euler86/Cargo.toml b/src/euler86/Cargo.toml new file mode 100644 index 0000000..4e96566 --- /dev/null +++ b/src/euler86/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "euler86" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[profile.release] +lto = "fat" +debug = true + diff --git a/src/euler86/src/main.rs b/src/euler86/src/main.rs new file mode 100644 index 0000000..9334990 --- /dev/null +++ b/src/euler86/src/main.rs @@ -0,0 +1,60 @@ +use std::f64::EPSILON; + +const TARGET: u32 = 1_000_000; + +fn main() { + let mut m = 1; + let mut distinct = 0; + + loop { + println!("trying: {m}"); + + for x in 1..m + 1 { + for y in x..m + 1 { + let z = m; + let c = Cuboid::new(x, y, z); + if c.shortest_path_is_integer() { + distinct += 1; + }; + } + } + + if distinct > TARGET { + break; + } + + m += 1; + } + + println!("\n{m}"); +} + +struct Cuboid { + x: u32, + y: u32, + z: u32, +} + +impl Cuboid { + fn new(x: u32, y: u32, z: u32) -> Self { + Self { x, y, z } + } + + fn shortest_path_is_integer(&self) -> bool { + // because of the way we iterator over x, y and z, these are guaranteed to be in order + // and therefore produces the shortest path + let shortest_path = pythagorean(self.x + self.y, self.z); + + is_integer(shortest_path) + } +} + +fn pythagorean(x: u32, y: u32) -> f64 { + ((x.pow(2) + y.pow(2)) as f64).sqrt() +} + +fn is_integer(result: f64) -> bool { + let rounded = result.round(); + + (result - rounded).abs() <= EPSILON +} From a47d72f8cca66b1c96be02069c1407d53d31b48c Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Tue, 20 Jun 2023 14:53:17 +0200 Subject: [PATCH 05/16] add 92 --- src/euler92/Cargo.toml | 9 +++++++++ src/euler92/src/main.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/euler92/Cargo.toml create mode 100644 src/euler92/src/main.rs diff --git a/src/euler92/Cargo.toml b/src/euler92/Cargo.toml new file mode 100644 index 0000000..93ea1b7 --- /dev/null +++ b/src/euler92/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "euler92" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[profile.release] +lto = "fat" diff --git a/src/euler92/src/main.rs b/src/euler92/src/main.rs new file mode 100644 index 0000000..24c4785 --- /dev/null +++ b/src/euler92/src/main.rs @@ -0,0 +1,38 @@ +const MAXIMUM: usize = 10_000_000; + +fn main() { + let mut arrives_at_89 = 0; + + for i in 1..MAXIMUM { + let mut number = i; + loop { + let sum: usize = number + .to_digits() + .into_iter() + .map(|d| d.pow(2) as usize) + .sum(); + if sum == 89 { + arrives_at_89 += 1; + break; + } else if sum == 1 { + break; + } + number = sum; + } + } + + println!("{arrives_at_89}"); +} + +trait ToDigits { + fn to_digits(&self) -> Vec; +} + +impl ToDigits for usize { + fn to_digits(&self) -> Vec { + self.to_string() + .chars() + .map(|c| c.to_digit(10).unwrap() as _) + .collect() + } +} From e76f3199de9aecbb8aac9f4c7290aa1eabed0017 Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Tue, 20 Jun 2023 15:47:02 +0200 Subject: [PATCH 06/16] add framework for 93 (WIP) --- src/euler93/Cargo.toml | 8 ++++++ src/euler93/src/main.rs | 64 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/euler93/Cargo.toml create mode 100644 src/euler93/src/main.rs diff --git a/src/euler93/Cargo.toml b/src/euler93/Cargo.toml new file mode 100644 index 0000000..d25de3e --- /dev/null +++ b/src/euler93/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "euler93" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/euler93/src/main.rs b/src/euler93/src/main.rs new file mode 100644 index 0000000..6c29e54 --- /dev/null +++ b/src/euler93/src/main.rs @@ -0,0 +1,64 @@ +type Num = f64; + +fn main() { + let digits = Digits(1., 2., 3., 4.); + + let mut all_possible_integers = digits.all_possible_integers(); + + all_possible_integers.sort(); + + let mut result = 0; + + for (i, num) in all_possible_integers.into_iter().enumerate() { + if i + 1 == num as usize { + result += 1; + } else { + break; + } + } +} + +struct Digits(Num, Num, Num, Num); + +impl Digits { + fn all_possible_integers(&self) -> Vec { + let a = self.0; + let b = self.1; + let c = self.2; + let d = self.3; + + let outcomes: Vec<_> = Operations::new() + .map(|f| f(a, b) as u32) + .filter(|i| *i > 0) + .collect(); + + dbg!(outcomes) + } +} + +struct Operations { + last: usize, +} + +impl Iterator for Operations { + type Item = Box Num>; + + fn next(&mut self) -> Option { + self.last += 1; + match self.last { + 1 => Some(Box::new(|l, r| l + r)), + 2 => Some(Box::new(|l, r| l - r)), + 3 => Some(Box::new(|l, r| r - l)), + 4 => Some(Box::new(|l, r| l * r)), + 5 => Some(Box::new(|l, r| l / r)), + 6 => Some(Box::new(|l, r| r / l)), + _ => None, + } + } +} + +impl Operations { + fn new() -> Self { + Self { last: 0 } + } +} From bf248f902cb8bf005596491ba6f80066b812aebb Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Tue, 20 Jun 2023 16:20:46 +0200 Subject: [PATCH 07/16] add framework for 93 (WIP) --- src/euler93/Cargo.toml | 1 + src/euler93/src/main.rs | 51 +++++++++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/euler93/Cargo.toml b/src/euler93/Cargo.toml index d25de3e..69a13a5 100644 --- a/src/euler93/Cargo.toml +++ b/src/euler93/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +itertools = "*" diff --git a/src/euler93/src/main.rs b/src/euler93/src/main.rs index 6c29e54..92359f8 100644 --- a/src/euler93/src/main.rs +++ b/src/euler93/src/main.rs @@ -1,3 +1,5 @@ +use itertools::Itertools; + type Num = f64; fn main() { @@ -18,21 +20,50 @@ fn main() { } } +fn recurse(array: &[Num]) -> Vec { + if array.len() == 2 { + let [a, b] = array else { panic!() }; + + let mut v = vec![]; + + for ops in Operations::iter() { + v.push(ops(*a, *b)); + } + v + } else { + let a = array[0]; + let b = array[1]; + + let mut v = vec![]; + + for ops in Operations::iter() { + v.push(ops(a, b)); + } + } +} + struct Digits(Num, Num, Num, Num); impl Digits { fn all_possible_integers(&self) -> Vec { - let a = self.0; - let b = self.1; - let c = self.2; - let d = self.3; + let array = [self.0, self.1, self.2, self.3]; - let outcomes: Vec<_> = Operations::new() - .map(|f| f(a, b) as u32) - .filter(|i| *i > 0) - .collect(); + array.into_iter().permutations(4).next().map(|a| { + for ops in Operations::iter() { + let x = ops(a[0], a[1]); + } + }); - dbg!(outcomes) + // dbg!(v); + + todo!() + + // let outcomes: Vec<_> = Operations::new() + // .map(|f| f(a, b) as u32) + // .filter(|i| *i > 0) + // .collect(); + + // dbg!(outcomes) } } @@ -58,7 +89,7 @@ impl Iterator for Operations { } impl Operations { - fn new() -> Self { + fn iter() -> Self { Self { last: 0 } } } From 5d262b44c49c0e2d3465ae15c8ae4f01e854ca99 Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Tue, 27 Jun 2023 16:08:09 +0200 Subject: [PATCH 08/16] finish 93 --- src/euler93/src/main.rs | 148 ++++++++++++++++++++++++++-------------- 1 file changed, 97 insertions(+), 51 deletions(-) diff --git a/src/euler93/src/main.rs b/src/euler93/src/main.rs index 92359f8..e84eb7a 100644 --- a/src/euler93/src/main.rs +++ b/src/euler93/src/main.rs @@ -1,69 +1,115 @@ +use std::cmp::Ordering; + use itertools::Itertools; type Num = f64; +type Quadruple = [Num; 4]; + +const ALL_DIGITS: [Num; 10] = [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]; fn main() { - let digits = Digits(1., 2., 3., 4.); + let mut digits_length = vec![]; - let mut all_possible_integers = digits.all_possible_integers(); + for quadruple_vec in ALL_DIGITS.into_iter().permutations(4) { + let quadruple = vec_to_quadruple(quadruple_vec); - all_possible_integers.sort(); + let digits = Digits(quadruple); - let mut result = 0; + let permutations = digits.permutations(); - for (i, num) in all_possible_integers.into_iter().enumerate() { - if i + 1 == num as usize { - result += 1; - } else { - break; - } - } -} + let mut results: Vec<_> = permutations + .into_iter() + .flat_map(find_integer_results) + .collect(); -fn recurse(array: &[Num]) -> Vec { - if array.len() == 2 { - let [a, b] = array else { panic!() }; - - let mut v = vec![]; - - for ops in Operations::iter() { - v.push(ops(*a, *b)); - } - v - } else { - let a = array[0]; - let b = array[1]; - - let mut v = vec![]; - - for ops in Operations::iter() { - v.push(ops(a, b)); - } - } -} - -struct Digits(Num, Num, Num, Num); - -impl Digits { - fn all_possible_integers(&self) -> Vec { - let array = [self.0, self.1, self.2, self.3]; - - array.into_iter().permutations(4).next().map(|a| { - for ops in Operations::iter() { - let x = ops(a[0], a[1]); + results.sort_unstable_by(|n1, n2| { + if n1 < n2 { + Ordering::Less + } else { + Ordering::Greater } }); - // dbg!(v); + let result = results + .into_iter() + .tuple_windows() + .filter(|(v0, v1)| (v1 - v0) > Num::EPSILON) + .map(|(n1, _)| n1) // TODO last number may be missing? + .collect_vec(); - todo!() + let length = number_consecutive_positive_integers(result); - // let outcomes: Vec<_> = Operations::new() - // .map(|f| f(a, b) as u32) - // .filter(|i| *i > 0) - // .collect(); + digits_length.push((quadruple, length)); + } - // dbg!(outcomes) + digits_length.sort_unstable_by(|a, b| a.1.cmp(&b.1)); + + let mut result = digits_length.last().unwrap().0; + + result.sort_by(Num::total_cmp); + + let s: String = result + .into_iter() + .map(|d| char::from_digit(d as u32, 10).unwrap()) + .collect(); + + println!("{s}"); +} + +fn number_consecutive_positive_integers(result: Vec) -> usize { + let length = result.len(); + + result + .into_iter() + .tuple_windows() + .filter(|(a, b)| b - 1.5 > *a) + .map(|(a, _)| a.round() as usize) + .next() + .unwrap_or(length) +} + +fn find_integer_results(quadruple: Quadruple) -> Vec { + let [i0, i1, i2, i3] = quadruple; + + let mut result = vec![]; + + for op1 in Operations::iter() { + for op2 in Operations::iter() { + for op3 in Operations::iter() { + let num = op3(op2(op1(i0, i1), i2), i3); + if num > 0. && is_integer(num) { + result.push(num); + } + } + } + } + + result +} + +fn is_integer(num: Num) -> bool { + (num.round() - num).abs() <= Num::EPSILON +} + +struct Digits(Quadruple); + +impl Digits { + fn permutations(&self) -> Vec { + self.0 + .into_iter() + .permutations(4) + .map(vec_to_quadruple) + .collect() + } +} + +fn vec_to_quadruple(v: Vec) -> Quadruple { + unsafe { + let i0 = *v.get_unchecked(0); + let i1 = *v.get_unchecked(1); + let i2 = *v.get_unchecked(2); + let i3 = *v.get_unchecked(3); + [i0, i1, i2, i3] } } @@ -72,7 +118,7 @@ struct Operations { } impl Iterator for Operations { - type Item = Box Num>; + type Item = Box Num>; fn next(&mut self) -> Option { self.last += 1; From 1e93703c61d944c90c19a909ed808f2d62ce14df Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Tue, 27 Jun 2023 16:48:14 +0200 Subject: [PATCH 09/16] add 94 (WIP) --- src/euler94/Cargo.toml | 8 +++++++ src/euler94/src/main.rs | 50 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/euler94/Cargo.toml create mode 100644 src/euler94/src/main.rs diff --git a/src/euler94/Cargo.toml b/src/euler94/Cargo.toml new file mode 100644 index 0000000..e197630 --- /dev/null +++ b/src/euler94/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "euler94" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/euler94/src/main.rs b/src/euler94/src/main.rs new file mode 100644 index 0000000..127fa08 --- /dev/null +++ b/src/euler94/src/main.rs @@ -0,0 +1,50 @@ +type Num = u64; + +const ONE_BILLION: Num = 1_000_000_000; + +fn main() { + let mut sum = 0 as Num; + 'outer: for i in 2.. { + let a_b = i as Num; + for c in [a_b - 1, a_b + 1] { + let t = Triangle::new(a_b, c); + let p = t.perimeter(); + + if p > ONE_BILLION { + break 'outer; + } + + if t.has_integer_area() { + sum += p; + } + } + } + + println!("{sum}"); +} + +struct Triangle { + a_b: Num, + c: Num, +} + +impl Triangle { + fn new(a_b: Num, c: Num) -> Self { + Self { a_b, c } + } + + fn perimeter(&self) -> Num { + 2 * self.a_b + self.c + } + + fn has_integer_area(&self) -> bool { + let a_b = self.a_b as f64; + let c = self.c as f64; + let area = c / 4. * (4. * a_b.powi(2) - c.powi(2)).sqrt(); + is_integer(area) + } +} + +fn is_integer(x: f64) -> bool { + (x.round() - x).abs() <= f64::EPSILON +} From 99c64ad172279e8fe15db920c1e9321246e25a1d Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Tue, 27 Jun 2023 17:41:58 +0200 Subject: [PATCH 10/16] finish 94 --- src/euler94/Cargo.toml | 6 +++++ src/euler94/src/main.rs | 51 ++++++++++++++++++++++++++--------------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/euler94/Cargo.toml b/src/euler94/Cargo.toml index e197630..0f099c5 100644 --- a/src/euler94/Cargo.toml +++ b/src/euler94/Cargo.toml @@ -6,3 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +f128 = "0.2.9" +num-traits = "0.2.15" + +[profile.release] +lto = "fat" +codegen-units = 1 diff --git a/src/euler94/src/main.rs b/src/euler94/src/main.rs index 127fa08..527b02f 100644 --- a/src/euler94/src/main.rs +++ b/src/euler94/src/main.rs @@ -1,50 +1,65 @@ -type Num = u64; +use f128::f128; +use num_traits::Float; -const ONE_BILLION: Num = 1_000_000_000; +type Num = f128; + +static mut ONE_BILLION: Num = f128::ZERO; +const ONE: Num = f128::ONE; fn main() { - let mut sum = 0 as Num; + unsafe { + ONE_BILLION = f128::from(1_000_000_000.); + } + + let mut sum = f128::ZERO; + 'outer: for i in 2.. { - let a_b = i as Num; - for c in [a_b - 1, a_b + 1] { - let t = Triangle::new(a_b, c); + let a = f128::from(i); + + for c in [a - ONE, a + ONE] { + let t = Triangle::new(a, c); let p = t.perimeter(); - if p > ONE_BILLION { + if unsafe { p > ONE_BILLION } { break 'outer; } if t.has_integer_area() { + println!("{:.10}, {:.10}", t.a, t.c); sum += p; } } } - println!("{sum}"); + println!("{sum:.15}"); } struct Triangle { - a_b: Num, + a: Num, c: Num, } impl Triangle { - fn new(a_b: Num, c: Num) -> Self { - Self { a_b, c } + fn new(a: Num, c: Num) -> Self { + Self { a, c } } fn perimeter(&self) -> Num { - 2 * self.a_b + self.c + Num::TWO * self.a + self.c } fn has_integer_area(&self) -> bool { - let a_b = self.a_b as f64; - let c = self.c as f64; - let area = c / 4. * (4. * a_b.powi(2) - c.powi(2)).sqrt(); - is_integer(area) + is_integer(self.area()) + } + + fn area(&self) -> Num { + let a = self.a; + let c = self.c; + + c / Num::from(4.) * (Num::from(4.) * a.powi(2) - c.powi(2)).sqrt() } } -fn is_integer(x: f64) -> bool { - (x.round() - x).abs() <= f64::EPSILON +fn is_integer(x: Num) -> bool { + (x.round() - x).abs() <= Num::EPSILON } From 0009062486d4972c6cc4a6f4255281010fe3aa75 Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Tue, 27 Jun 2023 18:25:01 +0200 Subject: [PATCH 11/16] optimize 94 --- src/euler94/src/main.rs | 54 +++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/src/euler94/src/main.rs b/src/euler94/src/main.rs index 527b02f..f6a0fa2 100644 --- a/src/euler94/src/main.rs +++ b/src/euler94/src/main.rs @@ -1,34 +1,32 @@ -use f128::f128; -use num_traits::Float; - -type Num = f128; - -static mut ONE_BILLION: Num = f128::ZERO; -const ONE: Num = f128::ONE; +type Num = usize; fn main() { - unsafe { - ONE_BILLION = f128::from(1_000_000_000.); - } + let mut sum = 0; - let mut sum = f128::ZERO; + let mut i = 5; + 'outer: loop { + if i > 333_333_334 { + break; + } + let a = i; - 'outer: for i in 2.. { - let a = f128::from(i); + if a % 2 == 0 { + i += 1; + continue; + } - for c in [a - ONE, a + ONE] { + for c in [a - 1, a + 1] { let t = Triangle::new(a, c); - let p = t.perimeter(); - - if unsafe { p > ONE_BILLION } { - break 'outer; - } if t.has_integer_area() { println!("{:.10}, {:.10}", t.a, t.c); - sum += p; + sum += t.perimeter(); + i = ((i as f64) * 3.59).trunc() as usize; + continue 'outer; } } + + i += 1; } println!("{sum:.15}"); @@ -45,21 +43,13 @@ impl Triangle { } fn perimeter(&self) -> Num { - Num::TWO * self.a + self.c + 2 * self.a + self.c } fn has_integer_area(&self) -> bool { - is_integer(self.area()) - } + let h = 4 * self.a.pow(2) - self.c.pow(2); + let root = (h as f64).sqrt().trunc() as usize; - fn area(&self) -> Num { - let a = self.a; - let c = self.c; - - c / Num::from(4.) * (Num::from(4.) * a.powi(2) - c.powi(2)).sqrt() + root * root == h } } - -fn is_integer(x: Num) -> bool { - (x.round() - x).abs() <= Num::EPSILON -} From 708aafb362b64fbb2e002718f739b5d04e06931c Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Tue, 27 Jun 2023 18:57:46 +0200 Subject: [PATCH 12/16] add 95 (WIP) --- src/euler95/Cargo.toml | 8 +++++++ src/euler95/src/main.rs | 51 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/euler95/Cargo.toml create mode 100644 src/euler95/src/main.rs diff --git a/src/euler95/Cargo.toml b/src/euler95/Cargo.toml new file mode 100644 index 0000000..38eb5dd --- /dev/null +++ b/src/euler95/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "euler95" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/euler95/src/main.rs b/src/euler95/src/main.rs new file mode 100644 index 0000000..0bf37d4 --- /dev/null +++ b/src/euler95/src/main.rs @@ -0,0 +1,51 @@ +const MAX: usize = 1_000; + +fn main() { + let mut numbers: Vec<_> = (0_usize..MAX + 1).map(|_| true).collect(); + + let mut chains = vec![]; + + let mut i = 0_usize; + loop { + if i > MAX { + break; + } + let mut num = if numbers[i] { + numbers[i] = false; + i + } else { + i += 1; + continue; + }; + + let mut maybe_chain = vec![]; + + loop { + let next: usize = get_proper_divisors(num).sum(); + if next > MAX { + break; + } + + if !numbers[next] { + if maybe_chain.contains(&next) { + chains.push(maybe_chain); + } + break; + } + + numbers[next] = false; + maybe_chain.push(num); + + num = next; + } + + i += 1; + } + + dbg!(chains); +} + +fn get_proper_divisors(x: usize) -> impl Iterator { + let limit = x / 2 + 1; + (1..limit).filter(move |i| x % *i == 0) +} From 5cb8d0cfe7321dbea5befc34261ccab5785e9859 Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Wed, 28 Jun 2023 14:41:10 +0200 Subject: [PATCH 13/16] basic version of 95, very slow --- src/euler95/Cargo.toml | 7 ++- src/euler95/src/main.rs | 117 +++++++++++++++++++++++++++------------- 2 files changed, 86 insertions(+), 38 deletions(-) diff --git a/src/euler95/Cargo.toml b/src/euler95/Cargo.toml index 38eb5dd..269262f 100644 --- a/src/euler95/Cargo.toml +++ b/src/euler95/Cargo.toml @@ -5,4 +5,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] +[dev-dependencies] +rstest = "0.17" + +[profile.release] +debug = true +lto = "fat" diff --git a/src/euler95/src/main.rs b/src/euler95/src/main.rs index 0bf37d4..f059047 100644 --- a/src/euler95/src/main.rs +++ b/src/euler95/src/main.rs @@ -1,51 +1,94 @@ -const MAX: usize = 1_000; +use std::collections::HashSet; + +const MAX: usize = 1_000_000; fn main() { - let mut numbers: Vec<_> = (0_usize..MAX + 1).map(|_| true).collect(); - let mut chains = vec![]; - let mut i = 0_usize; - loop { - if i > MAX { - break; - } - let mut num = if numbers[i] { - numbers[i] = false; - i - } else { - i += 1; + let mut part_of_any_chain = HashSet::new(); + + for current in 1..MAX { + if part_of_any_chain.contains(¤t) { continue; - }; - - let mut maybe_chain = vec![]; - - loop { - let next: usize = get_proper_divisors(num).sum(); - if next > MAX { - break; - } - - if !numbers[next] { - if maybe_chain.contains(&next) { - chains.push(maybe_chain); - } - break; - } - - numbers[next] = false; - maybe_chain.push(num); - - num = next; } - - i += 1; + try_build_chain_from_number(MAX, current, &mut chains, &mut part_of_any_chain); } - dbg!(chains); + chains.sort_by_key(|chain| chain.len()); + + let longest = chains.last().unwrap(); + + let smallest = longest.iter().min().unwrap().to_owned(); + + println!("{smallest}"); +} + +fn try_build_chain_from_number( + max: usize, + mut current: usize, + chains: &mut Vec>, + part_of_any_chain: &mut HashSet, +) { + let mut maybe_chain = vec![]; + + loop { + maybe_chain.push(current); + + let next: usize = get_proper_divisors(current).sum(); + + if next > max || next == 0 { + break; + } + + 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; + } + + current = next; + } } fn get_proper_divisors(x: usize) -> impl Iterator { let limit = x / 2 + 1; (1..limit).filter(move |i| x % *i == 0) } + +#[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; + + assert_eq!(get_proper_divisors(input).collect::>(), 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; + + use crate::try_build_chain_from_number; + + let max = expected.iter().flatten().max().unwrap().to_owned(); + let mut part_of_any_chain = HashSet::new(); + + let mut chains = vec![]; + try_build_chain_from_number(max, input, &mut chains, &mut part_of_any_chain); + + assert_eq!(chains, expected); + } +} From 3d588cc232d83bb8440e38aae5d7a315d3d549ac Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Wed, 28 Jun 2023 15:15:27 +0200 Subject: [PATCH 14/16] speed up proper divisor algorithm (95) --- src/euler95/src/main.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/euler95/src/main.rs b/src/euler95/src/main.rs index f059047..6decb83 100644 --- a/src/euler95/src/main.rs +++ b/src/euler95/src/main.rs @@ -23,6 +23,10 @@ fn main() { println!("{smallest}"); } +fn build_proper_divisors(max: usize) -> Vec> { + todo!() +} + fn try_build_chain_from_number( max: usize, mut current: usize, @@ -34,7 +38,7 @@ fn try_build_chain_from_number( loop { maybe_chain.push(current); - let next: usize = get_proper_divisors(current).sum(); + let next: usize = get_proper_divisors(current).into_iter().sum(); if next > max || next == 0 { break; @@ -55,9 +59,29 @@ fn try_build_chain_from_number( } } -fn get_proper_divisors(x: usize) -> impl Iterator { +fn get_proper_divisors(x: usize) -> Vec { + let mut result = vec![1]; + let limit = x / 2 + 1; - (1..limit).filter(move |i| x % *i == 0) + + 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)] @@ -72,7 +96,7 @@ mod tests { fn proper_divisor(#[case] input: usize, #[case] expected: Vec) { use crate::get_proper_divisors; - assert_eq!(get_proper_divisors(input).collect::>(), expected); + assert_eq!(get_proper_divisors(input), expected); } #[rstest] From 921f68935677b45094476b1c02bb839131a6d7ac Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Wed, 28 Jun 2023 15:30:24 +0200 Subject: [PATCH 15/16] starting second approach (95, WIP) --- src/euler95/Cargo.toml | 3 +++ src/euler95/src/main.rs | 23 +++++++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/euler95/Cargo.toml b/src/euler95/Cargo.toml index 269262f..8069e56 100644 --- a/src/euler95/Cargo.toml +++ b/src/euler95/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +itertools = "*" + [dev-dependencies] rstest = "0.17" diff --git a/src/euler95/src/main.rs b/src/euler95/src/main.rs index 6decb83..47bc04c 100644 --- a/src/euler95/src/main.rs +++ b/src/euler95/src/main.rs @@ -1,8 +1,15 @@ use std::collections::HashSet; +use itertools::Itertools; + const MAX: usize = 1_000_000; fn main() { + let mut x = build_proper_divisors(3); + x.sort(); + dbg!(x); + panic!(); + let mut chains = vec![]; let mut part_of_any_chain = HashSet::new(); @@ -23,10 +30,6 @@ fn main() { println!("{smallest}"); } -fn build_proper_divisors(max: usize) -> Vec> { - todo!() -} - fn try_build_chain_from_number( max: usize, mut current: usize, @@ -59,6 +62,18 @@ fn try_build_chain_from_number( } } +fn build_proper_divisors(max: usize) -> Vec<(usize, Vec)> { + // let mut result = vec![]; + + let result = (1..max + 1) + .combinations(2) + .map(|v| (v.clone().into_iter().product(), v)) + .collect(); + // result.push(x); + + result +} + fn get_proper_divisors(x: usize) -> Vec { let mut result = vec![1]; From e11b98afa3052a138aba177617ca0ed7da839877 Mon Sep 17 00:00:00 2001 From: "Dr. Matthias Ratajczak" Date: Wed, 12 Jul 2023 13:27:20 +0200 Subject: [PATCH 16/16] Revert "starting second approach (95, WIP)" This reverts commit 921f68935677b45094476b1c02bb839131a6d7ac. --- src/euler95/Cargo.toml | 3 --- src/euler95/src/main.rs | 23 ++++------------------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/src/euler95/Cargo.toml b/src/euler95/Cargo.toml index 8069e56..269262f 100644 --- a/src/euler95/Cargo.toml +++ b/src/euler95/Cargo.toml @@ -5,9 +5,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -itertools = "*" - [dev-dependencies] rstest = "0.17" diff --git a/src/euler95/src/main.rs b/src/euler95/src/main.rs index 47bc04c..6decb83 100644 --- a/src/euler95/src/main.rs +++ b/src/euler95/src/main.rs @@ -1,15 +1,8 @@ use std::collections::HashSet; -use itertools::Itertools; - const MAX: usize = 1_000_000; fn main() { - let mut x = build_proper_divisors(3); - x.sort(); - dbg!(x); - panic!(); - let mut chains = vec![]; let mut part_of_any_chain = HashSet::new(); @@ -30,6 +23,10 @@ fn main() { println!("{smallest}"); } +fn build_proper_divisors(max: usize) -> Vec> { + todo!() +} + fn try_build_chain_from_number( max: usize, mut current: usize, @@ -62,18 +59,6 @@ fn try_build_chain_from_number( } } -fn build_proper_divisors(max: usize) -> Vec<(usize, Vec)> { - // let mut result = vec![]; - - let result = (1..max + 1) - .combinations(2) - .map(|v| (v.clone().into_iter().product(), v)) - .collect(); - // result.push(x); - - result -} - fn get_proper_divisors(x: usize) -> Vec { let mut result = vec![1];