erdos_lucas-carmichael_from_lambdas.pl 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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. my $prod = 1;
  23. foreach my $k (@$arr) {
  24. $prod = mulmod($prod, $k, $mod);
  25. }
  26. $prod;
  27. }
  28. # Primes p such that p-1 divides L and p does not divide L
  29. sub lambda_primes ($L) {
  30. my @divisors = divisors($L);
  31. if (ref $L) {
  32. @divisors = map{Math::GMPz->new($_)} @divisors;
  33. }
  34. grep { ($L % $_) != 0 } grep { $_ > 2 and is_prime($_) } map { $_ - 1 } @divisors;
  35. }
  36. sub method_1 ($L) { # smallest numbers first
  37. my @P = lambda_primes($L);
  38. my $n = scalar(@P);
  39. my @orig = @P;
  40. my $max = 5e2;
  41. my $max_k = @P>>1;
  42. foreach my $k (3 .. @P>>1) {
  43. #next if (binomial($n, $k) > 1e6);
  44. next if ($k > $max_k);
  45. @P = @orig;
  46. my $count = 0;
  47. forcomb {
  48. if (vecprodmod([@P[@_]], $L) == $L-1) {
  49. say vecprod(@P[@_]);
  50. }
  51. lastfor if (++$count > $max);
  52. } $n, $k;
  53. next if (binomial($n, $k) < $max);
  54. @P = reverse(@P);
  55. $count = 0;
  56. forcomb {
  57. if (vecprodmod([@P[@_]], $L) == $L-1) {
  58. say vecprod(@P[@_]);
  59. }
  60. lastfor if (++$count > $max);
  61. } $n, $k;
  62. @P = shuffle(@P);
  63. $count = 0;
  64. forcomb {
  65. if (vecprodmod([@P[@_]], $L) == $L-1) {
  66. say vecprod(@P[@_]);
  67. }
  68. lastfor if (++$count > $max);
  69. } $n, $k;
  70. }
  71. my $B = Math::GMPz->new(vecprodmod(\@P, $L));
  72. my $T = Math::GMPz->new(vecprod(@P));
  73. foreach my $k (1 .. @P>>1) {
  74. #next if (binomial($n, $k) > 1e6);
  75. last if ($k > $max_k);
  76. @P = @orig;
  77. my $count = 0;
  78. forcomb {
  79. if (vecprodmod([@P[@_]], $L) == $L-$B) {
  80. my $S = vecprod(@P[@_]);
  81. say ($T / $S) if ($T != $S);
  82. }
  83. lastfor if (++$count > $max);
  84. } $n, $k;
  85. next if (binomial($n, $k) < $max);
  86. @P = reverse(@P);
  87. $count = 0;
  88. forcomb {
  89. if (vecprodmod([@P[@_]], $L) == $L-$B) {
  90. my $S = vecprod(@P[@_]);
  91. say ($T / $S) if ($T != $S);
  92. }
  93. lastfor if (++$count > $max);
  94. } $n, $k;
  95. @P = shuffle(@P);
  96. $count = 0;
  97. forcomb {
  98. if (vecprodmod([@P[@_]], $L) == $L-$B) {
  99. my $S = vecprod(@P[@_]);
  100. say ($T / $S) if ($T != $S);
  101. }
  102. lastfor if (++$count > $max);
  103. } $n, $k;
  104. }
  105. }
  106. use Math::GMPz;
  107. my %seen;
  108. my $count = 0;
  109. while (<>) {
  110. chomp(my $n = $_);
  111. #next if rand() < 0.95;
  112. #$n *= 210;
  113. $n *= 271;
  114. #$n = lcm($n, 10080);
  115. next if ($n < 1e3);
  116. #next if ($n < 829447240860);
  117. next if (length($n) > 45);
  118. #~ if (++$count % 100 == 0) {
  119. #~ say "# Testing: $n";
  120. #~ $count = 0 ;
  121. #~ }
  122. #say "Testing: $n";
  123. if ($n > ~0) {
  124. $n = Math::GMPz->new($n);
  125. }
  126. next if $seen{$n}++;
  127. method_1($n);
  128. }