Skip to content

Commit 2c2b8fe

Browse files
committed
new file: Math/partial_sums_of_generalized_gcd-sum_function.pl
1 parent 9623c39 commit 2c2b8fe

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#!/usr/bin/perl
2+
3+
# Daniel "Trizen" Șuteu
4+
# Date: 25 May 2025
5+
# https://github.com/trizen
6+
7+
# A sublinear algorithm for computing the partial sums of the generalized gcd-sum function, using Dirichlet's hyperbola method.
8+
9+
# Generalized Pillai's function:
10+
# pillai(n,k) = Sum_{d|n} mu(n/d) * d^k * tau(d)
11+
12+
# Multiplicative formula for Sum_{1 <= x_1, x_2, ..., x_k <= n} gcd(x_1, x_2, ..., x_k, n)^k:
13+
# a(p^e) = (e - e/p^k + 1) * p^(k*e) = p^((e - 1) * k) * (p^k + e*(p^k - 1))
14+
15+
# The partial sums of the gcd-sum function is defined as:
16+
#
17+
# a(n) = Sum_{k=1..n} Sum_{d|k} d*phi(k/d)
18+
#
19+
# where phi(k) is the Euler totient function.
20+
21+
# Also equivalent with:
22+
# a(n) = Sum_{j=1..n} Sum_{i=1..j} gcd(i, j)
23+
24+
# Based on the formula:
25+
# a(n) = (1/2)*Sum_{k=1..n} phi(k) * floor(n/k) * floor(1+n/k)
26+
27+
# Generalized formula:
28+
# a(n,k) = Sum_{x=1..n} J_k(x) * F_k(floor(n/x))
29+
# where F_k(n) are the Faulhaber polynomials: F_k(n) = Sum_{x=1..n} x^k.
30+
31+
# Example:
32+
# a(10^1) = 122
33+
# a(10^2) = 18065
34+
# a(10^3) = 2475190
35+
# a(10^4) = 317257140
36+
# a(10^5) = 38717197452
37+
# a(10^6) = 4571629173912
38+
# a(10^7) = 527148712519016
39+
# a(10^8) = 59713873168012716
40+
# a(10^9) = 6671288261316915052
41+
42+
# a(10^1, 2) = 1106
43+
# a(10^2, 2) = 1598361
44+
# a(10^3, 2) = 2193987154
45+
# a(10^4, 2) = 2828894776292
46+
# a(10^5, 2) = 3466053625977000
47+
# a(10^6, 2) = 4104546122851466704
48+
# a(10^7, 2) = 4742992578252739471520
49+
# a(10^8, 2) = 5381500783126483704718848
50+
# a(10^9, 2) = 6020011093886996189443484608
51+
52+
# OEIS sequences:
53+
# https://oeis.org/A272718 -- Partial sums of gcd-sum sequence A018804.
54+
# https://oeis.org/A018804 -- Pillai's arithmetical function: Sum_{k=1..n} gcd(k, n).
55+
56+
# See also:
57+
# https://en.wikipedia.org/wiki/Dirichlet_hyperbola_method
58+
# https://trizenx.blogspot.com/2018/11/partial-sums-of-arithmetical-functions.html
59+
60+
use 5.020;
61+
use strict;
62+
use warnings;
63+
64+
use experimental qw(signatures);
65+
use Math::AnyNum qw(faulhaber_sum ipow);
66+
use ntheory qw(jordan_totient sqrtint rootint);
67+
68+
sub partial_sums_of_gcd_sum_function($n, $m) {
69+
70+
my $s = sqrtint($n);
71+
my @totient_sum_lookup = (0);
72+
73+
my $lookup_size = 2 + 2 * rootint($n, 3)**2;
74+
my @jordan_totient = (0);
75+
76+
foreach my $x (1 .. $lookup_size) {
77+
push @jordan_totient, jordan_totient($m, $x);
78+
}
79+
80+
foreach my $i (1 .. $lookup_size) {
81+
$totient_sum_lookup[$i] = $totient_sum_lookup[$i - 1] + $jordan_totient[$i];
82+
}
83+
84+
my %seen;
85+
86+
my sub totient_partial_sum($n) {
87+
88+
if ($n <= $lookup_size) {
89+
return $totient_sum_lookup[$n];
90+
}
91+
92+
if (exists $seen{$n}) {
93+
return $seen{$n};
94+
}
95+
96+
my $s = sqrtint($n);
97+
my $T = ${faulhaber_sum($n, $m)};
98+
99+
foreach my $k (2 .. int($n / ($s + 1))) {
100+
$T -= __SUB__->(int($n / $k));
101+
}
102+
103+
foreach my $k (1 .. $s) {
104+
$T -= (int($n / $k) - int($n / ($k + 1))) * $totient_sum_lookup[$k];
105+
}
106+
107+
$seen{$n} = $T;
108+
}
109+
110+
my $A = 0;
111+
112+
foreach my $k (1 .. $s) {
113+
my $t = int($n / $k);
114+
$A += ${ipow($k, $m)} * totient_partial_sum($t) + $jordan_totient[$k] * ${faulhaber_sum($t, $m)};
115+
}
116+
117+
my $T = ${faulhaber_sum($s, $m)};
118+
my $C = totient_partial_sum($s);
119+
120+
return ($A - $T * $C);
121+
}
122+
123+
foreach my $n (1 .. 8) { # takes less than 1 second
124+
say "a(10^$n, 1) = ", partial_sums_of_gcd_sum_function(10**$n, 1);
125+
}
126+
127+
say '';
128+
129+
foreach my $n (1 .. 8) { # takes less than 1 second
130+
say "a(10^$n, 2) = ", partial_sums_of_gcd_sum_function(10**$n, 2);
131+
}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,7 @@ A nice collection of day-to-day Perl scripts.
10131013
* [Partial sums of gcd-sum function](./Math/partial_sums_of_gcd-sum_function.pl)
10141014
* [Partial sums of gcd-sum function fast](./Math/partial_sums_of_gcd-sum_function_fast.pl)
10151015
* [Partial sums of gcd-sum function faster](./Math/partial_sums_of_gcd-sum_function_faster.pl)
1016+
* [Partial sums of generalized gcd-sum function](./Math/partial_sums_of_generalized_gcd-sum_function.pl)
10161017
* [Partial sums of gpf](./Math/partial_sums_of_gpf.pl)
10171018
* [Partial sums of inverse moebius transform of dedekind function](./Math/partial_sums_of_inverse_moebius_transform_of_dedekind_function.pl)
10181019
* [Partial sums of jordan totient function](./Math/partial_sums_of_jordan_totient_function.pl)

0 commit comments

Comments
 (0)