difference_of_powers_factorization_method.pl 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 03 July 2019
  4. # Edit: 22 March 2022
  5. # https://github.com/trizen
  6. # A simple factorization method for numbers that can be expressed as a difference of powers.
  7. # Very effective for numbers of the form:
  8. #
  9. # n^k - 1
  10. #
  11. # where k has many divisors.
  12. use 5.020;
  13. use strict;
  14. use warnings;
  15. use experimental qw(signatures);
  16. use Math::GMPz;
  17. use ntheory qw(divisors rootint logint is_power gcd vecprod powint);
  18. use constant {
  19. MIN_FACTOR => 1, # ignore small factors
  20. LOG_BRANCH => 0, # true to use the log branch in addition to the root branch
  21. };
  22. sub diff_power_factorization ($n, $verbose = 0) {
  23. if (ref($n) ne 'Math::GMPz') {
  24. $n = Math::GMPz->new("$n");
  25. }
  26. my $orig = $n;
  27. my @diff_powers_params;
  28. my $diff_powers = sub ($r1, $e1, $r2, $e2) {
  29. my @factors;
  30. my @divs1 = divisors($e1);
  31. my @divs2 = divisors($e2);
  32. foreach my $d1 (@divs1) {
  33. my $x = $r1**$d1;
  34. foreach my $d2 (@divs2) {
  35. my $y = $r2**$d2;
  36. foreach my $j (1, -1) {
  37. my $t = $x - $j * $y;
  38. my $g = gcd($t, $n);
  39. if ($g > MIN_FACTOR and $g < $n) {
  40. while ($n % $g == 0) {
  41. $n /= $g;
  42. push @factors, $g;
  43. }
  44. }
  45. }
  46. }
  47. }
  48. sort { $a <=> $b } @factors;
  49. };
  50. my $diff_power_check = sub ($r1, $e1) {
  51. my $u = $r1**$e1;
  52. my $dx = abs($u - $n);
  53. if ($dx >= 1 and Math::GMPz::Rmpz_perfect_power_p($dx)) {
  54. my $e2 = ($dx == 1) ? 1 : is_power($dx);
  55. my $r2 = Math::GMPz->new(rootint($dx, $e2));
  56. if ($verbose) {
  57. if ($u > $n) {
  58. say "[*] Difference of powers detected: ", sprintf("%s^%s - %s^%s", $r1, $e1, $r2, $e2);
  59. }
  60. else {
  61. say "[*] Sum of powers detected: ", sprintf("%s^%s + %s^%s", $r1, $e1, $r2, $e2);
  62. }
  63. }
  64. push @diff_powers_params, [$r1, $e1, $r2, $e2];
  65. }
  66. };
  67. # Sum and difference of powers of the form a^k ± b^k, where a and b are large.
  68. foreach my $e1 (reverse 2 .. logint($n, 2)) {
  69. my $t = Math::GMPz->new(rootint($n, $e1));
  70. $diff_power_check->($t, $e1); # sum of powers
  71. $diff_power_check->($t + 1, $e1); # difference of powers
  72. }
  73. # Sum and difference of powers of the form a^k ± b^k, where a and b are small.
  74. if (LOG_BRANCH) {
  75. foreach my $r1 (2 .. logint($n, 2)) {
  76. my $t = logint($n, $r1);
  77. $diff_power_check->(Math::GMPz->new($r1), $t); # sum of powers
  78. $diff_power_check->(Math::GMPz->new($r1), $t + 1); # difference of powers
  79. }
  80. my %seen_param;
  81. @diff_powers_params = grep { !$seen_param{join(' ', @$_)}++ } @diff_powers_params;
  82. }
  83. my @factors;
  84. foreach my $fp (@diff_powers_params) {
  85. push @factors, $diff_powers->(@$fp);
  86. }
  87. push @factors, $orig / vecprod(@factors);
  88. return sort { $a <=> $b } @factors;
  89. }
  90. if (@ARGV) {
  91. say join ', ', diff_power_factorization($ARGV[0], 1);
  92. exit;
  93. }
  94. # Large roots
  95. say join ' * ', diff_power_factorization(powint(1009, 24) + powint(29, 12));
  96. say join ' * ', diff_power_factorization(powint(1009, 24) - powint(29, 12));
  97. say join ' * ', diff_power_factorization(powint(59388821, 12) - powint(151, 36));
  98. say '-' x 80;
  99. # Small roots
  100. say join ' * ', diff_power_factorization(powint(2, 256) - 1);
  101. say join ' * ', diff_power_factorization(powint(10, 120) + 1);
  102. say join ' * ', diff_power_factorization(powint(10, 120) - 1);
  103. say join ' * ', diff_power_factorization(powint(10, 120) - 25);
  104. say join ' * ', diff_power_factorization(powint(10, 105) - 1);
  105. say join ' * ', diff_power_factorization(powint(10, 105) + 1);
  106. say join ' * ', diff_power_factorization(powint(10, 120) - 2134 * 2134);
  107. __END__
  108. 2 * 537154643295831327753001 * 1154140443257087164049583013000044736320575461201
  109. 6 * 6 * 13 * 19 * 31 * 37 * 140 * 33937 * 36359 * 45120343 * 14006607073 * 1036518447751 * 1074309285719975471632201
  110. 3 * 3 * 10 * 12 * 13 * 14 * 19 * 61 * 1745327 * 5594587 * 28145554676761 * 85497773607889 * 1769442985679221 * 203250599010814323919992393181
  111. --------------------------------------------------------------------------------
  112. 3 * 5 * 17 * 257 * 65537 * 4294967297 * 18446744073709551617 * 340282366920938463463374607431768211457
  113. 100000001 * 9999999900000001 * 99999999000000009999999900000001 * 10000000099999999999999989999999899999999000000000000000100000001
  114. 3 * 9 * 11 * 37 * 91 * 101 * 9091 * 9901 * 10001 * 11111 * 90090991 * 99009901 * 99990001 * 109889011 * 9999000099990001 * 10099989899000101 * 100009999999899989999000000010001
  115. 3 * 5 * 5 * 29 * 2298850574712643678160919540229885057471264367816091954023 * 199999999999999999999999999999999999999999999999999999999999
  116. 9 * 111 * 11111 * 1111111 * 90090991 * 900900990991 * 900009090090909909099991 * 1109988789001111109989898989900111110998878900111
  117. 11 * 91 * 9091 * 909091 * 769223077 * 156985855573 * 1099988890111109888900011 * 910009191000909089989898989899909091000919100091
  118. 3 * 7 * 7 * 36 * 61 * 167280026764804282368685178989628638340582134493141518903 * 18518518518518518518518518518518518518518518518518518518479