def moebius_sum(n): r"""Returns the value of the sum of moebius(k) for k = 1, 2, ..., n. Parameters ---------- n : int (n > 0) Returns ------- s : int Raises ------ ValueError If n <= 0. Notes ----- The formula used here is based on the relationship \sum_{d = 1}^{n} M(n/d) = 1, where M(n) = \sum_{k = 1}^{n} moebius(k). We convert this to a recursive formula, whereby using some form of Dirichlet's hyperbola method and memoizing the recursion gives us a sublinear runtime. Examples -------- >>> moebius_sum(10) -1 >>> sum(lists.moebius_list(10)) -1 >>> moebius_sum(0) Traceback (most recent call last): ... ValueError: moebius_sum: Must have n > 0. """ if n <= 0: raise ValueError("moebius_sum: Must have n > 0.") sqrt = int(n**(0.5)) mu_list = lists.moebius_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] + mu_list[i] def M(n): if n in cache: return cache[n] sqrt_n = int(n**(0.5)) + 1 value = n - 1 for d in xrange(2, sqrt_n): value += M(n//d) - M(d - 1) value += mu_list[d]*(n//d - d) cache[n] = 1 - value return cache[n] return M(n)
def test_moebius_list(self): self.assertRaisesRegexp(ValueError, "moebius_list: Must have n > 0.", moebius_list, 0) values = [ 0, 1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0, 1, 1, -1, 0, 0, 1, 0, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, 1, 1, 0, -1, -1, -1, 0, 0, 1, -1, 0, 0, 0, 1, 0, -1, 0, 1, 0, 1, 1, -1, 0, -1, 1, 0, 0, 1, -1, -1, 0, 1, -1, -1, 0, -1, 1, 0, 0, 1, -1, -1, 0, 0, 1, -1, 0, 1, 1, 1, 0, -1, 0, 1, 0, 1, 1, 1, 0, -1, 0, 0, 0, -1, -1, -1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, -1, 0, 1, -1, -1, 0, 1, 1, 0, 0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 0, 0, -1, 0, -1, 0, 0, -1, 1, 0, -1, 1, 1, 0, 1, 0, -1, 0, -1, 1, -1, 0, 0, -1, 0, 0, -1, -1, 0, 0, 1, 1, -1, 0, -1, -1, 1, 0, 1, -1, 1, 0, 0, -1, -1, 0, -1, 1, -1, 0, -1, 0, -1, 0 ] self.assertEqual(moebius_list(200), values)