1414
1515How many fractions lie between 1/3 and 1/2 in the sorted set
1616of reduced proper fractions for d ≤ 12,000?
17- """
1817
19- from math import gcd
18+ References:
19+ - https://en.wikipedia.org/wiki/M%C3%B6bius_function
20+ - https://en.wikipedia.org/wiki/Farey_sequence
21+ """
2022
2123
2224def solution (max_d : int = 12_000 ) -> int :
2325 """
24- Returns number of fractions lie between 1/3 and 1/2 in the sorted set
25- of reduced proper fractions for d ≤ max_d
26+ Returns the number of fractions lying between 1/3 and 1/2 in the sorted
27+ set of reduced proper fractions for d ≤ max_d.
28+
29+ Uses Möbius inversion to avoid a per-fraction gcd check. Let
30+ f(j) = #{m ∈ ℤ : j/3 < m < j/2} = max(0, (j-1)//2 - j//3).
31+
32+ By Möbius inversion on the coprimality condition:
33+ count = Σ_{e=1}^{max_d} μ(e) · Σ_{j=1}^{⌊max_d/e⌋} f(j)
34+
35+ Building prefix sums of f reduces the inner sum to an O(1) lookup,
36+ giving overall complexity O(max_d log log max_d) for the Möbius sieve
37+ plus O(max_d) for the final summation — compared with O(max_d² log max_d)
38+ for the naive gcd-per-pair approach.
2639
2740 >>> solution(4)
2841 0
@@ -32,19 +45,41 @@ def solution(max_d: int = 12_000) -> int:
3245
3346 >>> solution(8)
3447 3
48+
49+ >>> solution(12_000)
50+ 7295372
3551 """
52+ # Compute the Möbius function for 1..max_d via a linear sieve.
53+ # μ(n) = 1 if n=1; (-1)^k if n is a product of k distinct primes; 0 otherwise.
54+ mobius = [0 ] * (max_d + 1 )
55+ mobius [1 ] = 1
56+ primes : list [int ] = []
57+ is_composite = bytearray (max_d + 1 )
58+
59+ for i in range (2 , max_d + 1 ):
60+ if not is_composite [i ]:
61+ primes .append (i )
62+ mobius [i ] = - 1
63+ for prime in primes :
64+ product = i * prime
65+ if product > max_d :
66+ break
67+ is_composite [product ] = 1
68+ if i % prime == 0 :
69+ mobius [product ] = 0 # prime² divides product
70+ break
71+ mobius [product ] = - mobius [i ] # new prime factor
72+
73+ # prefix[j] = Σ_{k=1}^{j} f(k), where f(k) = #{m : k/3 < m < k/2}
74+ prefix = [0 ] * (max_d + 1 )
75+ for j in range (1 , max_d + 1 ):
76+ prefix [j ] = prefix [j - 1 ] + max (0 , (j - 1 ) // 2 - j // 3 )
3677
37- fractions_number = 0
38- for d in range (max_d + 1 ):
39- n_start = d // 3 + 1
40- n_step = 1
41- if d % 2 == 0 :
42- n_start += 1 - n_start % 2
43- n_step = 2
44- for n in range (n_start , (d + 1 ) // 2 , n_step ):
45- if gcd (n , d ) == 1 :
46- fractions_number += 1
47- return fractions_number
78+ return sum (
79+ mobius [e ] * prefix [max_d // e ]
80+ for e in range (1 , max_d + 1 )
81+ if mobius [e ]
82+ )
4883
4984
5085if __name__ == "__main__" :
0 commit comments