lucas_carmichael_divisible_by_n.pl 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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.020;
  7. use warnings;
  8. use ntheory qw(:all);
  9. use experimental qw(signatures);
  10. use Math::GMPz;
  11. sub divceil ($x, $y) { # ceil(x/y)
  12. my $q = divint($x, $y);
  13. ($q * $y == $x) ? $q : ($q + 1);
  14. }
  15. sub lucas_carmichael_from_multiple ($A, $B, $m, $lambda, $p, $k, $callback) {
  16. my $y = rootint(($B / $m), $k);
  17. if ($k == 1) {
  18. my $x = vecmax($p, divceil($A, $m));
  19. $y = vecmin(773_602, $y);
  20. forprimes {
  21. if ($m % $_ != 0) {
  22. my $t = $m * $_;
  23. if (($t + 1) % $lambda == 0 and ($t + 1) % ($_ + 1) == 0) {
  24. $callback->($t);
  25. }
  26. }
  27. } $x, $y;
  28. return;
  29. }
  30. for (my $r ; $p <= $y ; $p = $r) {
  31. $r = next_prime($p);
  32. $m % $p == 0 and next;
  33. is_smooth($p+1, 29) || next;
  34. last if ($p > 20_000);
  35. my $L = lcm($lambda, $p + 1);
  36. gcd($L, $m) == 1 or next;
  37. my $t = $m * $p;
  38. my $u = divceil($A, $t);
  39. my $v = ($B / $t);
  40. if ($u <= $v) {
  41. __SUB__->($A, $B, $t, $L, $r, $k - 1, $callback);
  42. }
  43. }
  44. }
  45. sub lucas_carmichael_divisible_by ($m) {
  46. $m >= 1 or return;
  47. $m % 2 == 0 and return;
  48. is_square_free($m) || return;
  49. gcd($m, divisor_sum($m)) == 1 or return;
  50. my $A = vecmax(399, $m);
  51. my $B = 2 * $A;
  52. $A = Math::GMPz->new($A);
  53. $B = Math::GMPz->new($B);
  54. $m = Math::GMPz->new($m);
  55. my $L = vecmax(1, lcm(map { $_ + 1 } factor($m)));
  56. my @found;
  57. for (; ;) {
  58. say "# Sieving ($A, $B)";
  59. for my $k ((is_prime($m) ? 2 : 1) .. 1000) {
  60. my @P;
  61. for (my $p = 3 ; scalar(@P) < $k ; $p = next_prime($p)) {
  62. if ($m % $p != 0 and $L % $p != 0 and is_smooth($p+1, 29)) {
  63. push @P, $p;
  64. }
  65. }
  66. last if (vecprod(@P, $m) > $B);
  67. say "# Checking: k = $k";
  68. my $callback = sub ($n) {
  69. say $n;
  70. push @found, $n;
  71. $B = vecmin($B, $n);
  72. };
  73. lucas_carmichael_from_multiple($A, $B, $m, $L, $P[0], $k, $callback);
  74. }
  75. last if @found;
  76. $A = $B + 1;
  77. $B = 2 * $A;
  78. }
  79. vecmin(@found);
  80. }
  81. #~ lucas_carmichael_divisible_by(1) == 399 or die;
  82. #~ lucas_carmichael_divisible_by(3) == 399 or die;
  83. #~ lucas_carmichael_divisible_by(3 * 7) == 399 or die;
  84. #~ lucas_carmichael_divisible_by(7 * 19) == 399 or die;
  85. #say join(', ', map { lucas_carmichael_divisible_by($_) } @{primes(3, 50)});
  86. #say join(', ', map { lucas_carmichael_divisible_by($_) } 1..100);
  87. #lucas_carmichael_divisible_by(Math::GMPz->new("11160493497496010380479442945147306718821983182885"));
  88. lucas_carmichael_divisible_by(Math::GMPz->new("33226747402673571560714119010730997321228928534495"));