faulhaber_s_formula.pl 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #!/usr/bin/perl
  2. # The formula for calculating the sum of consecutive
  3. # numbers raised to a given power, such as:
  4. # 1^p + 2^p + 3^p + ... + n^p
  5. # where p is a positive integer.
  6. # See also:
  7. # https://en.wikipedia.org/wiki/Faulhaber%27s_formula
  8. use 5.020;
  9. use strict;
  10. use warnings;
  11. use lib qw(../lib);
  12. use experimental qw(signatures);
  13. use Math::AnyNum qw(:overload binomial factorial faulhaber_sum);
  14. # This function returns the n-th Bernoulli number
  15. # See: https://en.wikipedia.org/wiki/Bernoulli_number
  16. sub bernoulli_number ($n) {
  17. return -1/2 if ($n == 1);
  18. return 0 if ($n % 2 == 1);
  19. my @B = (1);
  20. foreach my $i (1 .. $n) {
  21. foreach my $k (0 .. $i - 1) {
  22. $B[$i] //= 0;
  23. $B[$i] -= $B[$k] / factorial($i - $k + 1);
  24. }
  25. }
  26. return $B[-1] * factorial($#B);
  27. }
  28. # The Faulhaber's formula
  29. # See: https://en.wikipedia.org/wiki/Faulhaber%27s_formula
  30. sub faulhaber_s_formula ($n, $p) {
  31. my $sum = 0;
  32. for my $k (0 .. $p) {
  33. $sum += (-1)**$k * binomial($p + 1, $k) * bernoulli_number($k) * $n**($p - $k + 1);
  34. }
  35. return $sum / ($p + 1);
  36. }
  37. # Alternate expression using Bernoulli polynomials
  38. # See: https://en.wikipedia.org/wiki/Faulhaber%27s_formula#Alternate_expressions
  39. sub bernoulli_polynomials ($n, $x) {
  40. my $sum = 0;
  41. for my $k (0 .. $n) {
  42. $sum += binomial($n, $k) * bernoulli_number($n - $k) * $x**$k;
  43. }
  44. return $sum;
  45. }
  46. sub faulhaber_s_formula_2 ($n, $p) {
  47. (bernoulli_polynomials($p + 1, $n + 1) - bernoulli_number($p + 1)) / ($p + 1);
  48. }
  49. foreach my $m (1 .. 15) {
  50. my $n = int(rand(100));
  51. my $t1 = faulhaber_s_formula($n, $m);
  52. my $t2 = faulhaber_s_formula_2($n, $m);
  53. my $t3 = faulhaber_sum($n, $m);
  54. say "Sum_{k=1..$n} k^$m = $t1";
  55. die "error: $t1 != $t2" if ($t1 != $t2);
  56. die "error: $t1 != $t3" if ($t1 != $t3);
  57. }
  58. __END__
  59. Sum_{k=1..79} k^1 = 3160
  60. Sum_{k=1..41} k^2 = 23821
  61. Sum_{k=1..90} k^3 = 16769025
  62. Sum_{k=1..52} k^4 = 79743482
  63. Sum_{k=1..55} k^5 = 4868894800
  64. Sum_{k=1..20} k^6 = 216455810
  65. Sum_{k=1..87} k^7 = 429380261081904
  66. Sum_{k=1..38} k^8 = 20607480744851
  67. Sum_{k=1..45} k^9 = 3796008746347665
  68. Sum_{k=1..91} k^10 = 341980696482343462726