-
Notifications
You must be signed in to change notification settings - Fork 33
/
harmonic_numbers_from_digamma.pl
executable file
·77 lines (59 loc) · 2.09 KB
/
harmonic_numbers_from_digamma.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/usr/bin/perl
# Daniel "Trizen" Șuteu
# License: GPLv3
# Date: 26 May 2017
# Edit: 04 November 2023
# https://github.com/trizen
# Computation of the nth-harmonic number, using the digamma(x) function.
# See also:
# https://en.wikipedia.org/wiki/Harmonic_number
use 5.010;
use strict;
use warnings;
use Math::GMPz qw();
use Math::GMPq qw();
use Math::MPFR qw();
use Math::AnyNum qw();
use Math::Prime::Util::GMP qw();
sub harmonic {
my ($n) = @_;
$n == 0 and return Math::AnyNum->zero;
$n == 1 and return Math::AnyNum->one;
state $tau = 6.28318530717958647692528676655900576839433879875;
state $gamma = 0.57721566490153286060651209008240243104215933594;
#my $log2_Hn = (-$n + $n * log($n) + (log($tau) + log($n)) / 2 + log(log($n) + $gamma)) / log(2);
my $log2_Hn = $n / log(2) + sqrt($n);
my $prec = int($log2_Hn + 3);
my $round = Math::MPFR::MPFR_RNDN();
my $r = Math::MPFR::Rmpfr_init2($prec);
Math::MPFR::Rmpfr_set_ui($r, $n + 1, $round);
Math::MPFR::Rmpfr_digamma($r, $r, $round);
my $t = Math::MPFR::Rmpfr_init2($prec);
Math::MPFR::Rmpfr_const_euler($t, $round);
Math::MPFR::Rmpfr_add($r, $r, $t, $round);
my $num = Math::GMPz::Rmpz_init();
my $den = Math::GMPz::Rmpz_init();
Math::GMPz::Rmpz_set_str($den, Math::Prime::Util::GMP::consecutive_integer_lcm($n), 10);
Math::MPFR::Rmpfr_mul_z($r, $r, $den, $round);
Math::MPFR::Rmpfr_round($r, $r);
Math::MPFR::Rmpfr_get_z($num, $r, $round);
my $q = Math::GMPq::Rmpq_init();
Math::GMPq::Rmpq_set_num($q, $num);
Math::GMPq::Rmpq_set_den($q, $den);
Math::GMPq::Rmpq_canonicalize($q);
Math::AnyNum->new($q);
}
foreach my $i (0 .. 30) {
printf "%20s / %-20s\n", harmonic($i)->nude;
harmonic($i) == Math::AnyNum::harmonic($i) or die "error";
}
foreach my $i (2863, 7000) {
harmonic($i) == Math::AnyNum::harmonic($i) or die "error";
}
__END__
# Extra testing
foreach my $k (1 .. 20) {
my $i = int(rand($k * 1e3));
say "Testing: $i";
harmonic($i) == Math::AnyNum::harmonic($i) or die "error";
}