From e3c1f9bb5866e460a48ed62918d23b097203a1d2 Mon Sep 17 00:00:00 2001 From: Olivia Appleton Date: Thu, 22 Aug 2024 18:12:47 -0500 Subject: [PATCH] Attempt to make p357 not slow --- rust/src/include/factors.rs | 71 +++++++++++++++++++++++++++++++++++++ rust/src/main.rs | 2 +- rust/src/p0357.rs | 5 +-- 3 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 rust/src/include/factors.rs diff --git a/rust/src/include/factors.rs b/rust/src/include/factors.rs new file mode 100644 index 00000000..f529c19f --- /dev/null +++ b/rust/src/include/factors.rs @@ -0,0 +1,71 @@ +use std::collections::HashSet; +use std::cmp::Ord; +use std::hash::Hash; +use std::ops::{Add,Div,Mul,Rem}; + +use num_traits::{one,One,Zero}; +use itertools::Itertools; + +use crate::include::primes::prime_factors; + +pub struct ProperDivisors +{ + seen: HashSet, + factors: Vec, + current_batch: Vec, + curr_index: usize, + next_size: usize, +} + +pub fn proper_divisors(num: I) -> ProperDivisors +{ + return ProperDivisors::::new(num); +} + +impl ProperDivisors +where I: Hash + Zero + One + Add + Ord + Copy + Div + Rem +{ + pub fn new(num: I) -> Self { + return ProperDivisors::{ + seen: HashSet::new(), + factors: prime_factors(num).collect(), + current_batch: vec![], + curr_index: 0, + next_size: 1, + }; + } +} + +impl Iterator for ProperDivisors +where I: Hash + Zero + One + Add + Ord + Copy + Div + Rem + Mul +{ + type Item = I; + + fn next(&mut self) -> Option { + loop { + if self.next_size > self.factors.len() { + return None; + } + if self.curr_index == self.current_batch.len() { + self.current_batch = self.factors + .iter() + .cloned() + .combinations(self.next_size) + .map(|v| v.into_iter().fold(one(), |x, y| x * y)) + .collect(); + self.next_size += 1; + self.curr_index = 0; + } + while self.curr_index < self.current_batch.len() { + let result = self.current_batch[self.curr_index]; + self.curr_index += 1; + if !self.seen.contains(&result) { + self.seen.insert(result); + return Some(result); + } + } + self.current_batch.clear(); + self.curr_index = 0; + } + } +} \ No newline at end of file diff --git a/rust/src/main.rs b/rust/src/main.rs index f7db81e2..8ad20052 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -73,7 +73,7 @@ seq!(N in 01..=20 { #[case::problem_76(76)] #[case::problem_77(77)] #[case::problem_87(87)] -// #[case::problem_357(357)] +#[case::problem_357(357)] #[case::problem_836(836)] fn test_problem(#[case] id: usize) -> Result<(), String> { let Some((_, func, _slow)) = get_problem(id) else { panic!() }; diff --git a/rust/src/p0357.rs b/rust/src/p0357.rs index 63193c7f..650dfef5 100644 --- a/rust/src/p0357.rs +++ b/rust/src/p0357.rs @@ -13,7 +13,8 @@ such that for every divisor d of n, d+n/d is prime. */ use std::collections::HashSet; -use crate::include::primes::{is_prime,primes_until,proper_divisors}; +use crate::include::primes::{is_prime,primes_until}; +use crate::include::factors::proper_divisors; use crate::include::utils::Answer; pub fn p0357() -> Answer { @@ -24,7 +25,7 @@ pub fn p0357() -> Answer { for n in (6..100000000).step_by(4) { // n can't be odd (unless 1) because then n + n/d is even, and can't be a multiple of 4 as shown below let mut broken = false; - for d in proper_divisors(n).into_iter().chain(vec![n].into_iter()) { + for d in proper_divisors(n).chain(vec![n].into_iter()) { if prime_squares.contains(&d) || !is_prime(d + n / d) { // this works because if n=kp^2, then whenever d=p, (p + kp^2/p) = (k+1)p, which isn't prime // but since detecting if n % d^2 == 0 is expensive, I just wait for p^2 to show up