mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-08-04 15:07:47 +00:00
factor: Refactor (eheh) around a Factors
datatype
It is clearer to see what is going on, as opposed to passing around an unmarked `Vec<u64>`, and there is a single place to add invariants checks. This is also a more compact memory representation: each prime factor is represented only once, with an additional byte for multiplicity. The performance impact is however not significant.
This commit is contained in:
parent
272b66aac8
commit
d9095a2539
1 changed files with 72 additions and 25 deletions
|
@ -23,9 +23,12 @@ use rand::distributions::{Distribution, Uniform};
|
|||
use rand::rngs::SmallRng;
|
||||
use rand::{thread_rng, SeedableRng};
|
||||
use std::cmp::{max, min};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::io::{stdin, BufRead};
|
||||
use std::mem::swap;
|
||||
use std::num::Wrapping;
|
||||
use std::ops;
|
||||
|
||||
mod numeric;
|
||||
|
||||
|
@ -36,14 +39,6 @@ static SUMMARY: &str = "Print the prime factors of the given number(s).
|
|||
If none are specified, read from standard input.";
|
||||
static LONG_HELP: &str = "";
|
||||
|
||||
fn rho_pollard_pseudorandom_function(x: u64, a: u64, b: u64, num: u64) -> u64 {
|
||||
if num < 1 << 63 {
|
||||
(sm_mul(a, sm_mul(x, x, num), num) + b) % num
|
||||
} else {
|
||||
big_add(big_mul(a, big_mul(x, x, num), num), b, num)
|
||||
}
|
||||
}
|
||||
|
||||
fn gcd(mut a: u64, mut b: u64) -> u64 {
|
||||
while b > 0 {
|
||||
a %= b;
|
||||
|
@ -52,6 +47,58 @@ fn gcd(mut a: u64, mut b: u64) -> u64 {
|
|||
a
|
||||
}
|
||||
|
||||
struct Factors {
|
||||
f: HashMap<u64, u8>,
|
||||
}
|
||||
|
||||
impl Factors {
|
||||
fn new() -> Factors {
|
||||
Factors { f: HashMap::new() }
|
||||
}
|
||||
|
||||
fn add(&mut self, prime: u64, exp: u8) {
|
||||
assert!(exp > 0);
|
||||
self.f.insert(prime, exp + self.f.get(&prime).unwrap_or(&0));
|
||||
}
|
||||
|
||||
fn push(&mut self, prime: u64) {
|
||||
self.add(prime, 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::MulAssign<Factors> for Factors {
|
||||
fn mul_assign(&mut self, other: Factors) {
|
||||
for (prime, exp) in &other.f {
|
||||
self.add(*prime, *exp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Factors {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// TODO: Use a representation with efficient in-order iteration
|
||||
let mut primes: Vec<&u64> = self.f.keys().collect();
|
||||
primes.sort();
|
||||
|
||||
for p in primes {
|
||||
for _ in 0..self.f[&p] {
|
||||
write!(f, " {}", p)?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn rho_pollard_pseudorandom_function(x: u64, a: u64, b: u64, num: u64) -> u64 {
|
||||
if num < 1 << 63 {
|
||||
(sm_mul(a, sm_mul(x, x, num), num) + b) % num
|
||||
} else {
|
||||
big_add(big_mul(a, big_mul(x, x, num), num), b, num)
|
||||
}
|
||||
}
|
||||
|
||||
fn rho_pollard_find_divisor(num: u64) -> u64 {
|
||||
#![allow(clippy::many_single_char_names)]
|
||||
let range = Uniform::new(1, num);
|
||||
|
@ -78,31 +125,39 @@ fn rho_pollard_find_divisor(num: u64) -> u64 {
|
|||
}
|
||||
}
|
||||
|
||||
fn rho_pollard_factor(num: u64, factors: &mut Vec<u64>) {
|
||||
fn rho_pollard_factor(num: u64, factors: &mut Factors) {
|
||||
if is_prime(num) {
|
||||
factors.push(num);
|
||||
return;
|
||||
}
|
||||
|
||||
let divisor = rho_pollard_find_divisor(num);
|
||||
rho_pollard_factor(divisor, factors);
|
||||
rho_pollard_factor(num / divisor, factors);
|
||||
}
|
||||
|
||||
fn table_division(mut num: u64, factors: &mut Vec<u64>) {
|
||||
fn table_division(mut num: u64) -> Factors {
|
||||
let mut factors = Factors::new();
|
||||
|
||||
if num < 2 {
|
||||
return;
|
||||
factors.push(num);
|
||||
return factors
|
||||
}
|
||||
|
||||
while num % 2 == 0 {
|
||||
num /= 2;
|
||||
factors.push(2);
|
||||
}
|
||||
|
||||
if num == 1 {
|
||||
return;
|
||||
return factors;
|
||||
}
|
||||
|
||||
if is_prime(num) {
|
||||
factors.push(num);
|
||||
return;
|
||||
return factors;
|
||||
}
|
||||
|
||||
for &(prime, inv, ceil) in P_INVS_U64 {
|
||||
if num == 1 {
|
||||
break;
|
||||
|
@ -120,7 +175,7 @@ fn table_division(mut num: u64, factors: &mut Vec<u64>) {
|
|||
factors.push(prime);
|
||||
if is_prime(num) {
|
||||
factors.push(num);
|
||||
return;
|
||||
return factors;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
@ -136,21 +191,13 @@ fn table_division(mut num: u64, factors: &mut Vec<u64>) {
|
|||
//trial_division_slow(num, factors);
|
||||
//} else if num > 1 {
|
||||
// number is still greater than 1, but not so big that we have to worry
|
||||
rho_pollard_factor(num, factors);
|
||||
rho_pollard_factor(num, &mut factors);
|
||||
factors
|
||||
//}
|
||||
}
|
||||
|
||||
fn print_factors(num: u64) {
|
||||
print!("{}:", num);
|
||||
|
||||
let mut factors = Vec::new();
|
||||
// we always start with table division, and go from there
|
||||
table_division(num, &mut factors);
|
||||
factors.sort();
|
||||
|
||||
for fac in &factors {
|
||||
print!(" {}", fac);
|
||||
}
|
||||
print!("{}:{}", num, table_division(num));
|
||||
println!();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue