795 Alternating GCD Sum.pl 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. #!/usr/bin/perl
  2. # Author: Trizen
  3. # Date: 02 May 2022
  4. # https://github.com/trizen
  5. # Alternating GCD Sum
  6. # https://projecteuler.net/problem=795
  7. # Formula:
  8. # g(4) = 6
  9. # g(n) = -n, if n is odd
  10. # g(2^k * n) = (g2(2^k) - 2^k) * Prod_{p^e | n} g2(p^e), where n is odd
  11. # where:
  12. # g2(n) = A078430(n)
  13. # = Sum_{d|n} d * phi(n/d) * f(n/d)
  14. # where:
  15. # f(n) = A000188(n)
  16. # = Prod_{p^e | n} p^floor(e/2)
  17. # Then:
  18. # G(n) = Sum_{k=1..n} g(k)
  19. # See also:
  20. # https://oeis.org/A078430 -- Sum of gcd(k^2,n) for 1 <= k <= n.
  21. # https://oeis.org/A199084 -- Sum_{k=1..n} (-1)^(k+1) gcd(k,n).
  22. # https://oeis.org/A000188 -- Number of solutions to x^2 == 0 (mod n).
  23. # Runtime: ~22 seconds.
  24. use 5.020;
  25. use strict;
  26. use warnings;
  27. use ntheory qw(:all);
  28. use experimental qw(signatures);
  29. sub f ($n) { # OEIS: A000188
  30. vecprod(map { ($_->[1] == 1) ? 1 : powint($_->[0], $_->[1] >> 1) } factor_exp($n));
  31. }
  32. my %cache;
  33. sub g2 ($n) { # OEIS: A078430
  34. if (is_prime($n)) {
  35. return ($n + ($n - 1));
  36. }
  37. if (exists $cache{$n}) {
  38. return $cache{$n};
  39. }
  40. my $sum = 0;
  41. foreach my $d (divisors($n)) {
  42. my $nod = divint($n, $d);
  43. $sum = addint($sum, vecprod($d, euler_phi($nod), f($nod)));
  44. }
  45. $cache{$n} = $sum;
  46. }
  47. sub g ($n) {
  48. return 6 if $n == 4;
  49. if ($n % 2 == 1) { # n is odd
  50. return -$n;
  51. }
  52. if (is_prime($n >> 1)) {
  53. return $n - 1;
  54. }
  55. my $v = valuation($n, 2);
  56. my $t = $n >> $v;
  57. my $w = g2(1 << $v) - (1 << $v);
  58. return $w if ($t == 1);
  59. return mulint($w, vecprod(map { g2(powint($_->[0], $_->[1])) } factor_exp($t)));
  60. }
  61. sub G ($n) {
  62. my $sum = 0;
  63. foreach my $k (1 .. $n) {
  64. $sum += g($k);
  65. }
  66. return $sum;
  67. }
  68. use Test::More tests => 7;
  69. is(g(2 * 47), 93);
  70. is(g(97), -97);
  71. is(g(3 * 31), -93);
  72. is(G(100), 7111);
  73. is(G(200), 36268);
  74. is(G(300), 89075);
  75. is(G(1234), 2194708);
  76. say G(12345678);