bernoulli_numbers_from_zeta.pl 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # License: GPLv3
  4. # Date: 13 October 2016
  5. # Website: https://github.com/trizen
  6. # Computation of the nth-Bernoulli number, using the Zeta function.
  7. use 5.010;
  8. use strict;
  9. use warnings;
  10. use Math::AnyNum;
  11. sub bern_zeta {
  12. my ($n) = @_;
  13. # B(n) = (-1)^(n/2 + 1) * zeta(n)*2*n! / (2*pi)^n
  14. $n == 0 and return Math::AnyNum->one;
  15. $n == 1 and return Math::AnyNum->new('1/2');
  16. $n < 0 and return Math::AnyNum->nan;
  17. $n % 2 and return Math::AnyNum->zero;
  18. my $ROUND = Math::MPFR::MPFR_RNDN();
  19. # The required precision is: O(n*log(n))
  20. my $prec = (
  21. $n <= 156
  22. ? CORE::int($n * CORE::log($n) + 1)
  23. : CORE::int($n * CORE::log($n) / CORE::log(2) - 3 * $n)
  24. );
  25. my $f = Math::MPFR::Rmpfr_init2($prec);
  26. Math::MPFR::Rmpfr_zeta_ui($f, $n, $ROUND); # f = zeta(n)
  27. my $z = Math::GMPz::Rmpz_init();
  28. Math::GMPz::Rmpz_fac_ui($z, $n); # z = n!
  29. Math::GMPz::Rmpz_div_2exp($z, $z, $n - 1); # z = z / 2^(n-1)
  30. Math::MPFR::Rmpfr_mul_z($f, $f, $z, $ROUND); # f = f*z
  31. my $p = Math::MPFR::Rmpfr_init2($prec);
  32. Math::MPFR::Rmpfr_const_pi($p, $ROUND); # p = PI
  33. Math::MPFR::Rmpfr_pow_ui($p, $p, $n, $ROUND); # p = p^n
  34. Math::MPFR::Rmpfr_div($f, $f, $p, $ROUND); # f = f/p
  35. Math::GMPz::Rmpz_set_ui($z, 1); # z = 1
  36. Math::GMPz::Rmpz_mul_2exp($z, $z, $n + 1); # z = 2^(n+1)
  37. Math::GMPz::Rmpz_sub_ui($z, $z, 2); # z = z-2
  38. Math::MPFR::Rmpfr_mul_z($f, $f, $z, $ROUND); # f = f*z
  39. Math::MPFR::Rmpfr_round($f, $f); # f = [f]
  40. my $q = Math::GMPq::Rmpq_init();
  41. Math::MPFR::Rmpfr_get_q($q, $f); # q = f
  42. Math::GMPq::Rmpq_set_den($q, $z); # q = q/z
  43. Math::GMPq::Rmpq_canonicalize($q); # remove common factors
  44. Math::GMPq::Rmpq_neg($q, $q) if $n % 4 == 0; # q = -q (iff 4|n)
  45. Math::AnyNum->new($q);
  46. }
  47. foreach my $i (0 .. 50) {
  48. printf "B%-3d = %s\n", 2 * $i, bern_zeta(2 * $i);
  49. }