bernoulli_numbers_from_primes_gmpf.pl 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # License: GPLv3
  4. # Date: 14 November 2017
  5. # https://github.com/trizen
  6. # Efficient algorithm for computing 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. # Run times:
  10. # bern( 40_000) - 2.763s
  11. # bern(100_000) - 19.591s
  12. # bern(200_000) - 1 min, 27.21s
  13. use 5.010;
  14. use strict;
  15. use warnings;
  16. use Math::GMPz;
  17. use Math::GMPq;
  18. use Math::GMPf;
  19. use Math::MPFR;
  20. sub bern_from_primes {
  21. my ($n) = @_;
  22. $n == 0 and return Math::GMPq->new('1');
  23. $n == 1 and return Math::GMPq->new('1/2');
  24. $n < 0 and return undef;
  25. $n % 2 and return Math::GMPq->new('0');
  26. state $round = Math::MPFR::MPFR_RNDN();
  27. state $tau = 6.28318530717958647692528676655900576839433879875;
  28. my $log2B = (CORE::log(4 * $tau * $n) / 2 + $n * (CORE::log($n / $tau) - 1)) / CORE::log(2);
  29. my $prec = CORE::int($n + $log2B) +
  30. ($n <= 90 ? (3, 3, 4, 4, 7, 6, 6, 6, 7, 7, 7, 8, 8, 9, 10, 12, 9, 7, 6, 0, 0, 0,
  31. 0, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4)[($n>>1)-1] : 0);
  32. state $d = Math::GMPz::Rmpz_init_nobless();
  33. Math::GMPz::Rmpz_fac_ui($d, $n); # d = n!
  34. my $K = Math::MPFR::Rmpfr_init2($prec);
  35. Math::MPFR::Rmpfr_const_pi($K, $round); # K = pi
  36. Math::MPFR::Rmpfr_pow_si($K, $K, -$n, $round); # K = K^(-n)
  37. Math::MPFR::Rmpfr_mul_z($K, $K, $d, $round); # K = K*d
  38. Math::MPFR::Rmpfr_div_2ui($K, $K, $n - 1, $round); # K = K / 2^(n-1)
  39. # `d` is the denominator of bernoulli(n)
  40. Math::GMPz::Rmpz_set_ui($d, 2); # d = 2
  41. my @primes = (2);
  42. {
  43. # Sieve the primes <= n+1
  44. # Sieve of Eratosthenes + Dana Jacobsen's optimizations
  45. my $N = $n + 1;
  46. my @composite;
  47. my $bound = CORE::int(CORE::sqrt($N));
  48. for (my $i = 3 ; $i <= $bound ; $i += 2) {
  49. if (!exists($composite[$i])) {
  50. for (my $j = $i * $i ; $j <= $N ; $j += 2 * $i) {
  51. undef $composite[$j];
  52. }
  53. }
  54. }
  55. foreach my $k (1 .. ($N - 1) >> 1) {
  56. if (!exists($composite[2 * $k + 1])) {
  57. push(@primes, 2 * $k + 1);
  58. if ($n % (2 * $k) == 0) { # d = d*p iff (p-1)|n
  59. Math::GMPz::Rmpz_mul_ui($d, $d, 2 * $k + 1);
  60. }
  61. }
  62. }
  63. }
  64. state $N = Math::MPFR::Rmpfr_init2_nobless(64);
  65. Math::MPFR::Rmpfr_mul_z($K, $K, $d, $round); # K = K*d
  66. Math::MPFR::Rmpfr_rootn_ui($N, $K, $n - 1, $round); # N = N^(1/(n-1))
  67. Math::MPFR::Rmpfr_ceil($N, $N); # N = ceil(N)
  68. my $bound = Math::MPFR::Rmpfr_get_ui($N, $round); # bound = int(N)
  69. my $t = Math::GMPf::Rmpf_init2($prec); # temporary variable
  70. my $f = Math::GMPf::Rmpf_init2($prec); # approximation to zeta(n)
  71. Math::MPFR::Rmpfr_get_f($f, $K, $round);
  72. for (my $i = 0 ; $primes[$i] <= $bound ; ++$i) { # primes <= N
  73. Math::GMPf::Rmpf_set_ui($t, $primes[$i]); # t = p
  74. Math::GMPf::Rmpf_pow_ui($t, $t, $n); # t = t^n
  75. Math::GMPf::Rmpf_mul($f, $f, $t); # f = f*t
  76. Math::GMPf::Rmpf_sub_ui($t, $t, 1); # t = t-1
  77. Math::GMPf::Rmpf_div($f, $f, $t); # f = f/t
  78. }
  79. my $q = Math::GMPq::Rmpq_init();
  80. Math::GMPf::Rmpf_ceil($f, $f); # f = ceil(f)
  81. Math::GMPq::Rmpq_set_f($q, $f); # q = f
  82. Math::GMPq::Rmpq_set_den($q, $d); # denominator
  83. Math::GMPq::Rmpq_neg($q, $q) if $n % 4 == 0; # q = -q, iff 4|n
  84. return $q; # Bn
  85. }
  86. foreach my $i (0 .. 50) {
  87. printf "B%-3d = %s\n", 2 * $i, bern_from_primes(2 * $i);
  88. }