dirichlet_hyperbola_method.pl 1.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. #!/usr/bin/perl
  2. # Simple implementation of Dirichlet's hyperbola method.
  3. # Useful to compute partial sums of in sublinear time:
  4. # Sum_{d|n} g(d) * h(n/d)
  5. # See also:
  6. # https://en.wikipedia.org/wiki/Dirichlet_hyperbola_method
  7. use 5.020;
  8. use strict;
  9. use warnings;
  10. use ntheory qw(:all);
  11. use Math::AnyNum qw(faulhaber_sum);
  12. use experimental qw(signatures);
  13. sub dirichlet_hyperbola_method ($n, $g, $h, $G, $H) {
  14. my $s = sqrtint($n);
  15. my $A = 0;
  16. my $B = 0;
  17. my $C = 0;
  18. foreach my $k (1 .. $s) {
  19. my $gk = $g->($k);
  20. my $hk = $h->($k);
  21. $A += $gk * $H->(divint($n, $k));
  22. $A += $hk * $G->(divint($n, $k));
  23. $B += $gk;
  24. $C += $hk;
  25. }
  26. $A - $B * $C;
  27. }
  28. sub g ($n) { $n }
  29. sub h ($n) { moebius($n) }
  30. sub G ($n) { faulhaber_sum($n, 1) } # partial sums of g(n): Sum_{k=1..n} g(k)
  31. sub H ($n) { mertens($n) } # partial sums of h(n): Sum_{k=1..n} h(k)
  32. foreach my $n (1 .. 8) {
  33. say "S(10^$n) = ", dirichlet_hyperbola_method(powint(10, $n), \&g, \&h, \&G, \&H);
  34. }
  35. __END__
  36. S(10^1) = 32
  37. S(10^2) = 3044
  38. S(10^3) = 304192
  39. S(10^4) = 30397486
  40. S(10^5) = 3039650754
  41. S(10^6) = 303963552392
  42. S(10^7) = 30396356427242
  43. S(10^8) = 3039635516365908