prog_faster.pl 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 24 September 2022
  4. # https://github.com/trizen
  5. # Carmichael numbers which are also base-2 strong pseudoprimes.
  6. # https://oeis.org/A063847
  7. # Let a(n) be the smallest Carmichael number with n prime factors that is also a strong pseudoprime to base 2.
  8. # https://oeis.org/A356866
  9. # First few terms:
  10. # 15841, 5310721, 440707345, 10761055201, 5478598723585, 713808066913201, 1022751992545146865, 5993318051893040401
  11. # New terms found (24 September 2022):
  12. # a(11) = 120459489697022624089201
  13. # a(12) = 27146803388402594456683201
  14. # New terms: (1st October 2022):
  15. # a(13) = 14889929431153115006659489681
  16. # Lower-bounds:
  17. # a(14) > 2693624541501640513291894811443
  18. # Finding a(13) took 1 hour and 34 minutes. (version 1 -- slow)
  19. # Timings for finding a(11):
  20. # version 1: took 4 minutes
  21. # version 2: took 20 seconds
  22. # Version 2 took 1 minute to find a(12).
  23. # Version 2 took 5 minutes to find a(13).
  24. # Upper-bounds:
  25. # a(14) <= 12119528395859597855693434006201 < 12901146646893310291414909176001
  26. # a(15) <= 8445045464974686705830286862791601
  27. # a(16) <= 431963846549014459308449974667236801
  28. # a(17) <= 467214942206286886822015370137826526001
  29. # a(18) <= 1249878762341814636782407094268125017522801
  30. # a(19) <= 4590172857833958394304163760489663619756066401
  31. # a(20) <= 179969791023878308369431665851191959700006574801
  32. # a(21) <= 107735170264024836555220903560040388670030679315201
  33. =for comment
  34. # PARI/GP programs:
  35. # Version 1
  36. carmichael_strong_psp(A, B, k, base) = A=max(A, vecprod(primes(k+1))\2); (f(m, l, p, k, k_exp, congr, u=0, v=0) = my(list=List()); if(k==1, forprime(q=u, v, my(t=m*q); if((t-1)%l == 0 && (t-1)%(q-1) == 0, my(tv=valuation(q-1, 2)); if(tv > k_exp && Mod(base, q)^(((q-1)>>tv)<<k_exp) == congr, listput(list, t)))), forprime(q = p, sqrtnint(B\m, k), if(base%q != 0, my(tv=valuation(q-1, 2)); if(tv > k_exp && Mod(base, q)^(((q-1)>>tv)<<k_exp) == congr, my(L=lcm(l, q-1)); if(gcd(L, m) == 1, my(t = m*q, 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, k_exp, congr, u, v)))))))); list); my(res=f(1, 1, 3, k, 0, 1)); for(v=0, logint(B, 2), res=concat(res, f(1, 1, 3, k, v, -1))); vecsort(Vec(res));
  37. a(n,base=2) = if(n < 3, return()); my(x=vecprod(primes(n+1))\2,y=2*x); while(1, my(v=carmichael_strong_psp(x,y,n,base)); if(#v >= 1, return(v[1])); x=y+1; y=2*x); \\ ~~~~
  38. =cut
  39. use 5.036;
  40. use ntheory qw(:all);
  41. use Math::GMPz;
  42. sub strong_carmichael_in_range ($A, $B, $k, $base, $callback) {
  43. $A = vecmax($A, Math::GMPz->new(pn_primorial($k)));
  44. $A = Math::GMPz->new("$A");
  45. $B = Math::GMPz->new("$B");
  46. $A > $B and return;
  47. my $u = Math::GMPz::Rmpz_init();
  48. my $v = Math::GMPz::Rmpz_init();
  49. # max_p = floor((1 + sqrt(8*B + 1))/4)
  50. my $max_p = Math::GMPz::Rmpz_init();
  51. Math::GMPz::Rmpz_mul_2exp($max_p, $B, 3);
  52. Math::GMPz::Rmpz_add_ui($max_p, $max_p, 1);
  53. Math::GMPz::Rmpz_sqrt($max_p, $max_p);
  54. Math::GMPz::Rmpz_add_ui($max_p, $max_p, 1);
  55. Math::GMPz::Rmpz_div_2exp($max_p, $max_p, 2);
  56. $max_p = Math::GMPz::Rmpz_get_ui($max_p) if Math::GMPz::Rmpz_fits_ulong_p($max_p);
  57. my $generator = sub ($m, $L, $lo, $k, $k_exp, $congr) {
  58. Math::GMPz::Rmpz_tdiv_q($u, $B, $m);
  59. Math::GMPz::Rmpz_root($u, $u, $k);
  60. Math::GMPz::Rmpz_fits_ulong_p($u) || die "Too large value!";
  61. my $hi = Math::GMPz::Rmpz_get_ui($u);
  62. if ($k == 1 and $max_p < $hi) {
  63. $hi = $max_p;
  64. }
  65. if ($lo > $hi) {
  66. return;
  67. }
  68. if ($k == 1) {
  69. Math::GMPz::Rmpz_cdiv_q($u, $A, $m);
  70. if (Math::GMPz::Rmpz_fits_ulong_p($u)) {
  71. $lo = vecmax($lo, Math::GMPz::Rmpz_get_ui($u));
  72. }
  73. elsif (Math::GMPz::Rmpz_cmp_ui($u, $lo) > 0) {
  74. if (Math::GMPz::Rmpz_cmp_ui($u, $hi) > 0) {
  75. return;
  76. }
  77. $lo = Math::GMPz::Rmpz_get_ui($u);
  78. }
  79. if ($lo > $hi) {
  80. return;
  81. }
  82. Math::GMPz::Rmpz_invert($v, $m, $L);
  83. if (Math::GMPz::Rmpz_cmp_ui($v, $hi) > 0) {
  84. return;
  85. }
  86. if (Math::GMPz::Rmpz_fits_ulong_p($L)) {
  87. $L = Math::GMPz::Rmpz_get_ui($L);
  88. }
  89. else {
  90. warn "Lambda is large: $L for m = $m with ($lo, $hi)\n";
  91. }
  92. my $t = Math::GMPz::Rmpz_get_ui($v);
  93. $t > $hi && return;
  94. $t += $L while ($t < $lo);
  95. for (my $p = $t ; $p <= $hi ; $p += $L) {
  96. if (is_prime($p)) {
  97. my $valuation = valuation($p - 1, 2);
  98. if ($valuation > $k_exp and powmod($base, ($p - 1) >> ($valuation - $k_exp), $p) == ($congr % $p)) {
  99. Math::GMPz::Rmpz_mul_ui($v, $m, $p);
  100. Math::GMPz::Rmpz_sub_ui($u, $v, 1);
  101. if (Math::GMPz::Rmpz_divisible_ui_p($u, $p - 1)) {
  102. my $term = Math::GMPz::Rmpz_init_set($v);
  103. say "# Found upper-bound: $term";
  104. $B = $term if ($term < $B);
  105. $callback->($term);
  106. }
  107. }
  108. }
  109. }
  110. return;
  111. }
  112. my $z = Math::GMPz::Rmpz_init();
  113. my $lcm = Math::GMPz::Rmpz_init();
  114. foreach my $p (@{primes($lo, $hi)}) {
  115. $base % $p == 0 and next;
  116. Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $m, $p - 1) == 1 or next;
  117. my $valuation = valuation($p - 1, 2);
  118. $valuation > $k_exp or next;
  119. powmod($base, ($p - 1) >> ($valuation - $k_exp), $p) == ($congr % $p) or next;
  120. #is_smooth($p-1, 17) || next;
  121. Math::GMPz::Rmpz_mul_ui($z, $m, $p);
  122. Math::GMPz::Rmpz_lcm_ui($lcm, $L, $p-1);
  123. __SUB__->($z, $lcm, $p + 1, $k - 1, $k_exp, $congr);
  124. }
  125. };
  126. say "# Sieving range: [$A, $B]";
  127. # Cases where 2^(d * 2^v) == -1 (mod p), for some v >= 0.
  128. foreach my $v (reverse(0 .. logint($B, 2))) {
  129. $generator->(Math::GMPz->new(1), Math::GMPz->new(1), 2, $k, $v, -1);
  130. }
  131. # Case where 2^d == 1 (mod p), where d is the odd part of p-1.
  132. $generator->(Math::GMPz->new(1), Math::GMPz->new(1), 2, $k, 0, 1);
  133. }
  134. my $k = 14;
  135. my $from = Math::GMPz->new(pn_primorial($k));
  136. $from = Math::GMPz->new("2693624541501640513291894811443");
  137. my $upto = 3 * $from;
  138. while (1) {
  139. my @found;
  140. strong_carmichael_in_range($from, $upto, $k, 2, sub ($n) { push @found, $n });
  141. if (@found) {
  142. @found = sort { $a <=> $b } @found;
  143. #say "Terms: @found";
  144. say "a($k) = $found[0]";
  145. last;
  146. }
  147. $from = $upto + 1;
  148. $upto = 3 * $from;
  149. }
  150. __END__
  151. a(3) = 15841
  152. a(4) = 5310721
  153. a(5) = 440707345
  154. a(6) = 10761055201
  155. a(7) = 5478598723585
  156. a(8) = 713808066913201
  157. a(9) = 1022751992545146865
  158. a(10) = 5993318051893040401
  159. a(11) = 120459489697022624089201
  160. a(12) = 27146803388402594456683201
  161. a(13) = 14889929431153115006659489681