266 Pseudo Square Root.pl 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #!/usr/bin/perl
  2. # Find the greatest divisor of `n` that does not exceed the square root of `n`.
  3. # See also:
  4. # https://projecteuler.net/problem=266
  5. # Best lower-bound found so far:
  6. # 2323218950482697766641170378640119830 <= PSR(primorial(190)) < 2323218950681478446587180516177954548
  7. use 5.036;
  8. use ntheory qw(:all);
  9. use Math::GMPz;
  10. sub squarefree_almost_primes_in_range ($A, $B, $k, $factors, $callback) {
  11. $A = Math::GMPz->new(vecmax($A, pn_primorial($k)));
  12. my $factors_end = $#{$factors};
  13. if ($k == 0) {
  14. return (($A > 1) ? () : 1);
  15. }
  16. my $z = Math::GMPz::Rmpz_init();
  17. sub ($m, $k, $i = 0) {
  18. Math::GMPz::Rmpz_div($z, $B, $m);
  19. Math::GMPz::Rmpz_root($z, $z, $k);
  20. my $lo = $factors->[$i];
  21. my $hi = Math::GMPz::Rmpz_get_ui($z);
  22. if ($lo > $hi) {
  23. return;
  24. }
  25. if ($k == 1) {
  26. Math::GMPz::Rmpz_cdiv_q($z, $A, $m);
  27. if ($z > $lo) {
  28. $lo = $z;
  29. $lo = Math::GMPz::Rmpz_get_ui($lo) if Math::GMPz::Rmpz_fits_ulong_p($lo);
  30. }
  31. if ($lo > $hi) {
  32. return;
  33. }
  34. foreach my $j ($i .. $factors_end) {
  35. my $q = $factors->[$j];
  36. last if ($q > $hi);
  37. next if ($q < $lo);
  38. my $t = $m * $q;
  39. $A = $t if ($t > $A);
  40. say "Found lower-bound: $t";
  41. $callback->($t);
  42. }
  43. return;
  44. }
  45. my $t = Math::GMPz::Rmpz_init();
  46. foreach my $j ($i .. $factors_end - 1) {
  47. my $q = $factors->[$j];
  48. last if ($q > $hi);
  49. next if ($q < $lo);
  50. Math::GMPz::Rmpz_mul_ui($t, $m, $q);
  51. if ($k == 2) {
  52. Math::GMPz::Rmpz_mul_ui($z, $t, $factors->[$j + 1]);
  53. $z <= $B or next;
  54. }
  55. __SUB__->($t, $k - 1, $j + 1);
  56. }
  57. }
  58. ->(Math::GMPz->new(1), $k);
  59. }
  60. my $n = 190;
  61. my $upto = Math::GMPz->new(sqrtint(primorial($n)));
  62. my $from = Math::GMPz->new($upto) >> 1;
  63. my @factors = @{primes($n)}; # prime list
  64. foreach my $k (reverse(0 .. scalar(@factors))) {
  65. say "Checking: k = $k";
  66. my @divisors;
  67. squarefree_almost_primes_in_range(
  68. $from, $upto, $k,
  69. \@factors,
  70. sub ($n) {
  71. push @divisors, $n;
  72. }
  73. );
  74. if (@divisors) {
  75. $from = Math::GMPz->new(vecmax(@divisors));
  76. printf("%2d-squarefree almost primes <= %s: [%s]\n", $k, $upto, $from);
  77. }
  78. }