sophie_germain_factorization_method.pl 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 26 July 2019
  4. # https://github.com/trizen
  5. # A simple factorization method, based on Sophie Germain's identity:
  6. # x^4 + 4y^4 = (x^2 + 2xy + 2y^2) * (x^2 - 2xy + 2y^2)
  7. # This method is also effective for numbers of the form: n^4 + 4^(2k+1).
  8. # See also:
  9. # https://oeis.org/A227855 -- Numbers of the form x^4 + 4*y^4.
  10. # https://www.quora.com/What-is-Sophie-Germains-Identity
  11. use 5.020;
  12. use strict;
  13. use warnings;
  14. use Math::GMPz;
  15. use ntheory qw(:all);
  16. use experimental qw(signatures);
  17. sub sophie_germain_factorization ($n, $verbose = 0) {
  18. if (ref($n) ne 'Math::GMPz') {
  19. $n = Math::GMPz->new("$n");
  20. }
  21. my $f = sub ($A, $B) {
  22. my @factors;
  23. foreach my $f (
  24. $A**2 + 2 * $B**2 - 2 * $A * $B,
  25. $A**2 + 2 * $B**2 + 2 * $A * $B,
  26. ) {
  27. my $g = Math::GMPz->new(gcd($f, $n));
  28. if ($g > 1 and $g < $n) {
  29. while ($n % $g == 0) {
  30. $n /= $g;
  31. push @factors, $g;
  32. }
  33. }
  34. }
  35. @factors;
  36. };
  37. my $orig = $n;
  38. my @sophie_params;
  39. my $sophie_check_root = sub ($r1) {
  40. {
  41. my $x = 4 * $r1**4;
  42. my $dx = $n - $x;
  43. if (is_power($dx, 4, \my $r2)) {
  44. $r2 = Math::GMPz->new($r2);
  45. say "[*] Sophie Germain special form detected: $r2^4 + 4*$r1^4" if $verbose;
  46. push @sophie_params, [$r2, $r1];
  47. }
  48. }
  49. {
  50. my $y = $r1**4;
  51. my $dy = $n - $y;
  52. if (($dy % 4 == 0) and is_power($dy >> 2, 4, \my $r2)) {
  53. $r2 = Math::GMPz->new($r2);
  54. say "[*] Sophie Germain special form detected: $r1^4 + 4*$r2^4" if $verbose;
  55. push @sophie_params, [$r1, $r2];
  56. }
  57. }
  58. };
  59. # Try to find n = x^4 + 4*y^4, for x or y small.
  60. foreach my $r (map { Math::GMPz->new($_) } 2 .. logint($n, 2)) {
  61. $sophie_check_root->($r);
  62. }
  63. # Try to find n = x^4 + 4*y^4 for x,y close to floor(n/5)^(1/4).
  64. my $k = Math::GMPz->new(rootint($n / 5, 4));
  65. for my $j (0 .. 1000) {
  66. $sophie_check_root->($k + $j);
  67. }
  68. my @factors;
  69. foreach my $args (@sophie_params) {
  70. push @factors, $f->(@$args);
  71. }
  72. push @factors, $orig / vecprod(@factors);
  73. return sort { $a <=> $b } @factors;
  74. }
  75. if (@ARGV) {
  76. say join ', ', sophie_germain_factorization($ARGV[0], 1);
  77. exit;
  78. }
  79. say join ' * ', sophie_germain_factorization(powint(43, 4) + 4 * powint(372485613, 4));
  80. say join ' * ', sophie_germain_factorization(powint(372485613, 4) + 4 * powint(97, 4));
  81. say join ' * ', sophie_germain_factorization(powint(372485613, 4) + 4 * powint(372485629, 4));
  82. say join ' * ', sophie_germain_factorization(powint(372485629, 4) + 4 * powint(372485511, 4));
  83. say '';
  84. say join ' * ', sophie_germain_factorization(powint(4, 117) + powint(53, 4));
  85. say join ' * ', sophie_germain_factorization(powint(4, 213) + powint(240, 4));
  86. say join ' * ', sophie_germain_factorization(powint(4, 251) + powint(251, 4));
  87. __END__
  88. 277491031750210669 * 277491095817736105
  89. 138745459629795665 * 138745604154213509
  90. 138745543811525897 * 693727695218548205
  91. 138745455904945045 * 693727455337830721
  92. 166153499473114453560556010453601017 * 166153499473114514665395754616490745
  93. 13164036458569648337239753460419861813422875717854660184319779072 * 13164036458569648337239753460497746266300898132282617629258080512
  94. 3618502788666131106986593281521497099061968496512379043906292883903830095385 * 3618502788666131106986593281521497141767405545090156208559806116590740633113