bernoulli_numbers_from_factorials_mpz.pl 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. #!/usr/bin/perl
  2. # Author: Daniel "Trizen" Șuteu
  3. # Date: 07 July 2018
  4. # https://github.com/trizen
  5. # A new algorithm for computing Bernoulli numbers.
  6. # Inspired from Norman J. Wildberger video lecture:
  7. # https://www.youtube.com/watch?v=qmMs6tf8qZ8
  8. # See also:
  9. # https://en.wikipedia.org/wiki/Bernoulli_number#Connection_with_Pascal’s_triangle
  10. use 5.010;
  11. use strict;
  12. use warnings;
  13. use Math::GMPq;
  14. use Math::GMPz;
  15. sub bernoulli_numbers {
  16. my ($n) = @_;
  17. my @A = (Math::GMPz::Rmpz_init_set_ui(1));
  18. my @B = (Math::GMPz::Rmpz_init_set_ui(1));
  19. my @F = (Math::GMPz::Rmpz_init_set_ui(1));
  20. foreach my $k (1 .. $n) {
  21. $F[$k] = Math::GMPz::Rmpz_init();
  22. $A[$k] = Math::GMPz::Rmpz_init_set_ui(0);
  23. $B[$k] = Math::GMPz::Rmpz_init_set_ui(1);
  24. Math::GMPz::Rmpz_mul_ui($F[$k], $F[$k - 1], $k);
  25. }
  26. Math::GMPz::Rmpz_mul_ui($F[$n + 1] = Math::GMPz::Rmpz_init(), $F[$n], $n + 1);
  27. my $t = Math::GMPz::Rmpz_init();
  28. foreach my $i (1 .. $n) {
  29. if ($i % 2 != 0 and $i > 1) {
  30. next;
  31. }
  32. foreach my $k (0 .. $i - 1) {
  33. if ($k % 2 != 0 and $k > 1) {
  34. next;
  35. }
  36. my $r = $i - $k + 1;
  37. Math::GMPz::Rmpz_mul($A[$i], $A[$i], $F[$r]);
  38. Math::GMPz::Rmpz_mul($A[$i], $A[$i], $B[$k]);
  39. Math::GMPz::Rmpz_submul($A[$i], $B[$i], $A[$k]);
  40. Math::GMPz::Rmpz_mul($B[$i], $B[$i], $F[$r]);
  41. Math::GMPz::Rmpz_mul($B[$i], $B[$i], $B[$k]);
  42. Math::GMPz::Rmpz_gcd($t, $A[$i], $B[$i]);
  43. Math::GMPz::Rmpz_divexact($A[$i], $A[$i], $t);
  44. Math::GMPz::Rmpz_divexact($B[$i], $B[$i], $t);
  45. }
  46. }
  47. my @R = @A;
  48. for (my $k = 2 ; $k <= $#B ; $k += 2) {
  49. Math::GMPz::Rmpz_mul($A[$k], $A[$k], $F[$k]);
  50. my $bern = Math::GMPq::Rmpq_init();
  51. Math::GMPq::Rmpq_set_num($bern, $A[$k]);
  52. Math::GMPq::Rmpq_set_den($bern, $B[$k]);
  53. Math::GMPq::Rmpq_canonicalize($bern);
  54. $R[$k] = $bern;
  55. }
  56. if ($#R > 0) {
  57. my $bern = Math::GMPq::Rmpq_init();
  58. Math::GMPq::Rmpq_set_num($bern, $A[1]);
  59. Math::GMPq::Rmpq_set_den($bern, $B[1]);
  60. Math::GMPq::Rmpq_canonicalize($bern);
  61. $R[1] = $bern;
  62. }
  63. return @R;
  64. }
  65. my @B = bernoulli_numbers(100); # first 100 Bernoulli numbers
  66. foreach my $i (0 .. $#B) {
  67. say "B($i) = $B[$i]";
  68. }