bernoulli_numbers_from_primes.pl 1.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. #!/usr/bin/perl
  2. # A very high-level computation of the nth-Bernoulli number, using prime numbers.
  3. # Algorithm due to Kevin J. McGown (December 8, 2005)
  4. # See his paper: "Computing Bernoulli Numbers Quickly"
  5. use 5.010;
  6. use strict;
  7. use warnings;
  8. use lib qw(../lib);
  9. #use Math::AnyNum qw(:overload); # can be uncommented
  10. use Math::AnyNum qw(factorial next_prime ceil float);
  11. sub bernoulli_from_primes {
  12. my ($n) = @_;
  13. $n == 0 and return Math::AnyNum->one;
  14. $n == 1 and return Math::AnyNum->new('1/2');
  15. $n < 0 and return Math::AnyNum->nan;
  16. $n % 2 and return Math::AnyNum->zero;
  17. my $tau = 6.28318530717958647692528676655900576839433879875;
  18. my $log2B = (log(4 * $tau * $n) / 2 + $n * log($n) - $n * log($tau) - $n) / log(2);
  19. local $Math::AnyNum::PREC = int($n + $log2B) + ($n <= 90 ? 18 : 0);
  20. my $K = factorial($n) * 2 / Math::AnyNum->tau**$n;
  21. my $d = 1;
  22. for (my $p = 2 ; $p <= $n + 1 ; $p = next_prime($p)) {
  23. if ($n % ($p - 1) == 0) {
  24. $d *= $p;
  25. }
  26. }
  27. my $N = ceil(($K * $d)->root($n - 1));
  28. my $z = 1.0;
  29. for (my $p = 2 ; $p <= $N ; $p = next_prime($p)) {
  30. my $u = float($p)**$n;
  31. $z *= $u / ($u-1);
  32. }
  33. (-1)**($n / 2 + 1) * int(ceil($d * $K * $z)) / $d;
  34. }
  35. foreach my $n (0 .. 100) {
  36. printf "B%-3d = %s\n", 2 * $n, bernoulli_from_primes(2 * $n);
  37. }