partial_sums_of_sigma_function.pl 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #!/usr/bin/perl
  2. # Author: Daniel "Trizen" Șuteu
  3. # Date: 10 Novermber 2018
  4. # https://github.com/trizen
  5. # A generalized formula with O(sqrt(n)) complexity for computing the partial-sum of `k^m * sigma_j(k)`, for `1 <= k <= n`:
  6. #
  7. # Sum_{k=1..n} k^m * sigma_j(k)
  8. #
  9. # for any fixed integers m >= 0 and j >= 0.
  10. # Formula:
  11. # Sum_{k=1..n} k^m * sigma_j(k) = Sum_{k=1..floor(sqrt(n))} F(m, k) * (F(m+j, floor(n/k)) - F(m+j, floor(n/(k+1))))
  12. # + Sum_{k=1..floor(n/(floor(sqrt(n))+1))} k^(m+j) * F(m, floor(n/k))
  13. #
  14. # where F(n,x) is Faulhaber's formula for `Sum_{k=1..x} k^n`, defined in terms of Bernoulli polynomials as:
  15. #
  16. # F(n, x) = (Bernoulli(n+1, x+1) - Bernoulli(n+1)) / (n+1)
  17. #
  18. # where Bernoulli(n,x) are the Bernoulli polynomials and Bernoulli(n) is the n-th Bernoulli number.
  19. # See also:
  20. # https://en.wikipedia.org/wiki/Divisor_function
  21. # https://en.wikipedia.org/wiki/Faulhaber%27s_formula
  22. # https://en.wikipedia.org/wiki/Bernoulli_polynomials
  23. # https://trizenx.blogspot.com/2018/11/partial-sums-of-arithmetical-functions.html
  24. use 5.020;
  25. use strict;
  26. use warnings;
  27. use lib qw(../lib);
  28. use experimental qw(signatures);
  29. use Math::AnyNum qw(isqrt idiv ipow bernoulli faulhaber_sum dirichlet_sum);
  30. sub faulhaber_partial_sum_of_sigma($n, $m, $j) { # using Faulhaber's formula
  31. my $total = 0;
  32. my $s = isqrt($n);
  33. my $u = int($n / ($s + 1));
  34. for my $k (1 .. $s) {
  35. $total += faulhaber_sum($k, $m) * (
  36. faulhaber_sum(int($n/$k), $m+$j)
  37. - faulhaber_sum(int($n/($k+1)), $m+$j)
  38. );
  39. }
  40. for my $k (1 .. $u) {
  41. $total += ipow($k, $m+$j) * faulhaber_sum(int($n/$k), $m);
  42. }
  43. return $total;
  44. }
  45. sub bernoulli_partial_sum_of_sigma($n, $m, $j) { # using Bernoulli polynomials
  46. my $total = 0;
  47. my $s = isqrt($n);
  48. my $u = int($n / ($s + 1));
  49. for my $k (1 .. $s) {
  50. $total += (
  51. bernoulli($m+1, $k+1)
  52. - bernoulli($m+1)
  53. )/($m+1)
  54. * (
  55. bernoulli($m+$j+1, 1+int($n/$k))
  56. - bernoulli($m+$j+1, 1+int($n/($k+1)))
  57. )/($m+$j+1);
  58. }
  59. for my $k (1 .. $u) {
  60. $total += ipow($k, $m+$j) * (bernoulli($m+1, 1+int($n/$k)) - bernoulli($m+1)) / ($m+1);
  61. }
  62. return $total;
  63. }
  64. sub dirichlet_partial_sum_of_sigma ($n, $m, $j) { # using Dirichlet's hyperbola method
  65. my $total = 0;
  66. my $s = isqrt($n);
  67. for my $k (1 .. $s) {
  68. $total += ipow($k, $m) * faulhaber_sum(int($n/$k), $m+$j);
  69. $total += ipow($k, $m+$j) * faulhaber_sum(int($n/$k), $m);
  70. }
  71. $total -= faulhaber_sum($s, $m) * faulhaber_sum($s, $j+$m);
  72. return $total;
  73. }
  74. sub builtin_dirichlet_sum ($n, $m, $j) { # using the built-in Dirichlet's hyperbola method
  75. dirichlet_sum($n,
  76. sub ($k) { ipow($k, $m) },
  77. sub ($k) { ipow($k, $m+$j) },
  78. sub ($k) { faulhaber_sum($k, $m) },
  79. sub ($k) { faulhaber_sum($k, $m+$j) },
  80. );
  81. }
  82. for my $m (0..10) {
  83. my $j = int rand 10;
  84. my $n = int rand 1000;
  85. my $t1 = faulhaber_partial_sum_of_sigma($n, $m, $j);
  86. my $t2 = bernoulli_partial_sum_of_sigma($n, $m, $j);
  87. my $t3 = dirichlet_partial_sum_of_sigma($n, $m, $j);
  88. my $t4 = builtin_dirichlet_sum($n, $m, $j);
  89. die "error: $t1 != $t2" if ($t1 != $t2);
  90. die "error: $t1 != $t3" if ($t1 != $t3);
  91. die "error: $t1 != $t4" if ($t1 != $t4);
  92. say "Sum_{k=1..$n} k^$m * sigma_$j(k) = $t2";
  93. }
  94. die "error" if faulhaber_partial_sum_of_sigma(10**8, 0, 2) != "400685641565621401132515"; # Sum_{k=1..10^8} sigma_2(k)
  95. __END__
  96. Sum_{k=1..955} k^0 * sigma_7(k) = 87199595877187457268469
  97. Sum_{k=1..765} k^1 * sigma_5(k) = 22385163976024509818
  98. Sum_{k=1..805} k^2 * sigma_6(k) = 15993292528868648475167542
  99. Sum_{k=1..477} k^3 * sigma_2(k) = 2374273670858643
  100. Sum_{k=1..522} k^4 * sigma_8(k) = 16674413261032779166355164886215351
  101. Sum_{k=1..983} k^5 * sigma_0(k) = 1180528862233337314
  102. Sum_{k=1..293} k^6 * sigma_1(k) = 11217015502565855041
  103. Sum_{k=1..906} k^7 * sigma_7(k) = 15353361004402823613827018815424339863159897
  104. Sum_{k=1..467} k^8 * sigma_2(k) = 25400023350505369496677066803
  105. Sum_{k=1..801} k^9 * sigma_4(k) = 3343390385697199861864437708422750691782
  106. Sum_{k=1..142} k^10 * sigma_8(k) = 4409116061384423423777822848241899183830