Change bound for factorization methods

This commit is contained in:
Jacob Zhong
2022-05-01 06:54:49 -04:00
parent d1a6719856
commit bbad5f5dbf
4 changed files with 142 additions and 51 deletions
+7
View File
@@ -5,6 +5,9 @@
//! See <https://web.archive.org/web/20110331180514/https://diamond.boisestate.edu/~liljanab/BOISECRYPTFall09/Jacobsen.pdf>
//! for a detailed comparison between different factorization algorithms
// XXX: make the factorization method resumable?
use crate::traits::ExactRoots;
use num_integer::{Integer, Roots};
use num_modular::{ModularCoreOps, ModularUnaryOps};
@@ -234,6 +237,7 @@ pub const SQUFOF_MULTIPLIERS: [u16; 38] = [
/// where p = next_prime(c^a+d1), p = next_prime(c^b+d2), a and b are close, and c, d1, d2 are small integers.
///
/// Reference: Hart, W. B. (2012). A one line factoring algorithm. Journal of the Australian Mathematical Society, 92(1), 61-69. doi:10.1017/S1446788712000146
// TODO: add multipliers preset for one_line method?
pub fn one_line<T: Integer + NumRef + FromPrimitive + ExactRoots + CheckedAdd>(target: &T, mul_target: T, max_iter: usize) -> (Option<T>, usize)
where
for<'r> &'r T: RefNum<T>, {
@@ -250,6 +254,7 @@ where
}
}
// prevent overflow
ikn = if let Some(n) = (&ikn).checked_add(&mul_target) {
n
} else {
@@ -307,6 +312,8 @@ mod tests {
// this case should success at step 276, from https://rosettacode.org/wiki/Talk:Square_form_factorization
assert!(matches!(squfof(&4558849u32, 4558849u32, 300).0, Some(_)));
// TODO(v0.next): add more cases from rosetta code
}
#[test]
+26 -24
View File
@@ -161,10 +161,6 @@ pub fn factorize64(target: u64) -> BTreeMap<u64, usize> {
// https://github.com/elmomoilanen/prime-factorization
// https://github.com/radii/msieve
// Pari/GP: ifac_crack
// TODO(v0.next): check the runtime of each factorization and put the fastest first
// TODO(v0.next): add multipliers for one_line method
// TODO(v0.next): quickly increase the limit for squfof, try to match the behavior of gnu factor
// TODO(v0.next): make the factorization method resumable?
let mut result = BTreeMap::new();
// quick check on factors of 2
@@ -269,31 +265,34 @@ pub(crate) fn factorize64_advanced(cofactors: &[(u64, usize)]) -> Vec<(u64, usiz
// try to find a divisor
let mut i = 0usize;
let mut max_iter = 2 << (target.bits() / 4); // empirical lower bound for iterations
let mut max_iter_ratio = 1; // increase max_iter after factorization round
let divisor = loop {
// try various factorization method iteratively
const NMETHODS: usize = 3;
match i % NMETHODS {
0 => {
// Pollard's rho
// Pollard's rho (quick check)
let start = MontgomeryInt::new(random::<u64>(), target);
let offset = start.convert(random::<u64>());
let max_iter = max_iter_ratio << (target.bits() / 6); // unoptimized heuristic
if let (Some(p), _) = pollard_rho(&Mint::from(target), start.into(), offset.into(), max_iter) {
break p.value();
}
}
1 => {
// Hart's one-line
// Hart's one-line (quick check)
let mul_target = target.checked_mul(480).unwrap_or(target);
let max_iter = max_iter_ratio << (mul_target.bits() / 6); // unoptimized heuristic
if let (Some(p), _) = one_line(&target, mul_target, max_iter) {
break p;
}
}
2 => {
// Shanks's squfof
// Shanks's squfof (main power)
let mut d = None;
for &k in SQUFOF_MULTIPLIERS.iter() {
if let Some(mul_target) = target.checked_mul(k as u64) {
let max_iter = max_iter_ratio * 2 * (2 * mul_target.sqrt()).sqrt() as usize;
if let (Some(p), _) = squfof(&target, mul_target, max_iter) {
d = Some(p);
break;
@@ -310,7 +309,7 @@ pub(crate) fn factorize64_advanced(cofactors: &[(u64, usize)]) -> Vec<(u64, usiz
// increase max iterations after trying all methods
if i % NMETHODS == 0 {
max_iter *= 4;
max_iter_ratio *= 2;
}
};
todo.push((divisor, exp));
@@ -421,23 +420,36 @@ pub(crate) fn factorize128_advanced(cofactors: &[(u128, usize)]) -> Vec<(u128, u
// try to find a divisor
let mut i = 0usize;
let mut max_iter = 2 << (target.bits() / 6); // empirical lower bound
let mut max_iter_ratio = 1;
let divisor = loop {
// try various factorization method iteratively
// try various factorization method iteratively, sort by time per iteration
const NMETHODS: usize = 3;
match i % NMETHODS {
0 => {
// Pollard's rho
let start = MontgomeryInt::new(random::<u128>(), target);
let offset = start.convert(random::<u128>());
let max_iter = max_iter_ratio << (target.bits() / 6); // unoptimized heuristic
if let (Some(p), _) = pollard_rho(&Mint::from(target), start.into(), offset.into(), max_iter) {
break p.value();
}
}
1 => {
// Hart's one-line
let mul_target = target.checked_mul(480).unwrap_or(target);
let max_iter = max_iter_ratio << (mul_target.bits() / 6); // unoptimized heuristic
if let (Some(p), _) = one_line(&target, mul_target, max_iter) {
break p;
}
}
1 => {
2 => {
// Shanks's squfof, try all mutipliers
let mut d = None;
for &k in SQUFOF_MULTIPLIERS.iter() {
if let Some(mul_target) = target.checked_mul(k as u128) {
if let Some(mul_target) = target.checked_mul(k as u128) {
// this bound is from GNU factor
let max_iter = 2*(2 * mul_target.sqrt()).sqrt() as usize;
if let (Some(p), _) = squfof(&target, mul_target, max_iter) {
d = Some(p);
break;
@@ -448,23 +460,13 @@ pub(crate) fn factorize128_advanced(cofactors: &[(u128, usize)]) -> Vec<(u128, u
break p;
}
}
2 => {
// Pollard's rho, only twice
if i / NMETHODS < 2 {
let start = MontgomeryInt::new(random::<u128>(), target);
let offset = start.convert(random::<u128>());
if let (Some(p), _) = pollard_rho(&Mint::from(target), start.into(), offset.into(), max_iter) {
break p.value();
}
}
}
_ => unreachable!(),
}
i += 1;
// increase max iterations after trying all methods
if i % NMETHODS == 0 {
max_iter *= 4;
max_iter_ratio *= 2;
}
};