Prerequisites
Before attempting this problem, you should be comfortable with:
- Prime Number Definition - Understanding that a prime number has exactly two divisors: 1 and itself
- Square Root Optimization - Knowing that factors come in pairs, so checking up to sqrt(n) is sufficient
- Sieve of Eratosthenes - Understanding this classic algorithm for efficiently finding all primes up to a limit
1. Brute Force
Intuition
The most straightforward way to count primes is to check each number individually. A number is prime if it has no divisors other than 1 and itself. We can optimize the divisibility check by only testing divisors up to the square root of the number, since if a number has a factor larger than its square root, it must also have a corresponding factor smaller than its square root.
Algorithm
- Initialize a counter
resto0. - For each number from
2ton-1:- Assume the number is prime.
- Check if any number from
2to the square root of the current number divides it evenly. - If a divisor is found, mark it as not prime.
- If no divisor is found, increment the counter.
- Return the count of primes.
Time & Space Complexity
- Time complexity:
- Space complexity:
2. Sieve of Eratosthenes
Intuition
Instead of checking each number for primality, we can use the Sieve of Eratosthenes to efficiently mark composite numbers. The key insight is that when we find a prime number, all of its multiples must be composite. By marking these multiples, we eliminate the need to check them later. We start marking from the square of each prime because smaller multiples would have already been marked by smaller primes.
Algorithm
- Create a boolean array
sieveof sizen, initialized tofalse(all numbers assumed prime). - Initialize a counter
resto0. - For each number from
2ton-1:- If the number is not marked as composite (
sieve[num]isfalse):- Increment the prime counter.
- Mark all multiples of this number starting from
num*numas composite.
- If the number is not marked as composite (
- Return the count of primes.
Time & Space Complexity
- Time complexity:
- Space complexity:
Common Pitfalls
Including n in the Count
The problem asks for primes strictly less than n, not less than or equal to. Using range(2, n + 1) or i <= n will include n itself if it's prime.
# Wrong: includes n
for num in range(2, n + 1):
# Correct: excludes n
for num in range(2, n):Integer Overflow When Computing num * num
When starting the sieve from i * i, this multiplication can overflow for large values. Use long or cast before multiplying.
// Overflow risk when i is large
for (int j = i * i; j < n; j += i)
// Safe: use long arithmetic
for (long j = (long) i * i; j < n; j += i)Forgetting to Handle n < 2
There are no primes less than 2. Failing to handle edge cases like n = 0, n = 1, or n = 2 can cause array index errors or incorrect results.