fermat_from_lambdas.pl 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #!/usr/bin/perl
  2. # Erdos construction method for Fermat pseudoprimes:
  3. # 1. Choose an even integer L with many divisors.
  4. # 2. Let P be the set of primes p such that p-1 divides L and p does not divide L.
  5. # 3. Find a subset S of P such that n = prod(S) satisfies 2^(n-1) == 1 (mod n).
  6. use 5.020;
  7. use warnings;
  8. use ntheory qw(:all);
  9. use List::Util qw(uniq);
  10. use experimental qw(signatures);
  11. sub lambda_primes ($L) {
  12. # Primes p such that `p - kronecker(D,p)` divides L and p does not divide L.
  13. my $sigma0 = divisor_sum($L, 0);
  14. $sigma0 < 1e5 or return;
  15. my @divisors = divisors($L);
  16. my @A = grep {
  17. ($_ > 2)
  18. and (modint($L, $_) != 0)
  19. #and (modint($_, 8) == 3)
  20. and is_prime($_)
  21. #and (kronecker(5,$_) == -1)
  22. #and (kronecker(-7,$_) == -1)
  23. #and (kronecker(-11,$_) == -1)
  24. #and is_smooth(addint($_, 1), 50)
  25. } map { addint($_, 1) } @divisors;
  26. return @A;
  27. }
  28. sub fermat_pseudoprimes ($L) { # smallest numbers first
  29. my $max = 1e4;
  30. my $max_k = 15;
  31. my @P = lambda_primes($L);
  32. my @orig = @P;
  33. foreach my $k (3 .. (@P >> 1)) {
  34. last if ($k > $max_k);
  35. ($k % 2 == 1) or next;
  36. my $count = 0;
  37. forcomb {
  38. my $n = Math::Prime::Util::GMP::vecprod(@P[@_]);
  39. if ($n > ~0 and Math::Prime::Util::GMP::is_pseudoprime($n, 2)) {
  40. say $n;
  41. }
  42. lastfor if (++$count > $max);
  43. } scalar(@P), $k;
  44. next if ($count < $max);
  45. @P = reverse(@P);
  46. $count = 0;
  47. forcomb {
  48. my $n = Math::Prime::Util::GMP::vecprod(@P[@_]);
  49. if ($n > ~0 and Math::Prime::Util::GMP::is_pseudoprime($n, 2)) {
  50. say $n;
  51. }
  52. lastfor if (++$count > $max);
  53. } scalar(@P), $k;
  54. }
  55. return;
  56. @P = @orig;
  57. my $len = scalar(@P);
  58. my $t = Math::Prime::Util::GMP::vecprod(@P);
  59. foreach my $k (1 .. (@P >> 1)) {
  60. last if ($k > $max_k);
  61. (($len - $k) % 2) == 1 or next;
  62. my $count = 0;
  63. forcomb {
  64. my $n = Math::Prime::Util::GMP::divint($t, Math::Prime::Util::GMP::vecprod(@P[@_]));
  65. if ($n > ~0 and Math::Prime::Util::GMP::is_pseudoprime($n, 2)) {
  66. say $n;
  67. }
  68. lastfor if (++$count > $max);
  69. } scalar(@P), $k;
  70. next if ($count < $max);
  71. @P = reverse(@P);
  72. $count = 0;
  73. forcomb {
  74. my $n = Math::Prime::Util::GMP::divint($t, Math::Prime::Util::GMP::vecprod(@P[@_]));
  75. if ($n > ~0 and Math::Prime::Util::GMP::is_pseudoprime($n, 2)) {
  76. say $n;
  77. }
  78. lastfor if (++$count > $max);
  79. } scalar(@P), $k;
  80. }
  81. }
  82. #~ foreach my $n(2..1e6) {
  83. #~ $n%2 == 0 or next;
  84. #~ lucas_pseudoprimes($n);
  85. #~ }
  86. #~ __END__
  87. while (<>) {
  88. chomp;
  89. #next if ($_ < 1e7);
  90. #next if ($_ < ~0);
  91. fermat_pseudoprimes($_);
  92. }
  93. __END__
  94. foreach my $n(2..1e6) {
  95. $n % 2 == 0 or next;
  96. lucas_pseudoprimes($n);
  97. }