generate_carmichael_of_second_order.pl 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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. # The sequence of Carmichael numbers of order 2:
  9. # 443372888629441, 39671149333495681, 842526563598720001, 2380296518909971201, 3188618003602886401, ...
  10. # OEIS sequence:
  11. # https://oeis.org/A175531
  12. use 5.020;
  13. use warnings;
  14. use ntheory qw(:all);
  15. use experimental qw(signatures);
  16. use Math::GMPz;
  17. #use Math::AnyNum qw(:overload);
  18. # Modular product of a list of integers
  19. sub vecprodmod ($arr, $mod) {
  20. my $prod = 1;
  21. foreach my $k (@$arr) {
  22. $prod = mulmod($prod, $k, $mod);
  23. }
  24. $prod;
  25. }
  26. # Primes p such that p-1 divides L and p does not divide L
  27. sub lambda_primes ($L) {
  28. grep { $L % $_ != 0 } grep { $_ > 2 } map { sqrtint($_) } grep { is_square($_) && is_prime(sqrtint($_)) } map { $_ + 1 } divisors($L);
  29. #grep { $L % $_ != 0 } grep { $_ > 2 and is_prime($_) } map { $_ + 1 } divisors($L);
  30. }
  31. sub method_1 ($L) { # smallest numbers first
  32. my @P = lambda_primes($L);
  33. foreach my $k (3 .. @P) {
  34. forcomb {
  35. if (vecprodmod([@P[@_]], $L) == 1) {
  36. say vecprod(@P[@_]);
  37. }
  38. } scalar(@P), $k;
  39. }
  40. }
  41. #~ sub method_2 ($L) { # largest numbers first
  42. #~ my @P = lambda_primes($L);
  43. #~ my $B = vecprodmod(\@P, $L);
  44. #~ my $T = vecprod(@P);
  45. #~ #say "@P";
  46. #~ foreach my $k (1 .. (@P-3)) {
  47. #~ #say "Testing: $k -- ", binomial(scalar(@P), $k);
  48. #~ my $count = 0;
  49. #~ forcomb {
  50. #~ if (vecprodmod([@P[@_]], $L) == $B) {
  51. #~ my $S = vecprod(@P[@_]);
  52. #~ say ($T / $S) if ($T != $S);
  53. #~ }
  54. #~ lastfor if (++$count > 1e6);
  55. #~ } scalar(@P), $k;
  56. #~ }
  57. #~ }
  58. sub method_2($L) {
  59. my @P = lambda_primes($L);
  60. return if (vecprod(@P) < ~0);
  61. my $n = scalar(@P);
  62. my @orig = @P;
  63. my $max = 1e5;
  64. my $max_k = 10;
  65. foreach my $k (3 .. @P>>1) {
  66. #next if (binomial($n, $k) > 1e6);
  67. next if ($k > $max_k);
  68. @P = @orig;
  69. my $count = 0;
  70. forcomb {
  71. if (vecprodmod([@P[@_]], $L) == 1) {
  72. say vecprod(@P[@_]);
  73. }
  74. lastfor if (++$count > $max);
  75. } $n, $k;
  76. next if (binomial($n, $k) < $max);
  77. @P = reverse(@P);
  78. $count = 0;
  79. forcomb {
  80. if (vecprodmod([@P[@_]], $L) == 1) {
  81. say vecprod(@P[@_]);
  82. }
  83. lastfor if (++$count > $max);
  84. } $n, $k;
  85. @P = shuffle(@P);
  86. $count = 0;
  87. forcomb {
  88. if (vecprodmod([@P[@_]], $L) == 1) {
  89. say vecprod(@P[@_]);
  90. }
  91. lastfor if (++$count > $max);
  92. } $n, $k;
  93. }
  94. my $B = Math::GMPz->new(vecprodmod(\@P, $L));
  95. my $T = Math::GMPz->new(vecprod(@P));
  96. foreach my $k (1 .. @P>>1) {
  97. #next if (binomial($n, $k) > 1e6);
  98. last if ($k > $max_k);
  99. @P = @orig;
  100. my $count = 0;
  101. forcomb {
  102. if (vecprodmod([@P[@_]], $L) == $B) {
  103. my $S = vecprod(@P[@_]);
  104. say ($T / $S) if ($T != $S);
  105. }
  106. lastfor if (++$count > $max);
  107. } $n, $k;
  108. next if (binomial($n, $k) < $max);
  109. @P = reverse(@P);
  110. $count = 0;
  111. forcomb {
  112. if (vecprodmod([@P[@_]], $L) == $B) {
  113. my $S = vecprod(@P[@_]);
  114. say ($T / $S) if ($T != $S);
  115. }
  116. lastfor if (++$count > $max);
  117. } $n, $k;
  118. @P = shuffle(@P);
  119. $count = 0;
  120. forcomb {
  121. if (vecprodmod([@P[@_]], $L) == $B) {
  122. my $S = vecprod(@P[@_]);
  123. say ($T / $S) if ($T != $S);
  124. }
  125. lastfor if (++$count > $max);
  126. } $n, $k;
  127. }
  128. }
  129. sub check_valuation ($n, $p) {
  130. if ($p == 2) {
  131. return valuation($n, $p) < 11;
  132. }
  133. if ($p == 3) {
  134. return valuation($n, $p) < 5;
  135. }
  136. if ($p == 5) {
  137. return valuation($n, $p) < 3;
  138. }
  139. if ($p == 7) {
  140. return valuation($n, $p) < 3;
  141. }
  142. if ($p == 11) {
  143. return valuation($n, $p) < 2;
  144. }
  145. ($n % $p) != 0;
  146. }
  147. sub smooth_numbers ($limit, $primes) {
  148. my @h = (1);
  149. foreach my $p (@$primes) {
  150. say "Prime: $p";
  151. foreach my $n (@h) {
  152. if ($n * $p <= $limit and check_valuation($n, $p)) {
  153. push @h, $n * $p;
  154. }
  155. }
  156. }
  157. return \@h;
  158. }
  159. my $h = smooth_numbers(10**10, [2, 3, 5, 7, 11, 13, 19, 31, 83]);
  160. say "\nFound: ", scalar(@$h), " terms";
  161. my %table;
  162. foreach my $n (@$h) {
  163. valuation($n, 2) >= 6 or next;
  164. valuation($n, 3) >= 2 or next;
  165. valuation($n, 5) >= 1 or next;
  166. valuation($n, 7) >= 1 or next;
  167. #say "Generating: $n";
  168. method_2($n);
  169. }