516 5-smooth totients -- v2.pl 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 03 October 2017
  4. # https://github.com/trizen
  5. # https://projecteuler.net/problem=516
  6. # Runtime: ~9 minutes.
  7. # Based on Dana Jacobsen's code from Math::Prime::Util,
  8. # which in turn is based on invphi.gp v1.3 by Max Alekseyev.
  9. # See also:
  10. # https://en.wikipedia.org/wiki/Euler%27s_totient_function
  11. # https://github.com/danaj/Math-Prime-Util/blob/master/examples/inverse_totient.pl
  12. use 5.010;
  13. use strict;
  14. use warnings;
  15. use ntheory qw(is_prime divisors valuation addmod);
  16. use constant LIMIT => 1e12;
  17. use constant MOD => 2**32;
  18. sub inverse_euler_phi {
  19. my ($n) = @_;
  20. my %r = (1 => [1]);
  21. foreach my $d (divisors($n)) {
  22. if (is_prime($d + 1)) {
  23. my %temp;
  24. foreach my $k (1 .. (valuation($n, $d + 1) + 1)) {
  25. my $u = $d * ($d + 1)**($k - 1);
  26. my $v = ($d + 1)**$k;
  27. foreach my $f (divisors($n / $u)) {
  28. if (exists $r{$f}) {
  29. push @{$temp{$f * $u}}, grep { $_ <= LIMIT } map { $v * $_ } @{$r{$f}};
  30. }
  31. }
  32. }
  33. while (my ($i, $v) = each(%temp)) {
  34. push @{$r{$i}}, @$v;
  35. }
  36. }
  37. }
  38. return if not exists $r{$n};
  39. return @{$r{$n}};
  40. }
  41. my @smooth = (1);
  42. foreach my $n (2, 3, 5) {
  43. foreach my $k (@smooth) {
  44. if ($n * $k <= LIMIT) {
  45. push @smooth, $n * $k;
  46. }
  47. }
  48. }
  49. my $sum = 0;
  50. foreach my $k (@smooth) {
  51. foreach my $n (inverse_euler_phi($k)) {
  52. $sum = addmod($sum, $n, MOD);
  53. }
  54. }
  55. say $sum;