modular_pseudo_square_root.pl 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 13 October 2017
  4. # https://github.com/trizen
  5. # Find the greatest divisor (mod m) of `n` that does not exceed the square root of `n`.
  6. # See also:
  7. # https://projecteuler.net/problem=266
  8. use 5.020;
  9. use warnings;
  10. use ntheory qw(factor mulmod);
  11. use experimental qw(signatures);
  12. sub pseudo_square_root_mod ($n, $mod) {
  13. my $sqrt_log = log("$n") / 2;
  14. my @factors = factor($n);
  15. my $end = $#factors;
  16. my $maximum_log = 0;
  17. my $maximum_num = 0;
  18. sub ($i, $log, $prod) {
  19. if ($log > $maximum_log) {
  20. $maximum_log = $log;
  21. $maximum_num = $prod;
  22. }
  23. if ($i > $end) {
  24. return;
  25. }
  26. if ($log + log($factors[$i]) <= $sqrt_log) {
  27. __SUB__->($i + 1, $log, $prod) if ($i < $end);
  28. __SUB__->($i + 1, $log + log($factors[$i]), mulmod($prod, $factors[$i], $mod));
  29. }
  30. }->(0, 0, 1);
  31. return $maximum_num;
  32. }
  33. say pseudo_square_root_mod(479001600, 10**16); #=> 21600
  34. say pseudo_square_root_mod(6469693230, 10**16); #=> 79534
  35. say pseudo_square_root_mod(12398712476, 10**16); #=> 68
  36. say pseudo_square_root_mod('614889782588491410', 10**8); #=> 83152070
  37. say pseudo_square_root_mod('3217644767340672907899084554130', 10**16); #=> 1793779293633437