diff --git a/src/solutions/largest_prime_factor.rs b/src/solutions/largest_prime_factor.rs index bd82956..7fa87cc 100644 --- a/src/solutions/largest_prime_factor.rs +++ b/src/solutions/largest_prime_factor.rs @@ -1,8 +1,34 @@ use crate::utils; +/// Determine the largest prime factor of the given number. +/// +/// * `num` +fn largest_prime_factor(mut num: i64) -> i64 { + // Not divisible by 2, 3 or 5. Look for other factors. + utils::PotentialPrimes::new(num) + .map_while(|potential_prime| { + if potential_prime > num { + None + } else if num % potential_prime != 0 { + // Dummy result so that the loop continues looking for factors and + // does not terminate. + Some(0) + } else { + // Found a potential prime factor. Eliminate it. Doing this ensures + // that all factors found are actually prime factors. + while num % potential_prime == 0 { + num /= potential_prime; + } + Some(potential_prime) + } + }) + .max() + .unwrap() +} + pub fn solve() -> i64 { let num: i64 = 600851475143; - let largest_pf = utils::PrimeDivisors::new(num).max().unwrap().0; + let largest_pf = largest_prime_factor(num); assert_eq!(largest_pf, 6857); largest_pf diff --git a/src/utils.rs b/src/utils.rs index 566b956..134e0a5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -32,10 +32,9 @@ pub fn is_prime(num: i64) -> bool { /// * `num` Must not be divisible by 2, 3 or 5. Must exceed 5. fn is_prime_td(num: i64) -> bool { // No need to search for composite factors. We'll find prime factors (if - // any) faster. - PotentialPrimes::new(isqrt(num)) - .skip(3) - .all(|potential_prime| num % potential_prime != 0) + // any) faster. Don't bother generating prime numbers. Potential prime + // numbers are faster to generate. + PotentialPrimes::new(isqrt(num)).all(|potential_prime| num % potential_prime != 0) } /// Check whether the given number is prime using the Miller-Rabin test. @@ -236,7 +235,6 @@ pub use iterators::divisors::Divisors; pub use iterators::fibonacci::Fibonacci; pub use iterators::polygonal::Polygonal; pub use iterators::potential_primes::PotentialPrimes; -pub use iterators::prime_divisors::PrimeDivisors; pub use iterators::pythagorean_triplets::PythagoreanTriplets; #[cfg(test)] diff --git a/src/utils/iterators.rs b/src/utils/iterators.rs index f6a055d..f47ff90 100644 --- a/src/utils/iterators.rs +++ b/src/utils/iterators.rs @@ -7,5 +7,4 @@ pub mod divisors; pub mod fibonacci; pub mod polygonal; pub mod potential_primes; -pub mod prime_divisors; pub mod pythagorean_triplets; diff --git a/src/utils/iterators/potential_primes.rs b/src/utils/iterators/potential_primes.rs index 3b21fa2..4112baf 100644 --- a/src/utils/iterators/potential_primes.rs +++ b/src/utils/iterators/potential_primes.rs @@ -1,5 +1,5 @@ -/// Potential prime numbers. Generates some small prime numbers and numbers -/// coprime to 30. Used for wheel factorisation with 2, 3 and 5. +/// Potential prime numbers. Generates numbers coprime to 30, starting from 7. +/// Used for wheel factorisation with 2, 3 and 5. pub struct PotentialPrimes { limit: i64, num: i64, @@ -11,7 +11,7 @@ impl PotentialPrimes { PotentialPrimes { limit, num: 1, - offset: [4, 2, 4, 2, 4, 6, 2, 6].into_iter().cycle(), + offset: [6, 4, 2, 4, 2, 4, 6, 2].into_iter().cycle(), } } } @@ -19,12 +19,7 @@ impl PotentialPrimes { impl Iterator for PotentialPrimes { type Item = i64; fn next(&mut self) -> Option { - self.num += match self.num { - 1 => 1, - 2 => 1, - 3 | 5 => 2, - _ => self.offset.next().unwrap(), - }; + self.num += self.offset.next().unwrap(); if self.num > self.limit { None } else { diff --git a/src/utils/iterators/prime_divisors.rs b/src/utils/iterators/prime_divisors.rs deleted file mode 100644 index 3e16ae0..0000000 --- a/src/utils/iterators/prime_divisors.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::utils; - -/// Prime divisors iterator. Generates all prime divisors of a number in -/// ascending order. Positive numbers only! -pub struct PrimeDivisors { - // If I actually find all prime numbers to iterate over (instead of just - // using potential prime numbers), performance drops significantly. - potential_primes: utils::PotentialPrimes, - num: i64, -} - -impl PrimeDivisors { - pub fn new(num: i64) -> PrimeDivisors { - PrimeDivisors { - potential_primes: utils::PotentialPrimes::new(num), - num, - } - } -} - -impl Iterator for PrimeDivisors { - type Item = (i64, u32); - fn next(&mut self) -> Option<(i64, u32)> { - loop { - let potential_prime = match self.potential_primes.next() { - Some(potential_prime) if potential_prime <= self.num => potential_prime, - _ => return None, - }; - let mut power = 0; - while self.num % potential_prime == 0 { - self.num /= potential_prime; - power += 1; - } - // Since I divide out the number by all potential primes I find, - // the potential primes I do find are actually prime. - if power > 0 { - return Some((potential_prime, power)); - } - } - } -}