fermat_pseudoprimes_in_range.pl 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 31 August 2022
  4. # https://github.com/trizen
  5. # Generate all the k-omega Fermat pseudoprimes in range [a,b]. (not in sorted order)
  6. # Definition:
  7. # k-omega primes are numbers n such that omega(n) = k.
  8. # See also:
  9. # https://en.wikipedia.org/wiki/Almost_prime
  10. # https://en.wikipedia.org/wiki/Prime_omega_function
  11. # https://trizenx.blogspot.com/2020/08/pseudoprimes-construction-methods-and.html
  12. # PARI/GP program (slow):
  13. # fermat_psp(A, B, k, base) = A=max(A, vecprod(primes(k))); (f(m, l, p, j) = my(list=List()); forprime(q=p, sqrtnint(B\m, j), if(base%q != 0, my(v=m*q, t=q, r=nextprime(q+1)); while(v <= B, my(L=lcm(l, znorder(Mod(base, t)))); if(gcd(L, v) == 1, if(j==1, if(v>=A && if(k==1, !isprime(v), 1) && (v-1)%L == 0, listput(list, v)), if(v*r <= B, list=concat(list, f(v, L, r, j-1)))), break); v *= q; t *= q))); list); vecsort(Vec(f(1, 1, 2, k)));
  14. # PARI/GP program (fast):
  15. # fermat_psp(A, B, k, base) = A=max(A, vecprod(primes(k))); (f(m, l, lo, k) = my(list=List()); my(hi=sqrtnint(B\m, k)); if(lo > hi, return(list)); if(k==1, forstep(p=lift(1/Mod(m, l)), hi, l, if(isprimepower(p) && gcd(m*base, p) == 1, my(n=m*p); if(n >= A && (n-1) % znorder(Mod(base, p)) == 0, listput(list, n)))), forprime(p=lo, hi, base%p == 0 && next; my(z=znorder(Mod(base, p))); gcd(m,z) == 1 || next; my(q=p, v=m*p); while(v <= B, list=concat(list, f(v, lcm(l, z), p+1, k-1)); q *= p; Mod(base, q)^z == 1 || break; v *= p))); list); vecsort(Set(f(1, 1, 2, k)));
  16. use 5.036;
  17. use warnings;
  18. use ntheory qw(:all);
  19. sub divceil ($x, $y) { # ceil(x/y)
  20. (($x % $y == 0) ? 0 : 1) + divint($x, $y);
  21. }
  22. sub fermat_pseudoprimes_in_range ($A, $B, $k, $base) {
  23. $A = vecmax($A, pn_primorial($k));
  24. my %seen;
  25. my @list;
  26. sub ($m, $L, $lo, $j) {
  27. my $hi = rootint(divint($B, $m), $j);
  28. if ($lo > $hi) {
  29. return;
  30. }
  31. if ($j == 1) {
  32. if ($L == 1) { # optimization
  33. foreach my $p (@{primes($lo, $hi)}) {
  34. $base % $p == 0 and next;
  35. for (my $v = (($m == 1) ? ($p * $p) : ($m * $p)) ; $v <= $B ; $v *= $p) {
  36. $v >= $A or next;
  37. powmod($base, $v - 1, $v) == 1 or last;
  38. push(@list, $v) if !$seen{$v}++;
  39. }
  40. }
  41. return;
  42. }
  43. my $t = invmod($m, $L);
  44. $t > $hi && return;
  45. $t += $L * divceil($lo - $t, $L) if ($t < $lo);
  46. for (my $p = $t ; $p <= $hi ; $p += $L) {
  47. if (is_prime_power($p) and gcd($m, $p) == 1 and gcd($base, $p) == 1) {
  48. my $v = $m * $p;
  49. $v >= $A or next;
  50. ($v - 1) % znorder($base, $p) == 0 or next;
  51. #powmod($base, $v-1, $v) == 1 or next;
  52. push(@list, $v) if !$seen{$v}++;
  53. }
  54. }
  55. return;
  56. }
  57. foreach my $p (@{primes($lo, $hi)}) {
  58. $base % $p == 0 and next;
  59. my $z = znorder($base, $p);
  60. gcd($m, $z) == 1 or next;
  61. for (my ($q, $v) = ($p, $m * $p) ; $v <= $B ; ($q, $v) = ($q * $p, $v * $p)) {
  62. if ($q > $p) {
  63. powmod($base, $z, $q) == 1 or last;
  64. }
  65. __SUB__->($v, lcm($L, $z), $p + 1, $j - 1);
  66. }
  67. }
  68. }
  69. ->(1, 1, 2, $k);
  70. return sort { $a <=> $b } @list;
  71. }
  72. # Generate all the Fermat pseudoprimes to base 3 in range [1, 10^5]
  73. my $from = 1;
  74. my $upto = 1e5;
  75. my $base = 3;
  76. my @arr;
  77. foreach my $k (1 .. 100) {
  78. last if pn_primorial($k) > $upto;
  79. push @arr, fermat_pseudoprimes_in_range($from, $upto, $k, $base);
  80. }
  81. say join(', ', sort { $a <=> $b } @arr);
  82. # Run some tests
  83. if (0) { # true to run some tests
  84. foreach my $k (1 .. 5) {
  85. say "Testing k = $k";
  86. my $lo = pn_primorial($k);
  87. my $hi = mulint($lo, 1000);
  88. my $omega_primes = omega_primes($k, $lo, $hi);
  89. foreach my $base (2 .. 100) {
  90. my @this = grep { is_pseudoprime($_, $base) and !is_prime($_) } @$omega_primes;
  91. my @that = fermat_pseudoprimes_in_range($lo, $hi, $k, $base);
  92. join(' ', @this) eq join(' ', @that)
  93. or die "Error for k = $k and base = $base with hi = $hi\n(@this) != (@that)";
  94. }
  95. }
  96. }
  97. __END__
  98. 91, 121, 286, 671, 703, 949, 1105, 1541, 1729, 1891, 2465, 2665, 2701, 2821, 3281, 3367, 3751, 4961, 5551, 6601, 7381, 8401, 8911, 10585, 11011, 12403, 14383, 15203, 15457, 15841, 16471, 16531, 18721, 19345, 23521, 24046, 24661, 24727, 28009, 29161, 29341, 30857, 31621, 31697, 32791, 38503, 41041, 44287, 46657, 46999, 47197, 49051, 49141, 50881, 52633, 53131, 55261, 55969, 63139, 63973, 65485, 68887, 72041, 74593, 75361, 76627, 79003, 82513, 83333, 83665, 87913, 88561, 88573, 88831, 90751, 93961, 96139, 97567