partial_sum_of_the_alternating_sum_of_divisors.sf 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. #!/usr/bin/ruby
  2. # Author: Trizen
  3. # Date: 11 May 2024
  4. # https://github.com/trizen
  5. # A sublinear formula for computing the partial sums alternating sum of divisors function (A206369), using Dirichlet's hyperbola method.
  6. # Formula:
  7. #
  8. # a(n) = Sum_{k=1..n} Sum_{d|k} (-1)^bigomega(k/d) * d
  9. # = Sum_{k=1..n} Sum_{d|k, d is a perfect square} phi(k/d)
  10. # = (1/2) * Sum_{k=1..n} (-1)^bigomega(k) * floor(n/k) * floor(n/k + 1)
  11. # = (Sum_{k=1..floor(n^(1/4))} S(floor(n/k^2))) + (Sum_{k=1..floor(sqrt(n))} phi(k) * floor(sqrt(floor(n/k)))) - S(floor(sqrt(n))) * floor(n^(1/4))
  12. #
  13. # where S(n) = Sum_{k=1..n} phi(k), where `phi` is the Euler totient function.
  14. # Example:
  15. # a(10^1) = 35
  16. # a(10^2) = 3311
  17. # a(10^3) = 329283
  18. # a(10^4) = 32900704
  19. # a(10^5) = 3289890783
  20. # a(10^6) = 328986877961
  21. # a(10^7) = 32898683779520
  22. # a(10^8) = 3289868149892131
  23. # a(10^9) = 328986813691207320
  24. # a(10^10) = 32898681337806276406
  25. # Asymptotic formula due to Tóth, 2013:
  26. # a(n) ~ (Pi^2/30) * n^2.
  27. # OEIS sequences:
  28. # https://oeis.org/A370905 -- Partial sums of the alternating sum of divisors function (A206369).
  29. # https://oeis.org/A002088 -- Sum of totient function: a(n) = Sum_{k=1..n} phi(k), cf. A000010.
  30. # See also:
  31. # https://en.wikipedia.org/wiki/Dirichlet_hyperbola_method
  32. # https://trizenx.blogspot.com/2018/11/partial-sums-of-arithmetical-functions.html
  33. func euler_totient_partial_sum (n) { # using Dirichlet's hyperbola method
  34. var total = 0
  35. var s = n.isqrt
  36. for k in (1..s) {
  37. total += k*mertens(idiv(n,k))
  38. }
  39. s.each_squarefree {|k|
  40. total += moebius(k)*faulhaber(idiv(n,k), 1)
  41. }
  42. total -= mertens(s)*faulhaber(s, 1)
  43. return total
  44. }
  45. func partial_sums_of_asod_1(n) { # sublinear complexity
  46. func S(n) {
  47. return euler_totient_partial_sum(n)
  48. }
  49. sum(1..n.iroot(4), {|k|
  50. S(floor(n / k**2))
  51. }) + sum(1..n.isqrt, {|k|
  52. phi(k) * isqrt(floor(n/k))
  53. }) - S(n.isqrt)*n.iroot(4)
  54. }
  55. func partial_sums_of_asod_2(n) { # sublinear time (slow-ish)
  56. sum(1..n.isqrt, {|k|
  57. k*liouville_sum(idiv(n,k)) + liouville(k)*faulhaber_sum(idiv(n,k), 1)
  58. }) - liouville_sum(n.isqrt)*faulhaber_sum(n.isqrt, 1)
  59. }
  60. func partial_sums_of_asod_test(n) { # just for testing
  61. sum(1..n, {|k| liouville(k) * faulhaber(idiv(n,k), 1) })
  62. }
  63. for m in (0..10) {
  64. var n = 1000.irand
  65. var t1 = partial_sums_of_asod_1(n)
  66. var t2 = partial_sums_of_asod_2(n)
  67. var t3 = partial_sums_of_asod_test(n)
  68. assert_eq(t1, t2, [n, t1, t2])
  69. assert_eq(t1, t3, [n, t1, t3])
  70. say "Sum_{k=1..#{n}} Sum_{d|k} λ(k/d) * d = #{t2}"
  71. }