sum_of_prime_powers.pl 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #!/usr/bin/perl
  2. # Three sublinear algorithms for computing the sum of prime powers <= n,
  3. # based on the sublinear algorithm for computing the sum of primes <= n.
  4. # See also:
  5. # https://oeis.org/A074793
  6. use 5.036;
  7. use Math::GMPz;
  8. use ntheory qw(:all);
  9. use Math::Prime::Util::GMP qw(faulhaber_sum);
  10. sub sum_of_primes ($n, $k = 1) { # Sum_{p prime <= n} p^k
  11. return sum_primes($n) if ($k == 1); # optimization
  12. $n > ~0 and return undef;
  13. $n <= 1 and return 0;
  14. my $r = sqrtint($n);
  15. my @V = map { divint($n, $_) } 1 .. $r;
  16. push @V, CORE::reverse(1 .. $V[-1] - 1);
  17. my $t = Math::GMPz::Rmpz_init_set_ui(0);
  18. my $u = Math::GMPz::Rmpz_init();
  19. my %S;
  20. @S{@V} = map { Math::GMPz::Rmpz_init_set_str(faulhaber_sum($_, $k), 10) } @V;
  21. foreach my $p (2 .. $r) {
  22. if ($S{$p} > $S{$p - 1}) {
  23. my $cp = $S{$p - 1};
  24. my $p2 = $p * $p;
  25. Math::GMPz::Rmpz_ui_pow_ui($t, $p, $k);
  26. foreach my $v (@V) {
  27. last if ($v < $p2);
  28. Math::GMPz::Rmpz_sub($u, $S{divint($v, $p)}, $cp);
  29. Math::GMPz::Rmpz_submul($S{$v}, $u, $t);
  30. }
  31. }
  32. }
  33. $S{$n} - 1;
  34. }
  35. sub sum_of_prime_powers ($n) {
  36. # a(n) = Sum_{p prime <= n} p
  37. # b(n) = Sum_{p prime <= n^(1/2)} p^2
  38. # c(n) = Sum_{p prime <= n^(1/3)} f(p)
  39. # sum_of_prime_powers(n) = a(n) + b(n) + c(n)
  40. my $ps1 = sum_of_primes($n);
  41. my $ps2 = sum_of_primes(sqrtint($n), 2);
  42. # f(p) = (Sum_{k=1..floor(log_p(n))} p^k) - p^2 - p
  43. # = (p^(1+floor(log_p(n))) - 1)/(p-1) - p^2 - p - 1
  44. my $ps3 = 0;
  45. foreach my $p (@{primes(rootint($n, 3))}) {
  46. $ps3 += divint(powint($p, logint($n, $p) + 1) - 1, $p - 1) - $p * $p - $p - 1;
  47. }
  48. return vecsum($ps1, $ps2, $ps3);
  49. }
  50. sub sum_of_prime_powers_2 ($n) {
  51. # a(n) = Sum_{p prime <= n} p
  52. # b(n) = Sum_{p prime <= n^(1/2)} f(p)
  53. # sum_of_prime_powers(n) = a(n) + b(n)
  54. my $ps1 = sum_of_primes($n);
  55. # f(p) = (Sum_{k=1..floor(log_p(n))} p^k) - p
  56. # = (p^(1+floor(log_p(n))) - 1)/(p-1) - p - 1
  57. my $ps2 = 0;
  58. forprimes {
  59. $ps2 += divint(powint($_, logint($n, $_) + 1) - 1, $_ - 1) - $_ - 1;
  60. } sqrtint($n);
  61. return vecsum($ps1, $ps2);
  62. }
  63. sub sum_of_prime_powers_3 ($n) {
  64. # a(n) = Sum_{k=1..floor(log_2(n))} Sum_{p prime <= n^(1/k)} p^k.
  65. vecsum(map { sum_of_primes(rootint($n, $_), $_) } 1 .. logint($n, 2));
  66. }
  67. foreach my $n (0 .. 10) {
  68. say "a(10^$n) = ", sum_of_prime_powers(powint(10, $n));
  69. }
  70. foreach my $k (1 .. 100) {
  71. my $n = int(rand(1e3)) + 1;
  72. my $x = sum_of_prime_powers($n);
  73. my $y = sum_of_prime_powers_2($n);
  74. my $z = sum_of_prime_powers_3($n);
  75. $x == $y or die "error";
  76. $x == $z or die "error";
  77. }
  78. __END__
  79. a(10^0) = 0
  80. a(10^1) = 38
  81. a(10^2) = 1375
  82. a(10^3) = 82674
  83. a(10^4) = 5850315
  84. a(10^5) = 457028152
  85. a(10^6) = 37610438089
  86. a(10^7) = 3204814813355
  87. a(10^8) = 279250347324393
  88. a(10^9) = 24740607755154524
  89. a(10^10) = 2220853189506845580
  90. a(10^11) = 201467948093608962539
  91. a(10^12) = 18435613572072500152927