prog.pl 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #!/usr/bin/perl
  2. # Smallest base-n strong Fermat pseudoprime with n distinct prime factors.
  3. # https://oeis.org/A271874
  4. # Known terms:
  5. # 2047, 8911, 129921, 381347461, 333515107081, 37388680793101, 713808066913201, 665242007427361, 179042026797485691841, 8915864307267517099501, 331537694571170093744101, 2359851544225139066759651401, 17890806687914532842449765082011
  6. =for comment
  7. # PARI/GP program:
  8. strong_check(p, base, e, r) = my(tv=valuation(p-1, 2)); tv > e && Mod(base, p)^((p-1)>>(tv-e)) == r;
  9. strong_fermat_psp(A, B, k, base) = A=max(A, vecprod(primes(k))); (f(m, l, lo, k, e, r) = 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 && strong_check(p, base, e, r), 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; strong_check(p, base, e, r) || 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, e, r)); q *= p; Mod(base, q)^z == 1 || break; v *= p))); list); my(res=f(1, 1, 2, k, 0, 1)); for(v=0, logint(B, 2), res=concat(res, f(1, 1, 2, k, v, -1))); vecsort(Set(res));
  10. a(n) = if(n < 2, return()); my(x=vecprod(primes(n)), y=2*x); while(1, my(v=strong_fermat_psp(x, y, n, n)); if(#v >= 1, return(v[1])); x=y+1; y=2*x); \\ ~~~~
  11. =cut
  12. # It takes 25 minutes to find the terms a(2)-a(13).
  13. # Lower-bounds:
  14. # a(14) > 12428336051357263377401993232370
  15. # Upper-bounds:
  16. # a(14) <= 17890806687914532842449765082011
  17. # It took 8 hours to find a(14).
  18. use 5.036;
  19. use Math::GMPz;
  20. use ntheory qw(:all);
  21. sub strong_fermat_pseudoprimes_in_range ($A, $B, $k, $base, $callback) {
  22. $A = vecmax($A, pn_primorial($k));
  23. $A = Math::GMPz->new("$A");
  24. $B = Math::GMPz->new("$B");
  25. my $u = Math::GMPz::Rmpz_init();
  26. my $v = Math::GMPz::Rmpz_init();
  27. my %seen;
  28. my $generator = sub ($m, $L, $lo, $j, $k_exp, $congr) {
  29. Math::GMPz::Rmpz_tdiv_q($u, $B, $m);
  30. Math::GMPz::Rmpz_root($u, $u, $j);
  31. my $hi = Math::GMPz::Rmpz_get_ui($u);
  32. if ($lo > $hi) {
  33. return;
  34. }
  35. if ($j == 1) {
  36. Math::GMPz::Rmpz_invert($v, $m, $L);
  37. if (Math::GMPz::Rmpz_cmp_ui($v, $hi) > 0) {
  38. return;
  39. }
  40. if (Math::GMPz::Rmpz_fits_ulong_p($L)) {
  41. $L = Math::GMPz::Rmpz_get_ui($L);
  42. }
  43. my $t = Math::GMPz::Rmpz_get_ui($v);
  44. $t > $hi && return;
  45. $t += $L while ($t < $lo);
  46. for (my $p = $t ; $p <= $hi ; $p += $L) {
  47. if (is_prime_power($p) and Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $m, $p) == 1 and gcd($base, $p) == 1) {
  48. my $val = valuation($p - 1, 2);
  49. $val > $k_exp or next;
  50. powmod($base, ($p - 1) >> ($val - $k_exp), $p) == ($congr % $p) or next;
  51. Math::GMPz::Rmpz_mul_ui($v, $m, $p);
  52. if ($k == 1 and is_prime($p) and Math::GMPz::Rmpz_cmp_ui($m, 1) == 0) {
  53. ## ok
  54. }
  55. elsif (Math::GMPz::Rmpz_cmp($v, $A) >= 0) {
  56. Math::GMPz::Rmpz_sub_ui($u, $v, 1);
  57. if (Math::GMPz::Rmpz_divisible_ui_p($u, znorder($base, $p))) {
  58. if (!$seen{Math::GMPz::Rmpz_get_str($v, 10)}++) {
  59. my $value = Math::GMPz::Rmpz_init_set($v);
  60. $B = $value if ($value < $B);
  61. say "Found upper-bound: $value";
  62. $callback->($value);
  63. }
  64. }
  65. }
  66. }
  67. }
  68. return;
  69. }
  70. my $u = Math::GMPz::Rmpz_init();
  71. my $v = Math::GMPz::Rmpz_init();
  72. my $lcm = Math::GMPz::Rmpz_init();
  73. foreach my $p (@{primes($lo, $hi)}) {
  74. $base % $p == 0 and next;
  75. my $val = valuation($p - 1, 2);
  76. $val > $k_exp or next;
  77. powmod($base, ($p - 1) >> ($val - $k_exp), $p) == ($congr % $p) or next;
  78. my $z = znorder($base, $p);
  79. Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $m, $z) == 1 or next;
  80. Math::GMPz::Rmpz_lcm_ui($lcm, $L, $z);
  81. Math::GMPz::Rmpz_set_ui($u, $p);
  82. for (Math::GMPz::Rmpz_mul_ui($v, $m, $p) ;
  83. Math::GMPz::Rmpz_cmp($v, $B) <= 0 ;
  84. Math::GMPz::Rmpz_mul_ui($v, $v, $p)) {
  85. __SUB__->($v, $lcm, $p + 1, $j - 1, $k_exp, $congr);
  86. Math::GMPz::Rmpz_mul_ui($u, $u, $p);
  87. powmod($base, $z, $u) == 1 or last;
  88. }
  89. }
  90. };
  91. # Case where 2^d == 1 (mod p), where d is the odd part of p-1.
  92. $generator->(Math::GMPz->new(1), Math::GMPz->new(1), 2, $k, 0, 1);
  93. # Cases where 2^(d * 2^v) == -1 (mod p), for some v >= 0.
  94. foreach my $v (reverse(0 .. logint($B, 2))) {
  95. $generator->(Math::GMPz->new(1), Math::GMPz->new(1), 2, $k, $v, -1);
  96. }
  97. }
  98. sub a($n) {
  99. say "Searching for a($n)";
  100. my $x = pn_primorial($n);
  101. my $y = 2*$x;
  102. #$x = Math::GMPz->new("12428336051357263377401993232370");
  103. #$y = Math::GMPz->new("17890806687914532842449765082011");
  104. $x = Math::GMPz->new("$x");
  105. $y = Math::GMPz->new("$y");
  106. for (;;) {
  107. say "Sieving range: [$x, $y]";
  108. my @arr;
  109. strong_fermat_pseudoprimes_in_range($x, $y, $n, $n, sub($v) { push @arr, $v });
  110. if (@arr) {
  111. @arr = sort {$a <=> $b} @arr;
  112. return $arr[0];
  113. }
  114. $x = $y+1;
  115. $y = 2*$x;
  116. }
  117. }
  118. foreach my $n (10) {
  119. say "a($n) = ", a($n);
  120. }
  121. __END__
  122. a(2) = 2047
  123. a(3) = 8911
  124. a(4) = 129921
  125. a(5) = 381347461
  126. a(6) = 333515107081
  127. a(7) = 37388680793101
  128. a(8) = 713808066913201
  129. a(9) = 665242007427361
  130. a(10) = 179042026797485691841
  131. a(11) = 8915864307267517099501
  132. a(12) = 331537694571170093744101
  133. a(13) = 2359851544225139066759651401
  134. a(14) = 17890806687914532842449765082011