smallest_lucas-carmichael_divisible_by_n.pl 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. #!/usr/bin/perl
  2. # Method for finding the smallest Lucas-Carmichael number divisible by n.
  3. # See also:
  4. # https://oeis.org/A253597
  5. # https://oeis.org/A253598
  6. use 5.036;
  7. use warnings;
  8. use ntheory qw(:all);
  9. sub divceil ($x, $y) { # ceil(x/y)
  10. (($x % $y == 0) ? 0 : 1) + divint($x, $y);
  11. }
  12. sub lucas_carmichael_from_multiple ($A, $B, $m, $L, $lo, $k, $callback) {
  13. my $hi = vecmin(rootint(divint($B, $m), $k), sqrtint($B));
  14. if ($lo > $hi) {
  15. return;
  16. }
  17. if ($k == 1) {
  18. $lo = vecmax($lo, divceil($A, $m));
  19. $lo > $hi && return;
  20. my $t = mulmod(invmod($m, $L) // (return), -1, $L);
  21. $t > $hi && return;
  22. $t += $L * divceil($lo - $t, $L) if ($t < $lo);
  23. for (my $p = $t ; $p <= $hi ; $p += $L) {
  24. if ($m % $p != 0 and is_prime($p)) {
  25. my $n = $m * $p;
  26. if (($n + 1) % ($p + 1) == 0) {
  27. $callback->($n);
  28. }
  29. }
  30. }
  31. return;
  32. }
  33. foreach my $p (@{primes($lo, $hi)}) {
  34. $m % $p == 0 and next;
  35. gcd($m, $p + 1) == 1 or next;
  36. __SUB__->($A, $B, $m * $p, lcm($L, $p + 1), $p + 1, $k - 1, $callback);
  37. }
  38. }
  39. sub lucas_carmichael_divisible_by ($m) {
  40. $m >= 1 or return;
  41. $m % 2 == 0 and return;
  42. is_square_free($m) || return;
  43. gcd($m, divisor_sum($m)) == 1 or return;
  44. my $A = vecmax(399, $m);
  45. my $B = 2 * $A;
  46. my $L = vecmax(1, lcm(map { $_ + 1 } factor($m)));
  47. my @found;
  48. for (; ;) {
  49. for my $k ((is_prime($m) ? 2 : 1) .. 1000) {
  50. my @P;
  51. for (my $p = 3 ; scalar(@P) < $k ; $p = next_prime($p)) {
  52. if ($m % $p != 0 and $L % $p != 0) {
  53. push @P, $p;
  54. }
  55. }
  56. last if (vecprod(@P, $m) > $B);
  57. my $callback = sub ($n) {
  58. push @found, $n;
  59. $B = vecmin($B, $n);
  60. };
  61. lucas_carmichael_from_multiple($A, $B, $m, $L, $P[0], $k, $callback);
  62. }
  63. last if @found;
  64. $A = $B + 1;
  65. $B = 2 * $A;
  66. }
  67. vecmin(@found);
  68. }
  69. lucas_carmichael_divisible_by(1) == 399 or die;
  70. lucas_carmichael_divisible_by(3) == 399 or die;
  71. lucas_carmichael_divisible_by(3 * 7) == 399 or die;
  72. lucas_carmichael_divisible_by(7 * 19) == 399 or die;
  73. say join(', ', map { lucas_carmichael_divisible_by($_) } @{primes(3, 50)});
  74. say join(', ', map { lucas_carmichael_divisible_by($_) } 1 .. 100);
  75. __END__
  76. 399, 935, 399, 935, 2015, 935, 399, 4991, 51359, 2015, 1584599, 20705, 5719, 18095
  77. 399, 399, 935, 399, 935, 2015, 935, 399, 399, 4991, 51359, 2015, 8855, 1584599, 9486399, 20705, 5719, 18095, 2915, 935, 399, 46079, 162687, 2015, 22847, 46079, 16719263, 8855, 12719, 7055, 935, 80189, 189099039, 104663