modular_pseudo_square_root_2.pl 1.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 05 June 2019
  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(:all);
  11. use experimental qw(signatures);
  12. sub pseudo_square_root_mod ($n, $mod) {
  13. my $lim = sqrtint($n);
  14. my @factors = map { [$_, log($_)] } grep { $_ <= $lim } factor($n);
  15. my @d = ([1, 0]);
  16. my $sqrt_log = log("$n") / 2;
  17. my %seen;
  18. while (my $p = shift(@factors)) {
  19. my @t;
  20. foreach my $d (@d) {
  21. if ($p->[1] + $d->[1] <= $sqrt_log) {
  22. push @t, [mulmod($p->[0], $d->[0], $mod), $p->[1] + $d->[1]];
  23. }
  24. }
  25. push @d, @t;
  26. }
  27. my $max_log = 0;
  28. my $max_div = 0;
  29. foreach my $d (@d) {
  30. if ($d->[1] > $max_log) {
  31. $max_div = $d->[0];
  32. $max_log = $d->[1];
  33. }
  34. }
  35. return $max_div;
  36. }
  37. say pseudo_square_root_mod(479001600, 10**16); #=> 21600
  38. say pseudo_square_root_mod(6469693230, 10**16); #=> 79534
  39. say pseudo_square_root_mod(12398712476, 10**16); #=> 68
  40. say pseudo_square_root_mod('614889782588491410', 10**8); #=> 83152070
  41. say pseudo_square_root_mod('3217644767340672907899084554130', 10**16); #=> 1793779293633437