Math-AnyNum
view release on metacpan or search on metacpan
examples/partial_sums_of_sigma_function.pl view on Meta::CPAN
#!/usr/bin/perl
# Author: Daniel "Trizen" Èuteu
# Date: 10 Novermber 2018
# https://github.com/trizen
# A generalized formula with O(sqrt(n)) complexity for computing the partial-sum of `k^m * sigma_j(k)`, for `1 <= k <= n`:
#
# Sum_{k=1..n} k^m * sigma_j(k)
#
# for any fixed integers m >= 0 and j >= 0.
# Formula:
# 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))))
# + Sum_{k=1..floor(n/(floor(sqrt(n))+1))} k^(m+j) * F(m, floor(n/k))
#
# where F(n,x) is Faulhaber's formula for `Sum_{k=1..x} k^n`, defined in terms of Bernoulli polynomials as:
#
# F(n, x) = (Bernoulli(n+1, x+1) - Bernoulli(n+1)) / (n+1)
#
# where Bernoulli(n,x) are the Bernoulli polynomials and Bernoulli(n) is the n-th Bernoulli number.
# See also:
# https://en.wikipedia.org/wiki/Divisor_function
# https://en.wikipedia.org/wiki/Faulhaber%27s_formula
# https://en.wikipedia.org/wiki/Bernoulli_polynomials
# https://trizenx.blogspot.com/2018/11/partial-sums-of-arithmetical-functions.html
use 5.020;
use strict;
use warnings;
use lib qw(../lib);
use experimental qw(signatures);
use Math::AnyNum qw(isqrt idiv ipow bernoulli faulhaber_sum dirichlet_sum);
sub faulhaber_partial_sum_of_sigma($n, $m, $j) { # using Faulhaber's formula
my $total = 0;
my $s = isqrt($n);
my $u = int($n / ($s + 1));
for my $k (1 .. $s) {
$total += faulhaber_sum($k, $m) * (
faulhaber_sum(int($n/$k), $m+$j)
- faulhaber_sum(int($n/($k+1)), $m+$j)
);
}
for my $k (1 .. $u) {
$total += ipow($k, $m+$j) * faulhaber_sum(int($n/$k), $m);
}
return $total;
}
sub bernoulli_partial_sum_of_sigma($n, $m, $j) { # using Bernoulli polynomials
my $total = 0;
my $s = isqrt($n);
my $u = int($n / ($s + 1));
for my $k (1 .. $s) {
$total += (
bernoulli($m+1, $k+1)
- bernoulli($m+1)
)/($m+1)
* (
bernoulli($m+$j+1, 1+int($n/$k))
- bernoulli($m+$j+1, 1+int($n/($k+1)))
)/($m+$j+1);
}
for my $k (1 .. $u) {
$total += ipow($k, $m+$j) * (bernoulli($m+1, 1+int($n/$k)) - bernoulli($m+1)) / ($m+1);
}
return $total;
}
sub dirichlet_partial_sum_of_sigma ($n, $m, $j) { # using Dirichlet's hyperbola method
my $total = 0;
my $s = isqrt($n);
for my $k (1 .. $s) {
$total += ipow($k, $m) * faulhaber_sum(int($n/$k), $m+$j);
$total += ipow($k, $m+$j) * faulhaber_sum(int($n/$k), $m);
}
$total -= faulhaber_sum($s, $m) * faulhaber_sum($s, $j+$m);
return $total;
}
( run in 0.441 second using v1.01-cache-2.11-cpan-e1769b4cff6 )