carmichael_numbers_in_range_from_prime_factors.pl 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 25 September 2022
  4. # https://github.com/trizen
  5. # Generate all the Carmichael numbers with n prime factors in a given range [A,B], using a given list of prime factors. (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. use 5.020;
  10. use warnings;
  11. use ntheory qw(:all);
  12. use experimental qw(signatures);
  13. use List::Util qw(uniq);
  14. sub divceil ($x, $y) { # ceil(x/y)
  15. (($x % $y == 0) ? 0 : 1) + divint($x, $y);
  16. }
  17. sub carmichael_numbers_in_range ($A, $B, $k, $primes, $callback) {
  18. $A = vecmax($A, pn_primorial($k));
  19. # Largest possisble prime factor for Carmichael numbers <= B
  20. my $max_p = (1 + sqrtint(8*$B + 1))>>2;
  21. my @P = sort { $a <=> $b } grep { $_ <= $max_p } uniq(@$primes);
  22. my $end = $#P;
  23. sub ($m, $lambda, $j, $k) {
  24. my $y = vecmin($max_p, rootint(divint($B, $m), $k));
  25. if ($k == 1) {
  26. my $x = divceil($A, $m);
  27. if ($P[-1] < $x) {
  28. return;
  29. }
  30. foreach my $i ($j .. $end) {
  31. my $p = $P[$i];
  32. last if ($p > $y);
  33. next if ($p < $x);
  34. my $t = $m * $p;
  35. if (($t - 1) % $lambda == 0 and ($t - 1) % ($p - 1) == 0) {
  36. $callback->($t);
  37. }
  38. }
  39. return;
  40. }
  41. foreach my $i ($j .. $end) {
  42. my $p = $P[$i];
  43. last if ($p > $y);
  44. gcd($m, $p - 1) == 1 or next;
  45. # gcd($m*$p, euler_phi($m*$p)) == 1 or die "$m*$p: not cyclic";
  46. __SUB__->($m * $p, lcm($lambda, $p - 1), $i + 1, $k - 1);
  47. }
  48. }
  49. ->(1, 1, 0, $k);
  50. }
  51. my $lambda = 5040;
  52. my @primes = grep { $_ > 2 and $lambda % $_ != 0 and is_prime($_) } map { $_ + 1 } divisors($lambda);
  53. foreach my $k (3 .. 6) {
  54. my @arr;
  55. carmichael_numbers_in_range(1, 10**(2 * $k), $k, \@primes, sub ($n) { push @arr, $n });
  56. say "$k: ", join(', ', sort { $a <=> $b } @arr);
  57. }
  58. __END__
  59. 3: 29341, 115921, 399001, 488881
  60. 4: 75361, 552721, 852841, 1569457, 3146221, 5310721, 8927101, 12262321, 27402481, 29020321, 49333201, 80282161
  61. 5: 10877581, 18162001, 67994641, 75151441, 76595761, 129255841, 133205761, 140241361, 169570801, 311388337, 461854261, 548871961, 561777121, 568227241, 577240273, 609865201, 631071001, 765245881, 839275921, 1583582113, 2178944461, 2443829641, 2811315361, 3240392401, 3245477761, 3246238801, 3630291841, 4684846321, 4885398001, 5961977281, 6030849889, 7261390081, 7906474801, 9722094481, 9825933601
  62. 6: 496050841, 832060801, 868234081, 1256855041, 1676641681, 1698623641, 1705470481, 1932608161, 2029554241, 2111416021, 3722793481, 4579461601, 5507520481, 5990940901, 7192589041, 7368233041, 8221139641, 13907587681, 16596266401, 19167739921, 22374999361, 23796818641, 29397916801, 33643718641, 41778063601, 42108575041, 47090317681, 48537130321, 53365160521, 54173581561, 57627937081, 57840264721, 60769467361, 66940720561, 74382893761, 74513421361, 77005913041, 77494371361, 84552825841, 88968511801, 94267516561, 97894836481, 107729884081, 112180797121, 114659813521, 126110113921, 126631194481, 131056332121, 142101232561, 152222039761, 167836660321, 169456971601, 171414489961, 174294847441, 187443219601, 193051454401, 207928264321, 225607349521, 237902646241, 244357656481, 297973194121, 314190832033, 329236460281, 330090228721, 335330503201, 494544949921, 507455393761, 582435435457, 638204086801, 639883767601, 643919472001, 672941621521, 725104658881, 810976375441, 866981525761