lucas_psp_from_lambdas.pl 3.5 KB

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