erdos_carmichael_with_prefix_from_lambda.pl 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. #!/usr/bin/perl
  2. # Erdos construction method for Carmichael numbers:
  3. # 1. Choose an even integer L with many prime factors.
  4. # 2. Let P be the set of primes d+1, where d|L and d+1 does not divide L.
  5. # 3. Find a subset S of P such that prod(S) == 1 (mod L). Then prod(S) is a Carmichael number.
  6. # Alternatively:
  7. # 3. Find a subset S of P such that prod(S) == prod(P) (mod L). Then prod(P) / prod(S) is a Carmichael number.
  8. use 5.020;
  9. use warnings;
  10. use ntheory qw(:all);
  11. use List::Util qw(shuffle);
  12. use experimental qw(signatures);
  13. # Modular product of a list of integers
  14. sub vecprodmod ($arr, $mod) {
  15. #~ if ($mod > ~0) {
  16. #~ my $prod = Math::GMPz->new(1);
  17. #~ foreach my $k(@$arr) {
  18. #~ $prod = ($prod * $k) % $mod;
  19. #~ }
  20. #~ return $prod;
  21. #~ }
  22. if ($mod < ~0) {
  23. my $prod = 1;
  24. foreach my $k(@$arr) {
  25. $prod = mulmod($prod, $k, $mod);
  26. }
  27. return $prod;
  28. }
  29. my $prod = 1;
  30. foreach my $k (@$arr) {
  31. $prod = Math::Prime::Util::GMP::mulmod($prod, $k, $mod);
  32. }
  33. #Math::GMPz->new($prod);
  34. Math::GMPz::Rmpz_init_set_str($prod, 10);
  35. }
  36. # Primes p such that p-1 divides L and p does not divide L
  37. sub lambda_primes ($L) {
  38. #grep { ($L % $_) != 0 } grep { $_ > 2 and is_prime($_) } map { $_ + 1 } divisors($L);
  39. grep { ($_ > 2) and (($L % $_) != 0) and is_prime($_) } map { ($_ >= ~0) ? (Math::GMPz->new($_)+1) : ($_ + 1) } divisors($L);
  40. }
  41. #my @prefix = (3, 5, 17, 23, 29, 53, 83, 89);
  42. #my @prefix = (3,5,17,23, 29);
  43. #my @prefix = (3, 5, 17, 23, 29, 53, 83, 89);
  44. #my @prefix = (3, 5, 17, 23, 29, 53, 83, 89, 113, 353, 617, 2003, 2549);
  45. #my @prefix = (3, 5, 17, 23, 29, 53, 83, 89, 113, 353, 617);
  46. #my @prefix = (3, 5, 17, 23, 29, 53, 83, 89, 113, 419, 449, 617);
  47. #my @prefix = (3, 5, 17, 23, 29, 53, 83, 89, 113, 257, 353, 449, 617, 1409, 2003);
  48. #my @prefix = (3, 5, 17, 23, 29, 53, 83, 89, 113, 197, 353, 449, 617, 1409, 2003);
  49. #~ [3, 5, 17, 23, 29, 53, 83, 89, 113, 197, 257, 353, 617, 1409, 2003, 2549, 3137, 9857, 10193, 16073, 68993, 202049, 275969, 1500929, 18386369]
  50. #~ [3, 5, 17, 23, 29, 53, 83, 89, 113, 197, 353, 449, 617, 2003, 2297, 2549, 3137, 9857, 10193, 68993, 88397, 93809, 5850209, 8044037, 18386369]
  51. #~ [3, 5, 17, 23, 29, 53, 83, 89, 113, 197, 353, 449, 617, 2003, 2549, 3137, 4019, 4289, 10193, 16073, 21977, 38459, 50513, 52529, 76649, 93809, 97553]
  52. #~ [3, 5, 17, 23, 29, 53, 83, 89, 113, 197, 353, 449, 617, 1409, 2003, 2549, 8009, 9857, 23297, 50513, 68993, 275969, 375233, 1500929, 3232769, 18386369]
  53. #~ [3, 5, 17, 23, 29, 53, 83, 89, 113, 197, 353, 449, 617, 1409, 2003, 2297, 2549, 3329, 8009, 10193, 16073, 23297, 50177, 93809, 202049, 275969, 656657, 18386369]
  54. #~ [3, 5, 17, 23, 29, 53, 83, 89, 113, 257, 353, 449, 617, 1409, 2003, 2297, 2549, 3137, 3329, 4019, 7547, 9857, 10193, 16073, 17837, 23297, 68993, 88397, 93809, 202049, 896897, 1500929, 2475089, 8044037, 18386369]
  55. my @prefix = (3, 5, 17, 23, 29, 53, 83, 89, 113, 197, 257, 353, 449, 617, 1409, 2003, 2297, 2549, 3137, 3329, 4019);
  56. #~ [3, 5, 17, 23, 29, 53, 83, 89, 113, 197, 353, 449, 617, 2003, 2297, 2549, 3137, 9857, 10193, 68993, 88397, 93809, 5850209, 8044037, 18386369]
  57. #~ [3, 5, 17, 23, 29, 53, 83, 89, 113, 197, 353, 449, 617, 2003, 2549, 3137, 4019, 4289, 10193, 16073, 21977, 38459, 50513, 52529, 76649, 93809, 97553]
  58. #~ [3, 5, 17, 23, 29, 53, 83, 89, 113, 197, 353, 449, 617, 1409, 2003, 2549, 8009, 9857, 23297, 50513, 68993, 275969, 375233, 1500929, 3232769, 18386369]
  59. #~ [3, 5, 17, 23, 29, 53, 83, 89, 113, 197, 353, 449, 617, 1409, 2003, 2297, 2549, 3329, 8009, 10193, 16073, 23297, 50177, 93809, 202049, 275969, 656657, 18386369]
  60. #~ [3, 5, 17, 23, 29, 53, 83, 89, 113, 257, 353, 449, 617, 1409, 2003, 2297, 2549, 3137, 3329, 4019, 7547, 9857, 10193, 16073, 17837, 23297, 68993, 88397, 93809, 202049, 896897, 1500929, 2475089, 8044037, 18386369]
  61. #my @prefix = (3, 5, 17, 23, 29);
  62. my $prefix_prod = Math::GMPz->new(vecprod(@prefix));
  63. sub isok ($prefix_prod, $p) {
  64. (($prefix_prod % $p) == 0)
  65. ? 1
  66. : Math::Prime::Util::GMP::gcd(Math::Prime::Util::GMP::totient(Math::Prime::Util::GMP::mulint($prefix_prod, $p)), Math::Prime::Util::GMP::mulint($prefix_prod, $p)) eq '1';
  67. }
  68. sub method_1 ($L) {
  69. (vecall { ($L % ($_-1)) == 0 } @prefix) or return;
  70. my @P = lambda_primes($L);
  71. @P = grep {
  72. isok($prefix_prod, $_)
  73. } @P;
  74. #vecprodmod(@P, 3*5*17*23) == 0 or return;
  75. #vecprodmod(\@P, 3*5*17*23*29) == 0 or return;
  76. if (@prefix) {
  77. vecprodmod(\@P, $prefix_prod) == 0 or return;
  78. }
  79. #return if (vecprod(@P) < ~0);
  80. @P = grep { $_ > $prefix[-1] } @P;
  81. #~ @P = grep { gcd($prefix_prod, $_) == 1 } @P;
  82. say "# Testing: $L -- ", scalar(@P);
  83. my $n = scalar(@P);
  84. my @orig = @P;
  85. my $max = 1e5;
  86. my $max_k = scalar(@P)>>1;
  87. my $L_rem = invmod($prefix_prod, $L);
  88. foreach my $k (1 .. @P>>1) {
  89. #next if (binomial($n, $k) > 1e6);
  90. next if ($k > $max_k);
  91. @P = @orig;
  92. my $count = 0;
  93. forcomb {
  94. if (vecprodmod([@P[@_]], $L) == $L_rem) {
  95. say vecprod(@P[@_], $prefix_prod);
  96. }
  97. lastfor if (++$count > $max);
  98. } $n, $k;
  99. next if (binomial($n, $k) < $max);
  100. @P = reverse(@P);
  101. $count = 0;
  102. forcomb {
  103. if (vecprodmod([@P[@_]], $L) == $L_rem) {
  104. say vecprod(@P[@_], $prefix_prod);
  105. }
  106. lastfor if (++$count > $max);
  107. } $n, $k;
  108. @P = shuffle(@P);
  109. $count = 0;
  110. forcomb {
  111. if (vecprodmod([@P[@_]], $L) == $L_rem) {
  112. say vecprod(@P[@_], $prefix_prod);
  113. }
  114. lastfor if (++$count > $max);
  115. } $n, $k;
  116. }
  117. my $B = vecprodmod([@P, $prefix_prod], $L);
  118. my $T = Math::GMPz->new(Math::Prime::Util::GMP::vecprod(@P));
  119. foreach my $k (1 .. @P>>1) {
  120. #next if (binomial($n, $k) > 1e6);
  121. last if ($k > $max_k);
  122. @P = @orig;
  123. my $count = 0;
  124. forcomb {
  125. if (vecprodmod([@P[@_]], $L) == $B) {
  126. my $S = Math::GMPz->new(Math::Prime::Util::GMP::vecprod(@P[@_]));
  127. say vecprod($prefix_prod, $T / $S) if ($T != $S);
  128. }
  129. lastfor if (++$count > $max);
  130. } $n, $k;
  131. next if (binomial($n, $k) < $max);
  132. @P = reverse(@P);
  133. $count = 0;
  134. forcomb {
  135. if (vecprodmod([@P[@_]], $L) == $B) {
  136. my $S = Math::GMPz->new(Math::Prime::Util::GMP::vecprod(@P[@_]));
  137. say vecprod($prefix_prod, $T / $S) if ($T != $S);
  138. }
  139. lastfor if (++$count > $max);
  140. } $n, $k;
  141. @P = shuffle(@P);
  142. $count = 0;
  143. forcomb {
  144. if (vecprodmod([@P[@_]], $L) == $B) {
  145. my $S = Math::GMPz->new(Math::Prime::Util::GMP::vecprod(@P[@_]));
  146. say vecprod($prefix_prod, $T / $S) if ($T != $S);
  147. }
  148. lastfor if (++$count > $max);
  149. } $n, $k;
  150. }
  151. }
  152. use Math::GMPz;
  153. my %seen;
  154. foreach my $n(
  155. #~ 125067215
  156. #~ 7378965685
  157. #~ 8379503405
  158. #~ 494390700895
  159. #~ 611703748565
  160. #~ 36090521165335
  161. #~ 54441633622285
  162. #~ 8873986280432455
  163. #~ 11440695209411195
  164. #~ 3132517156992656615
  165. #~ "22331714812200649008335"
  166. #~ "2981979290957543061700035521"
  167. #~ "3804631920839812370901025615"
  168. #~ "450278872934589002316705363671"
  169. #~ "685938843816243059603953030639"
  170. #~ "124154930730739993788315498545659"
  171. #~ "2663278599434746232204172675190229413374378164374136375059"
  172. #~ "2000122228175494420385333679067862289444158001444976417669309"
  173. #~ "1538093993466955209276321599203186100582557503111186865187698621"
  174. #~ "1247394228701700674723096816953783927572454135023172547667223581631"
  175. #~ "1438245545693060877955730629947712868491039617681717947460308789620543"
  176. #~ "1727332900377366114424832486567203155057738580835743254899830856334272143"
  177. #~ "2240350771789443850409007735077662492109886939343959001605080620665550969471"
  178. #~ "562119408266512625797415127440913486421059303057986587120893756048196298403865200212378105189386871279"
  179. #~ "1686920344207804390018042797450181372749598968477017747949802161900637091509999465837346693673350000708279"
  180. #~ 5831683629926379776292373950785277005595363634025050354662466073690502425350068153399707520028770952448520503
  181. #~ 22679417636783690950001042294603942274760369172723420829282330560582363932186415048571462545391890234072296236167
  182. #~ 90740349964771547490954170220710373041316237060066406737958604572890038092677846609334421644112952826523257240904167
  183. #~ 367589157707289538885855343564097721190372076330329013695470307124777544313437956614413742080301571900245715082902780517
  184. #~ 1764795546152697076190991504451233159434976338461909594751952944506056990248815629705800375727527846693079678113016249262117
  185. #~ "8578671149848260487364409703137444388013419981263342540089243263243943029599492775999895626411512862775060315307371987663150737"
  186. #~ "55598367722166576218608739286033777078714974898567723002318385589083994774834312681255323554773014863645165903507077852044879926497"
  187. 6601
  188. ) {
  189. gcd(euler_phi($n), $n) == 1 or next;
  190. @prefix = factor($n);
  191. $prefix_prod = vecprod(@prefix);
  192. if ($prefix_prod > ~0) {
  193. $prefix_prod = Math::GMPz->new($prefix_prod);
  194. }
  195. foreach my $k(1..1e6) {
  196. my $m = vecprod($k, $prefix_prod);
  197. my $L = lcm(map { subint($_, 1) } factor($m));
  198. if ($L > ~0) {
  199. $L = Math::GMPz->new("$L");
  200. }
  201. $L % 2 == 0 or next;
  202. next if $seen{$L}++;
  203. method_1($L);
  204. }
  205. }
  206. __END__
  207. my $count = 0;
  208. while (<>) {
  209. chomp(my $n = $_);
  210. #$n > 1e5 or next;
  211. #$n *= 656656;
  212. #$n *= 78848;
  213. #$n *= 1232;
  214. #$n = lcm($n, 29586292736);
  215. #$n = lcm($n, 1232);
  216. #$n = lcm($n, 5789168, 147090944);
  217. #$n = lcm($n, 78848);
  218. #$n = lcm($n, 656656);
  219. #$n = lcm($n, 9193184);
  220. #$n = lcm($n, 10506496);
  221. #$n = lcm($n, 36772736);
  222. #~ next if ($n < 707981814540);
  223. #~ next if ($n > 44351949725003712);
  224. #next if ($n < 1e6);
  225. #next if ($n < 1e8);
  226. #next if ($n < 7813080); # for 2^64
  227. #next if ($n < ~0);
  228. #next if ($n < 17125441200);
  229. #next if (length($n) > 45);
  230. # if (++$count % 1000 == 0) {
  231. #say "Testing: $n";
  232. # $count = 0 ;
  233. # }
  234. #say "Testing: $n";
  235. if ($n > ~0) {
  236. $n = Math::GMPz->new($n);
  237. }
  238. next if $seen{$n}++;
  239. method_1($n);
  240. }