bernoulli_numbers_from_primes_ntheory.pl 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # License: GPLv3
  4. # Date: 13 May 2017
  5. # https://github.com/trizen
  6. # Computation of the nth-Bernoulli number, using prime numbers.
  7. # Algorithm due to Kevin J. McGown (December 8, 2005)
  8. # See his paper: "Computing Bernoulli Numbers Quickly"
  9. use 5.010;
  10. use strict;
  11. use warnings;
  12. use Math::GMPz;
  13. use Math::GMPq;
  14. use Math::MPFR;
  15. use ntheory qw(is_prob_prime forprimes fordivisors);
  16. sub bern_from_primes {
  17. my ($n) = @_;
  18. $n == 0 and return Math::GMPq->new('1');
  19. $n == 1 and return Math::GMPq->new('1/2');
  20. $n < 0 and return undef;
  21. $n % 2 and return Math::GMPq->new('0');
  22. my $round = Math::MPFR::MPFR_RNDN();
  23. my $tau = 6.28318530717958647692528676655900576839433879875;
  24. my $log2B = (log(4 * $tau * $n) / 2 + $n * log($n) - $n * log($tau) - $n) / log(2);
  25. my $prec = int($n + $log2B) + ($n <= 90 ? 18 : 0);
  26. my $d = Math::GMPz::Rmpz_init();
  27. Math::GMPz::Rmpz_fac_ui($d, $n); # d = n!
  28. my $K = Math::MPFR::Rmpfr_init2($prec);
  29. Math::MPFR::Rmpfr_const_pi($K, $round); # K = pi
  30. Math::MPFR::Rmpfr_pow_ui($K, $K, $n, $round); # K = K^n
  31. Math::MPFR::Rmpfr_mul_2ui($K, $K, $n - 1, $round); # K = K * 2^(n-1)
  32. Math::MPFR::Rmpfr_div_z($K, $K, $d, $round); # K = K / d
  33. Math::MPFR::Rmpfr_ui_div($K, 1, $K, $round); # K = 1 / K
  34. Math::GMPz::Rmpz_set_ui($d, 1); # d = 1
  35. fordivisors { # divisors of n
  36. if (is_prob_prime($_ + 1)) {
  37. Math::GMPz::Rmpz_mul_ui($d, $d, $_ + 1); # d = d * p, where (p-1)|n
  38. }
  39. } $n;
  40. my $N = Math::MPFR::Rmpfr_init2(64);
  41. Math::MPFR::Rmpfr_mul_z($N, $K, $d, $round); # N = K * d
  42. Math::MPFR::Rmpfr_rootn_ui($N, $N, $n - 1, $round); # N = K^(1/(n-1))
  43. Math::MPFR::Rmpfr_ceil($N, $N); # N = ceil(N)
  44. $N = Math::MPFR::Rmpfr_get_ui($N, $round);
  45. my $z = Math::MPFR::Rmpfr_init2($prec); # zeta(n)
  46. my $u = Math::GMPz::Rmpz_init(); # p^n
  47. Math::MPFR::Rmpfr_set_ui($z, 1, $round); # z = 1
  48. forprimes { # primes <= N
  49. Math::GMPz::Rmpz_ui_pow_ui($u, $_, $n); # u = p^n
  50. Math::MPFR::Rmpfr_mul_z($z, $z, $u, $round); # z = z*u
  51. Math::GMPz::Rmpz_sub_ui($u, $u, 1); # u = u-1
  52. Math::MPFR::Rmpfr_div_z($z, $z, $u, $round); # z = z/u
  53. } $N;
  54. Math::MPFR::Rmpfr_mul($z, $z, $K, $round); # z = z * K
  55. Math::MPFR::Rmpfr_mul_z($z, $z, $d, $round); # z = z * d
  56. Math::MPFR::Rmpfr_ceil($z, $z); # z = ceil(z)
  57. my $q = Math::GMPq::Rmpq_init();
  58. Math::GMPq::Rmpq_set_den($q, $d); # denominator
  59. Math::MPFR::Rmpfr_get_z($d, $z, $round);
  60. Math::GMPz::Rmpz_neg($d, $d) if $n % 4 == 0; # d = -d, iff 4|n
  61. Math::GMPq::Rmpq_set_num($q, $d); # numerator
  62. return $q; # Bn
  63. }
  64. foreach my $i (0 .. 50) {
  65. printf "B%-3d = %s\n", 2 * $i, bern_from_primes(2 * $i);
  66. }