Problem Statement
Count the number of prime numbers less than a non-negative integer n.
Example
-
Input: n = 10
Output: 4
Explanation: The prime numbers less than 10 are 2, 3, 5, 7.
-
Input: n = 20
Output: 8
Explanation: The prime numbers less than 20 are 2, 3, 5, 7, 11, 13, 17, 19.
Brute Force Approach
Approach
Idea: For each number less than n, check if it is prime by testing divisibility by all numbers up to its square root.
Steps
- Initialize a counter to
0.
- Loop through each number from
2 to n - 1.
- For each number, check if it is divisible by any integer from
2 to the square root of the number.
- If it is not divisible, it is a prime number; increment the counter.
- Return the counter.
Time & Space Complexity
- Time Complexity: O(n√n)
- For each number, we check divisibility up to its square root.
- Space Complexity: O(1)
- Only a few variables are used.
Code Snippet
Dry Run
Input: n = 10
- Loop through numbers
2 to 9:
2: Prime, increment count = 1.
3: Prime, increment count = 2.
4: Not prime.
5: Prime, increment count = 3.
6: Not prime.
7: Prime, increment count = 4.
8: Not prime.
9: Not prime.
- Return
4.
Efficient Approach
Approach
Idea: Use a boolean array to mark non-prime numbers. Start with the assumption that all numbers are prime and mark multiples of each prime number as non-prime.
Steps
- Create an array
isPrime of size n and initialize all values to true. Set isPrime[0] and isPrime[1] to false (0 and 1 are not prime).
- Loop through numbers from
2 to n - 1:
- If the number is marked as prime, count it.
- Mark all multiples of the number as non-prime.
- Return the count of
true values in the array.
Time & Space Complexity
- Time Complexity: O(log log n)
- Each number checks all its multiples.
- Space Complexity: O(n)
- An array of size
n is used.
Code Snippet
Dry Run
Input: n = 10
- Initialize
isPrime as [false, false, true, true, true, true, true, true, true, true].
- Loop through numbers
2 to 9:
2: Prime, increment count = 1, mark multiples: [false, false, true, true, false, true, false, true, false, true].
3: Prime, increment count = 2, mark multiples: [false, false, true, true, false, true, false, true, false, false].
4: Not prime.
5: Prime, increment count = 3.
6: Not prime.
7: Prime, increment count = 4.
8: Not prime.
9: Not prime.
- Return
4.
Complexity Analysis
| Operation | Brute Force | Efficient Approach |
|---|
| Time Complexity | O(n√n) | O(log log n) |
| Space Complexity | O(1) | O(n) |
Note: The time complexity of the efficient approach is based on the assumption that the number of primes less than n is approximately n / log(n). This is a result of the Prime Number Theorem. This is also known as the Sieve of Eratosthenes.
Conclusion
- The brute force approach is simple but inefficient for large values of
n due to its O(n√n) time complexity.
- The efficient approach reduces redundant checks by marking multiples of prime numbers, achieving better scalability.
- This problem demonstrates the importance of using optimized data structures like boolean arrays for problem-solving.