def euler_phi_weighted_sum(n): """Returns the value of the sum of k*euler_phi(k) for k = 1, 2, ..., n. Parameters * n: int (n > 0) Returns: * s: int Raises: * ValueError: If n <= 0. Examples: >>> euler_phi_weighted_sum(100) 203085 >>> sum(k*euler_phi(k) for k in xrange(1, 101)) 203085 >>> euler_phi_weighted_sum(0) Traceback (most recent call last): ... ValueError: euler_phi_weighted_sum: Must have n > 0. Details: Let T(n) = \sum_{k = 1}^{n} k*euler_phi(k). We use the relationship \sum_{d = 1}^{n} d*T(n/d) = n*(n + 1)*(2*n + 1) / 6 to form a recurrence for T(n). Using a form of Dirichlet's hyperbola method and memoizing the recursion gives us a sublinear runtime. """ if n <= 0: raise ValueError("euler_phi_weighted_sum: Must have n > 0.") sqrt = int(n**(0.5)) totients = lists.euler_phi_list(sqrt) # for i <= sqrt(n), compute the sum directly cache = {1: 1} for i in xrange(2, sqrt + 1): cache[i] = cache[i - 1] + i*totients[i] def T(n): if n in cache: return cache[n] sqrt_n = int(n**(0.5)) + 1 s1 = sum(d*totients[d]*(n//d - d + 1)*(n//d + d)//2 for d in xrange(1, sqrt_n)) s2 = sum(d*(T(n//d) - T(d - 1)) for d in xrange(2, sqrt_n)) s3 = sum(d*d*totients[d] for d in xrange(1, sqrt_n)) cache[n] = n*(n + 1)*(2*n + 1)//6 - (s1 + s2 - s3) return cache[n] return T(n)
def test_euler_phi_list(self): self.assertRaisesRegexp(ValueError, "euler_phi_list: Must have n > 0.", euler_phi_list, 0) values = [ 0, 1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18, 8, 12, 10, 22, 8, 20, 12, 18, 12, 28, 8, 30, 16, 20, 16, 24, 12, 36, 18, 24, 16, 40, 12, 42, 20, 24, 22, 46, 16, 42, 20, 32, 24, 52, 18, 40, 24, 36, 28, 58, 16, 60, 30, 36, 32, 48, 20, 66, 32, 44, 24, 70, 24, 72, 36, 40, 36, 60, 24, 78, 32, 54, 40, 82, 24, 64, 42, 56, 40, 88, 24, 72, 44, 60, 46, 72, 32, 96, 42, 60, 40, 100, 32, 102, 48, 48, 52, 106, 36, 108, 40, 72, 48, 112, 36, 88, 56, 72, 58, 96, 32, 110, 60, 80, 60, 100, 36, 126, 64, 84, 48, 130, 40, 108, 66, 72, 64, 136, 44, 138, 48, 92, 70, 120, 48, 112, 72, 84, 72, 148, 40, 150, 72, 96, 60, 120, 48, 156, 78, 104, 64, 132, 54, 162, 80, 80, 82, 166, 48, 156, 64, 108, 84, 172, 56, 120, 80, 116, 88, 178, 48, 180, 72, 120, 88, 144, 60, 160, 92, 108, 72, 190, 64, 192, 96, 96, 84, 196, 60, 198, 80 ] self.assertEqual(euler_phi_list(200), values)
def euler_phi_sum(n): r"""Returns the value of the sum of euler_phi(k) for k = 1, 2, ..., n. Parameters ---------- n : int (n > 0) Returns ------- s : int Raises ------ ValueError If n <= 0. Notes ----- Let S(n) = \sum_{k = 1}^{n} \phi(k). We use the identity S(n) = n*(n + 1)/2 - \sum_{d = 2}^{n} S(n/d) to compute the value. By applying a variant of Dirichlet's Hyperbola Method, we are able to cut the limits of summation, and by memoizing the recursion, we are able to achieve a sublinear runtime. See Section 4.2 of "The Prime Numbers and Their Distribution" by Mendes, et al for the Hyperbola Method. See section 4.9 equation 4.60 in "Concrete Mathematics" by Graham, et al for a proof of the identity for S(n). Examples -------- >>> euler_phi_sum(10) 32 >>> sum(lists.euler_phi_list(10)) 32 >>> euler_phi_sum(10**5) 3039650754 >>> euler_phi_sum(0) Traceback (most recent call last): ... ValueError: euler_phi_sum: Must have n > 0. """ if n <= 0: raise ValueError("euler_phi_sum: Must have n > 0.") sqrt = int(n**(0.5)) phi_list = lists.euler_phi_list(sqrt) # for i <= sqrt(n), compute the sum directly cache = {1: 1} for i in xrange(2, sqrt + 1): cache[i] = cache[i - 1] + phi_list[i] def S(n): if n in cache: return cache[n] sqrt_n = int(n**(0.5)) value = n - sqrt_n*cache[sqrt_n] for d in xrange(2, sqrt_n + 1): value += (S(n//d) + phi_list[d]*(n//d)) cache[n] = n*(n + 1)//2 - value return cache[n] return S(n)