487 Sums of power sums -- v2.pl 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 19 August 2020
  4. # https://github.com/trizen
  5. # https://projecteuler.net/problem=487
  6. # Runtime: 47.519s
  7. use 5.020;
  8. use strict;
  9. use warnings;
  10. use experimental qw(signatures);
  11. use Math::GMPz;
  12. use ntheory qw(forprimes submod addmod divmod mulmod powmod);
  13. use Math::Prime::Util::GMP qw(bernvec modint);
  14. say ":: Computing Bernoulli numbers...";
  15. my $power = 1e4;
  16. my @bernoulli = bernvec(($power + 1) >> 1);
  17. splice(@bernoulli, 1, 0, [1, 2]);
  18. say ":: Applying Faulhaber's formula...";
  19. my $B0 = [1, 1];
  20. my $B1 = [1, 2];
  21. @bernoulli = map {
  22. [map { Math::GMPz->new($_) } @$_]
  23. } @bernoulli;
  24. {
  25. my @cache;
  26. my $t = Math::GMPz::Rmpz_init_nobless();
  27. sub binomialmod ($n, $k, $m) {
  28. my $bin = (
  29. $cache[$n][$k] //= do {
  30. my $z = Math::GMPz::Rmpz_init_nobless();
  31. Math::GMPz::Rmpz_bin_uiui($z, $n, $k);
  32. $z;
  33. }
  34. );
  35. Math::GMPz::Rmpz_mod_ui($t, $bin, $m);
  36. }
  37. }
  38. # Faulhaber's formula (modulo some m)
  39. # See: https://en.wikipedia.org/wiki/Faulhaber%27s_formula
  40. sub faulhabermod ($n, $p, $m) {
  41. my $sum = 0;
  42. for my $k (0 .. $p) {
  43. $k % 2 == 0 or $k == 1 or next;
  44. my $B =
  45. ($k == 0) ? $B0
  46. : ($k == 1) ? $B1
  47. : $bernoulli[($k >> 1) + 1];
  48. $sum += mulmod(divmod(mulmod(binomialmod($p + 1, $k, $m), $B->[0] % $m, $m), $B->[1] % $m, $m),
  49. powmod($n, $p - $k + 1, $m), $m);
  50. $sum %= $m;
  51. }
  52. divmod($sum, $p + 1, $m);
  53. }
  54. # Efficient formula for computing:
  55. # S_p(n) = Sum_{k=1..n} Sum_{j=1..k} j^p
  56. # for some positive integer p.
  57. # The formula is:
  58. # S_p(n) = (n+1) * F_p(n) - F_(p+1)(n)
  59. # where F_n(x) are the Faulhaber polynomials.
  60. sub S ($n, $p, $m) {
  61. submod(mulmod($n + 1, faulhabermod($n, $p, $m), $m), faulhabermod($n, $p + 1, $m), $m);
  62. }
  63. # Sanity check
  64. if (S(100, 4, 1e9 + 1) != (35375333830 % (1e9 + 1))) {
  65. die "Error for S_4(100)";
  66. }
  67. my $sum = 0;
  68. forprimes { # there are only 100 primes in this range
  69. $sum += S(1e12, $power, $_);
  70. } 2e9, 2e9 + 2000;
  71. say ":: Total: $sum";