1447. Simplified Fractions
Problem Description
You need to find all simplified fractions between 0 and 1 (not including 0 or 1) where the denominator is at most n
.
A simplified fraction means the numerator and denominator have no common factors other than 1 (they are coprime). For example, 2/4
is not simplified because both 2 and 4 share a common factor of 2, but 1/2
is simplified.
Given an integer n
, return a list of strings representing all such fractions in the format "numerator/denominator"
. The fractions can be returned in any order.
For example, if n = 4
, the valid simplified fractions between 0 and 1 would be:
"1/2"
(0.5)"1/3"
(≈0.333)"2/3"
(≈0.667)"1/4"
(0.25)"3/4"
(0.75)
Note that "2/4"
is not included because it simplifies to "1/2"
which is already in the list.
The solution uses a nested loop to check all possible numerator-denominator pairs where:
- The numerator
i
ranges from 1 ton-1
- The denominator
j
ranges fromi+1
ton
(ensuring the fraction is less than 1) - Only pairs where
gcd(i, j) == 1
are included (ensuring the fraction is simplified)
Intuition
To find all simplified fractions between 0 and 1, we need to think about what makes a fraction simplified and what constraints we have.
First, for a fraction to be between 0 and 1, the numerator must be smaller than the denominator. This gives us the relationship numerator < denominator
.
Second, a fraction is in its simplest form when the numerator and denominator share no common factors except 1. This is equivalent to saying their greatest common divisor (GCD) equals 1. For instance, 2/6
and 1/3
represent the same value, but only 1/3
is simplified because gcd(1, 3) = 1
, while gcd(2, 6) = 2
.
Given these constraints, we can systematically generate all possible fractions by:
- Trying every possible numerator from 1 to
n-1
(we start from 1 because 0 would give us 0, which is excluded) - For each numerator, trying every possible denominator from
numerator + 1
ton
(ensuring the fraction is less than 1) - Checking if
gcd(numerator, denominator) = 1
to ensure the fraction is already simplified
This approach naturally avoids duplicates. For example, when we check 2/4
, we find gcd(2, 4) = 2
, so we skip it. Later when we check 1/2
, we find gcd(1, 2) = 1
, so we include it. This way, each fraction value appears exactly once in its simplified form.
The key insight is that by checking the GCD condition, we automatically filter out all non-simplified fractions, leaving us with exactly the unique simplified fractions we need.
Learn more about Math patterns.
Solution Approach
The implementation uses a list comprehension with nested loops to generate all valid simplified fractions.
Here's how the solution works step by step:
-
Outer loop for numerators: We iterate
i
from 1 ton-1
. Starting from 1 ensures we exclude 0 (which would give us0/denominator = 0
), and stopping atn-1
ensures we have room for denominators greater than the numerator. -
Inner loop for denominators: For each numerator
i
, we iteratej
fromi+1
ton
. Starting fromi+1
guarantees that our fractioni/j
will be less than 1 (since numerator < denominator). Going up ton
respects the constraint that denominators should be at mostn
. -
GCD check for simplification: We use
gcd(i, j) == 1
to filter only simplified fractions. Thegcd
function (greatest common divisor) returns 1 only when the two numbers are coprime (share no common factors other than 1). -
String formatting: Each valid fraction is formatted as
f'{i}/{j}'
using Python's f-string notation to create the required string representation.
The list comprehension elegantly combines all these steps:
[f'{i}/{j}' for i in range(1, n) for j in range(i + 1, n + 1) if gcd(i, j) == 1]
This approach has a time complexity of O(n² × log(n))
where:
O(n²)
comes from the nested loopsO(log(n))
is the time complexity of computing GCD using Euclid's algorithm
The space complexity is O(k)
where k
is the number of simplified fractions generated, which depends on the value of n
.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with n = 4
to see how we find all simplified fractions between 0 and 1.
Step 1: Set up our search space
- Numerators can range from 1 to 3 (i.e., 1 to n-1)
- For each numerator, denominators range from (numerator + 1) to 4
Step 2: Check each fraction
When i = 1
(numerator = 1):
j = 2
: Check1/2
→gcd(1, 2) = 1
✓ Add "1/2"j = 3
: Check1/3
→gcd(1, 3) = 1
✓ Add "1/3"j = 4
: Check1/4
→gcd(1, 4) = 1
✓ Add "1/4"
When i = 2
(numerator = 2):
j = 3
: Check2/3
→gcd(2, 3) = 1
✓ Add "2/3"j = 4
: Check2/4
→gcd(2, 4) = 2
✗ Skip (not simplified)
When i = 3
(numerator = 3):
j = 4
: Check3/4
→gcd(3, 4) = 1
✓ Add "3/4"
Step 3: Collect results
Final list: ["1/2", "1/3", "1/4", "2/3", "3/4"]
Notice how 2/4
was automatically excluded because gcd(2, 4) = 2
, preventing the duplicate representation of 1/2
. This demonstrates how the GCD check ensures each fraction appears exactly once in its simplified form.
Solution Implementation
1from typing import List
2from math import gcd
3
4class Solution:
5 def simplifiedFractions(self, n: int) -> List[str]:
6 """
7 Generate all simplified fractions between 0 and 1 with denominators <= n.
8
9 A fraction is simplified when the numerator and denominator are coprime (GCD = 1).
10
11 Args:
12 n: Maximum denominator value
13
14 Returns:
15 List of simplified fractions in "numerator/denominator" format
16 """
17 # List comprehension to generate all valid simplified fractions
18 return [
19 f'{numerator}/{denominator}' # Format fraction as string
20 for numerator in range(1, n) # Numerator from 1 to n-1
21 for denominator in range(numerator + 1, n + 1) # Denominator from numerator+1 to n
22 if gcd(numerator, denominator) == 1 # Only include if fraction is in simplified form
23 ]
24
1class Solution {
2 /**
3 * Generate all simplified fractions between 0 and 1 with denominators <= n.
4 * A fraction is simplified when the numerator and denominator are coprime (GCD = 1).
5 *
6 * @param n The maximum denominator value
7 * @return List of simplified fractions in "numerator/denominator" format
8 */
9 public List<String> simplifiedFractions(int n) {
10 List<String> result = new ArrayList<>();
11
12 // Iterate through all possible numerators (1 to n-1)
13 for (int numerator = 1; numerator < n; numerator++) {
14 // Iterate through all possible denominators (numerator+1 to n)
15 // This ensures fraction is always less than 1
16 for (int denominator = numerator + 1; denominator <= n; denominator++) {
17 // Check if the fraction is in its simplest form
18 // (numerator and denominator are coprime)
19 if (gcd(numerator, denominator) == 1) {
20 result.add(numerator + "/" + denominator);
21 }
22 }
23 }
24
25 return result;
26 }
27
28 /**
29 * Calculate the Greatest Common Divisor (GCD) using Euclidean algorithm.
30 *
31 * @param a First number
32 * @param b Second number
33 * @return The GCD of a and b
34 */
35 private int gcd(int a, int b) {
36 // Recursive implementation: if b is 0, return a; otherwise, compute gcd(b, a % b)
37 return b > 0 ? gcd(b, a % b) : a;
38 }
39}
40
1class Solution {
2public:
3 vector<string> simplifiedFractions(int n) {
4 vector<string> result;
5
6 // Iterate through all possible numerators from 1 to n-1
7 for (int numerator = 1; numerator < n; ++numerator) {
8 // Iterate through all possible denominators from numerator+1 to n
9 // This ensures numerator < denominator (proper fraction)
10 for (int denominator = numerator + 1; denominator <= n; ++denominator) {
11 // Check if the fraction is in its simplest form
12 // Two numbers are coprime if their GCD is 1
13 if (__gcd(numerator, denominator) == 1) {
14 // Add the simplified fraction to the result
15 result.push_back(to_string(numerator) + "/" + to_string(denominator));
16 }
17 }
18 }
19
20 return result;
21 }
22};
23
1/**
2 * Generates all simplified fractions between 0 and 1 with denominators from 2 to n
3 * @param n - The maximum denominator value
4 * @returns An array of strings representing simplified fractions in "numerator/denominator" format
5 */
6function simplifiedFractions(n: number): string[] {
7 const result: string[] = [];
8
9 // Iterate through all possible numerators (1 to n-1)
10 for (let numerator = 1; numerator < n; ++numerator) {
11 // Iterate through all possible denominators (numerator+1 to n)
12 // This ensures the fraction is less than 1
13 for (let denominator = numerator + 1; denominator <= n; ++denominator) {
14 // Check if the fraction is in simplified form (GCD equals 1)
15 if (gcd(numerator, denominator) === 1) {
16 result.push(`${numerator}/${denominator}`);
17 }
18 }
19 }
20
21 return result;
22}
23
24/**
25 * Calculates the Greatest Common Divisor (GCD) of two numbers using Euclidean algorithm
26 * @param a - First number
27 * @param b - Second number
28 * @returns The GCD of a and b
29 */
30function gcd(a: number, b: number): number {
31 // Base case: when b is 0, return a
32 // Recursive case: calculate GCD of b and the remainder of a divided by b
33 return b === 0 ? a : gcd(b, a % b);
34}
35
Time and Space Complexity
Time Complexity: O(n² × log(min(i, j)))
The code uses nested loops where:
- The outer loop runs from
i = 1
toi = n-1
, which isO(n)
iterations - The inner loop runs from
j = i+1
toj = n
, which averages toO(n)
iterations - For each pair
(i, j)
, we computegcd(i, j)
which takesO(log(min(i, j)))
time using the Euclidean algorithm
The total number of pairs checked is approximately n²/2
, and for each pair we perform a GCD operation. Therefore, the overall time complexity is O(n² × log(n))
, which can be simplified to O(n² × log(min(i, j)))
for precision.
Space Complexity: O(φ(2) + φ(3) + ... + φ(n))
≈ O(n²)
The space is used to store the result list. The number of simplified fractions with denominator d
equals φ(d)
(Euler's totient function), where φ(d)
counts integers less than d
that are coprime to d
.
The total number of simplified fractions is the sum of totient values from 2 to n: Σφ(k)
for k = 2
to n
. This sum is approximately 3n²/π²
for large n, which gives us O(n²)
space complexity.
Additionally, the list comprehension itself doesn't use extra space beyond the output list, making the overall space complexity O(n²)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Range Boundaries Leading to Missing or Invalid Fractions
One of the most common mistakes is getting the loop ranges wrong, which can result in:
- Including fractions equal to or greater than 1
- Missing valid fractions
- Including 0 as a fraction
Incorrect Implementation Example:
# Pitfall 1: Starting denominator from i instead of i+1
for numerator in range(1, n):
for denominator in range(numerator, n + 1): # Wrong! Includes i/i = 1
if gcd(numerator, denominator) == 1:
result.append(f'{numerator}/{denominator}')
# Pitfall 2: Incorrect upper bound for numerator
for numerator in range(1, n + 1): # Wrong! When numerator = n, no valid denominators exist
for denominator in range(numerator + 1, n + 1):
# When numerator = n, this loop never executes since n+1 > n+1
Solution: Ensure the ranges are:
- Numerator:
range(1, n)
- starts at 1 (excludes 0) and goes up to n-1 - Denominator:
range(numerator + 1, n + 1)
- ensures fraction < 1 and includes n as possible denominator
2. Forgetting to Import GCD or Using Wrong GCD Implementation
Incorrect Implementation Example:
class Solution:
def simplifiedFractions(self, n: int) -> List[str]:
# Forgot to import gcd!
return [f'{i}/{j}' for i in range(1, n)
for j in range(i + 1, n + 1)
if gcd(i, j) == 1] # NameError: name 'gcd' is not defined
Solution:
Always import gcd
from the math
module:
from math import gcd
Alternatively, implement your own GCD function:
def gcd(a, b):
while b:
a, b = b, a % b
return a
3. Not Understanding What "Simplified" Means
Some might think checking numerator < denominator
is enough, forgetting about the coprime requirement.
Incorrect Implementation Example:
# Wrong: This includes non-simplified fractions like "2/4"
return [f'{i}/{j}' for i in range(1, n)
for j in range(i + 1, n + 1)] # Missing GCD check!
Solution:
Always check that gcd(numerator, denominator) == 1
to ensure the fraction cannot be reduced further.
4. Performance Issues with Large Values of n
While the given solution is efficient, some might try alternative approaches that are less optimal:
Inefficient Approach Example:
# Generating all fractions first, then removing duplicates
fractions = set()
for denominator in range(2, n + 1):
for numerator in range(1, denominator):
# Simplify the fraction first
g = gcd(numerator, denominator)
simplified_num = numerator // g
simplified_den = denominator // g
fractions.add(f'{simplified_num}/{simplified_den}')
return list(fractions)
This approach has unnecessary overhead from:
- Creating simplified versions of non-simplified fractions
- Using a set for deduplication
- Extra division operations
Solution:
Stick with the direct approach of only generating simplified fractions by checking gcd(i, j) == 1
before adding to the result.
A person thinks of a number between 1 and 1000. You may ask any number questions to them, provided that the question can be answered with either "yes" or "no".
What is the minimum number of questions you needed to ask so that you are guaranteed to know the number that the person is thinking?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!