partial_sums_of_sigma_function.pl 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. #!/usr/bin/perl
  2. # Author: Daniel "Trizen" Șuteu
  3. # Date: 09 November 2018
  4. # https://github.com/trizen
  5. # A new generalized algorithm with O(sqrt(n)) complexity for computing the partial-sums of the `sigma_j(k)` function:
  6. #
  7. # Sum_{k=1..n} sigma_j(k)
  8. #
  9. # for any integer j >= 0.
  10. # See also:
  11. # https://en.wikipedia.org/wiki/Divisor_function
  12. # https://en.wikipedia.org/wiki/Faulhaber%27s_formula
  13. # https://en.wikipedia.org/wiki/Bernoulli_polynomials
  14. # https://trizenx.blogspot.com/2018/11/partial-sums-of-arithmetical-functions.html
  15. use 5.020;
  16. use strict;
  17. use warnings;
  18. use ntheory qw(divisors);
  19. use experimental qw(signatures);
  20. use Math::AnyNum qw(faulhaber_sum bernoulli sum isqrt ipow);
  21. sub sigma_partial_sum_faulhaber ($n, $m = 1) { # using Faulhaber's formula
  22. my $s = isqrt($n);
  23. my $u = int($n / ($s + 1));
  24. my $sum = 0;
  25. foreach my $k (1 .. $s) {
  26. $sum += $k * (faulhaber_sum(int($n/$k), $m) - faulhaber_sum(int($n/($k+1)), $m));
  27. }
  28. foreach my $k (1 .. $u) {
  29. $sum += ipow($k, $m) * int($n / $k);
  30. }
  31. return $sum;
  32. }
  33. sub sigma_partial_sum_bernoulli ($n, $m = 1) { # using Bernoulli polynomials
  34. my $s = isqrt($n);
  35. my $u = int($n / ($s + 1));
  36. my $sum = 0;
  37. foreach my $k (1 .. $s) {
  38. $sum += $k * (bernoulli($m+1, 1+int($n/$k)) - bernoulli($m+1, 1+int($n/($k+1)))) / ($m+1);
  39. }
  40. foreach my $k (1 .. $u) {
  41. $sum += ipow($k, $m) * int($n / $k);
  42. }
  43. return $sum;
  44. }
  45. sub sigma_partial_sum_test ($n, $m = 1) { # just for testing
  46. sum(map { sum(map { ipow($_, $m) } divisors($_)) } 1..$n);
  47. }
  48. foreach my $m (0 .. 10) {
  49. my $n = int(rand(1000));
  50. my $t1 = sigma_partial_sum_test($n, $m);
  51. my $t2 = sigma_partial_sum_faulhaber($n, $m);
  52. my $t3 = sigma_partial_sum_bernoulli($n, $m);
  53. say "Sum_{k=1..$n} sigma_$m(k) = $t2";
  54. die "error: $t1 != $t2" if ($t1 != $t2);
  55. die "error: $t1 != $t3" if ($t1 != $t3);
  56. }
  57. __END__
  58. Sum_{k=1..198} sigma_0(k) = 1084
  59. Sum_{k=1..657} sigma_1(k) = 355131
  60. Sum_{k=1..933} sigma_2(k) = 325914283
  61. Sum_{k=1..905} sigma_3(k) = 181878297343
  62. Sum_{k=1..402} sigma_4(k) = 2191328841200
  63. Sum_{k=1..967} sigma_5(k) = 139059243381760868
  64. Sum_{k=1..320} sigma_6(k) = 50042081613053611
  65. Sum_{k=1..168} sigma_7(k) = 81561359789498529
  66. Sum_{k=1..977} sigma_8(k) = 90713993807165413835362083
  67. Sum_{k=1..219} sigma_9(k) = 25985664184393953943010
  68. Sum_{k=1..552} sigma_10(k) = 133190310787744370768676943091