generate_williams_numbers.pl 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 27 August 2022
  4. # https://github.com/trizen
  5. # Generate all the Carmichael numbers with n prime factors in a given range [a,b]. (not in sorted order)
  6. # See also:
  7. # https://en.wikipedia.org/wiki/Almost_prime
  8. # https://trizenx.blogspot.com/2020/08/pseudoprimes-construction-methods-and.html
  9. # PARI/GP program (in range):
  10. # carmichael(A, B, k) = A=max(A, vecprod(primes(k+1))\2); (f(m, l, p, k, u=0, v=0) = my(list=List()); if(k==1, forprime(p=u, v, my(t=m*p); if((t-1)%l == 0 && (t-1)%(p-1) == 0, listput(list, t))), forprime(q = p, sqrtnint(B\m, k), my(t = m*q); my(L=lcm(l, q-1)); if(gcd(L, t) == 1, my(u=ceil(A/t), v=B\t); if(u <= v, my(r=nextprime(q+1)); if(k==2 && r>u, u=r); list=concat(list, f(t, L, r, k-1, u, v)))))); list); vecsort(Vec(f(1, 1, 3, k)));
  11. use 5.020;
  12. use warnings;
  13. use ntheory qw(:all);
  14. use experimental qw(signatures);
  15. use Math::GMPz;
  16. sub divceil ($x,$y) { # ceil(x/y)
  17. my $q = $x/$y;
  18. ($q*$y == $x) ? $q : ($q+1);
  19. }
  20. sub is_pomerance_prime ($p) {
  21. # p == 3 (mod 8) and (5/p) = -1
  22. # is_congruent(p, 3, 8) && (kronecker(5, p) == -1) &&
  23. # (p-1)/2 and (p+1)/4 are squarefree
  24. # is_squarefree((p-1)/2) && is_squarefree((p+1)/4) &&
  25. # all factors q of (p-1)/2 are q == 1 (mod 4)
  26. # factor((p-1)/2).all { |q|
  27. # is_congruent(q, 1, 4)
  28. # } &&
  29. # all factors q of (p+1)/4 are q == 3 (mod 4)
  30. # factor((p+1)/4).all {|q|
  31. # is_congruent(q, 3, 4)
  32. # }
  33. # p == 3 (mod 8)
  34. $p%8 == 3 or return;
  35. # (5/p) = -1
  36. #kronecker(5, $p) == -1 or return;
  37. # (p-1)/2 and (p+1)/4 are squarefree
  38. (is_square_free(($p-1)>>1) and is_square_free(($p+1)>>2)) || return;
  39. # all prime factors q of (p-1)/2 are q == 1 (mod 4)
  40. (vecall { $_%4 == 1 } factor(($p-1)>>1)) || return;
  41. # all prime factors q of (p+1)/4 are q == 3 (mod 4)
  42. (vecall { $_%4 == 3 } factor(($p+1)>>2)) || return;
  43. return 1;
  44. }
  45. #my $prime_file = '../primes/smooth_primes.txt';
  46. my $prime_file = '../primes/nice_primes.txt';
  47. my @prime_list;
  48. open my $fh, '<', $prime_file
  49. or die "Can't open file <<$prime_file>> for reading: $!";
  50. while (<$fh>) {
  51. chomp(my $p = $_);
  52. if ($p > ~0) {
  53. $p = Math::GMPz->new("$p");
  54. }
  55. is_smooth($p-1, 1000) || next;
  56. is_smooth($p+1, 1000) || next;
  57. if (is_pomerance_prime($p)) {
  58. push @prime_list, $p;
  59. }
  60. }
  61. close $fh;
  62. say "# The prime list has ", scalar(@prime_list), " terms";
  63. sub carmichael_numbers_in_range ($A, $B, $k, $callback) {
  64. $A = vecmax($A, pn_primorial($k+1)>>1);
  65. sub ($m, $lambda, $lambda2, $p, $k, $u = undef, $v = undef) {
  66. if ($k == 1) {
  67. say "# Prime $p -> $m -- ($lambda, $lambda2)";
  68. foreach my $p (@prime_list) {
  69. $p < $u and next;
  70. $p > $v and last;
  71. my $t = $m*$p;
  72. if (($t-1)%$lambda == 0 and ($t-1)%($p-1) == 0) {
  73. say "Carmichael: $t";
  74. if (($t+1)%$lambda == 0 and ($t+1)%($p+1) == 0) {
  75. die "Found a Williams number: $t";
  76. $callback->($t);
  77. }
  78. }
  79. }
  80. return;
  81. }
  82. my $y = rootint(divint($B, $m), $k);
  83. my $x = $p;
  84. foreach my $p (@prime_list) {
  85. $p < $x and next;
  86. $p > $y and last;
  87. #is_pomerance_prime($p) || next;
  88. #is_smooth($p+1, 1000) || next;
  89. #is_smooth($p-1, 1000) || next;
  90. my $L = lcm($lambda, $p-1);
  91. gcd($L, $m) == 1 or next;
  92. my $L2 = lcm($lambda2, $p+1);
  93. gcd($L2, $m) == 1 or next;
  94. $L < ~0 or next;
  95. $L2 < ~0 or next;
  96. #say "# Prime: $p -> $m";
  97. # gcd($m*$p, euler_phi($m*$p)) == 1 or die "$m*$p: not cyclic";
  98. my $t = $m*$p;
  99. my $u = divceil($A, $t);
  100. my $v = $B / $t;
  101. if ($u <= $v) {
  102. my $r = next_prime($p);
  103. __SUB__->($t, $L, $L2, $r, $k - 1, (($k==2 && $r>$u) ? $r : $u), $v);
  104. }
  105. }
  106. }->(Math::GMPz->new(1), 1, 1, 3, $k);
  107. }
  108. my $k = 5;
  109. my $from = Math::GMPz->new(2)**64;
  110. my $upto = Math::GMPz->new(10)**20000;
  111. #while (1) {
  112. say "# [$k] Sieving: [$from, $upto]";
  113. carmichael_numbers_in_range($from, $upto, $k, sub ($n) { say $n });
  114. # $from = $upto+1;
  115. # $upto = 2*$from;
  116. #}