Closed trizen closed 4 years ago
Interesting. Pari/GP has a bernvec(n) call that returns B_0 .. B_2n as rationals. It points out that while it takes a lot of memory, it is much faster than calling bernfrac(k) repeatedly. It also caches them so bernfrac(k) within the range is immediate.
Looks like Pari/GP also has an internal faulhauber call added in 2018, used by the sumformal() command.
I'll look at this soon.
I have a version of bernvec(n) similar to Pari/GP, along with simple caching.
Timing (+/- 0.1s not unusual):
11.5s mpz/bernfrac version as shown
5.7s adding bernfrac caching in module
2.6s mpz/bernfrac version using 1 line caching B instead of recalc each time
2.3s in module, without caching
1.7s mpq version
1.6s mpz/bernfrac version added a gcd in the loop (similar to what mpq does)
1.0s in module, with caching (call bernvec once)
It in interesting to see that while caching in the module is useful, there is still a bunch of GMP/string/GMP etc. back and forth that takes up a lot of time. Clearly we want to not constantly make new Math::GMPz objects.
Once computing B is done only as needed, and the gcd is added in the loop, there isn't much time difference between the two versions (this is all before module changes).
Pari/GP's bernvec is ~10x faster, probably because their zeta function is a lot faster.
Putting faulhaber_sum(n,k) in the module results in 1.0s if bernvec is called first, 2.3s if not. The bernvec call notices it is a void call merely requesting precalculation and caching of the Bernoulli numbers (Pari does something similar).
I tried both mpz and mpq and the performance is essentially the same if a gcd reduction is added, which is, I believe, something the mpq calls do internally.
API for bernvec, similar to Pari/GP for simplicity: bernvec(n) returns B[0], B[2], ..., B[2n]. E.g. gp:
? bernvec(5)
[1, 1/6, -1/30, 1/42, -1/30, 5/66]
So .... the question I have is what the return value should be for MPU. I am thinking something like:
[ [1,1], [1,6], [-1,30], [1,42], [-1,30], [5,66] ]
Which seems reasonably easy to parse. Easiest would be (1,1,1,6,-1,30,1,42,-1,30,5,66)
but that seems a bit obscure.
faulhaber_sum(n,p) is easy -- just one huge number. I am restricting p to an unsigned long.
I prefer [ [1,1], [1,6], [-1,30], [1,42], [-1,30], [5,66] ]
, which allows easy map
-ing of the result.
I just implemented and documented something close.
mpu 'say join ", ", map { join "/" ,@$_; } Math::Prime::Util::GMP::bernvec(8);'
1/1, 1/6, -1/30, 1/42, -1/30, 5/66, -691/2730, 7/6, -3617/510
The difference being it returns an array rather than an array reference. It should be easy to map.
At some point I should revive my experiment with direct objects, so everything would just be a Math::GMPz object directly if requested.
Pushed.
Great work as always. Thank you very much!
I would like to suggest a new function that implements Faulhaber's formula:
Name suggestion:
Reference implementation:
Alternative implementation, using
bernfrac
and not usingmpq
:The function is particularly useful in computing partial sums of the Dirichlet convolution of an arithmetic function
f(k)
with the positive m-th powers (for a fixedm >= 0
):where
F_m(x) = faulhaber_sum(x, m)
.By applying the Dirichlet hyperbola method, we have:
where
Thanks for considering!