lucas_factorization_method.pl 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. #!/usr/bin/perl
  2. # Author: Daniel "Trizen" Șuteu
  3. # Date: 13 October 2018
  4. # https://github.com/trizen
  5. # A new integer factorization method, using the Lucas U and V sequences.
  6. # Inspired by the BPSW primality test.
  7. # See also:
  8. # https://en.wikipedia.org/wiki/Lucas_sequence
  9. # https://en.wikipedia.org/wiki/Lucas_pseudoprime
  10. # https://en.wikipedia.org/wiki/Baillie%E2%80%93PSW_primality_test
  11. use 5.020;
  12. use warnings;
  13. use experimental qw(signatures);
  14. use Math::AnyNum qw(:overload bit_scan1 is_power kronecker gcd prod);
  15. use Math::Prime::Util::GMP qw(lucas_sequence consecutive_integer_lcm random_nbit_prime);
  16. sub lucas_factorization ($n, $B) {
  17. return 1 if $n <= 2;
  18. return 1 if is_power($n);
  19. my ($P, $Q) = (1, 0);
  20. for (my $k = 2 ; ; ++$k) {
  21. my $D = (-1)**$k * (2 * $k + 1);
  22. if (kronecker($D, $n) == -1) {
  23. $Q = (1 - $D) / 4;
  24. last;
  25. }
  26. }
  27. my $d = consecutive_integer_lcm($B);
  28. my ($U, $V) = lucas_sequence($n, $P, $Q, $d);
  29. foreach my $f (sub { gcd($U, $n) }, sub { gcd($V - 2, $n) }) {
  30. my $g = $f->();
  31. return $g if ($g > 1 and $g < $n);
  32. }
  33. return 1;
  34. }
  35. say lucas_factorization(257221 * 470783, 700); #=> 470783 (p+1 is 700-smooth)
  36. say lucas_factorization(333732865481 * 1632480277613, 3000); #=> 333732865481 (p-1 is 3000-smooth)
  37. say lucas_factorization(1124075136413 * 3556516507813, 4000); #=> 1124075136413 (p+1 is 4000-smooth)
  38. say lucas_factorization(6555457852399 * 7864885571993, 700); #=> 6555457852399 (p-1 is 700-smooth)
  39. say lucas_factorization(7553377229 * 588103349, 800); #=> 7553377229 (p+1 is 800-smooth)
  40. # Example of a larger number that can be factorized fast with this method
  41. say lucas_factorization(203544696384073367670016326770637347800169508950125910682353, 19); #=> 5741461760879844361
  42. say "\n=> More tests:";
  43. foreach my $k (10 .. 50) {
  44. my $n = prod(map { random_nbit_prime($k) } 1 .. 2);
  45. my $p = lucas_factorization($n, 2 * $n->ilog2**2);
  46. if ($p > 1 and $p < $n) {
  47. say "$n = $p * ", $n / $p;
  48. }
  49. }
  50. __END__
  51. 36815861 = 6199 * 5939
  52. 748527379 = 31151 * 24029
  53. 2205610861 = 46279 * 47659
  54. 6464972083 = 72623 * 89021
  55. 42908134667 = 165037 * 259991
  56. 144064607993 = 324589 * 443837
  57. 14055375555899 = 3773629 * 3724631
  58. 34326163013579 = 4942513 * 6945083
  59. 635676232543327 = 28513789 * 22293643
  60. 4228743692662373 = 64463821 * 65598713
  61. 44525895097265171 = 211263823 * 210759677
  62. 88671631232856109 = 269999071 * 328414579
  63. 8445394419907066249 = 3185955247 * 2650820167
  64. 508484280918603770621 = 17377315313 * 29261383117
  65. 12301305131668154065127 = 91341582047 * 134673659641
  66. 8834277945256453860289739 = 2536339835969 * 3483081336331