Sieve of Eratosthenes Find All Primes in DSA Rust - Time & Space Complexity
We want to understand how the time needed to find all prime numbers grows as we check bigger ranges.
Specifically, how does the Sieve of Eratosthenes perform when finding primes up to a number n?
Analyze the time complexity of the following code snippet.
fn sieve(n: usize) -> Vec<bool> {
let mut primes = vec![true; n + 1];
primes[0] = false;
primes[1] = false;
let mut p = 2;
while p * p <= n {
if primes[p] {
let mut i = p * p;
while i <= n {
primes[i] = false;
i += p;
}
}
p += 1;
}
primes
}
This code marks multiples of each prime number as not prime, effectively finding all primes up to n.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Marking multiples of primes as not prime inside nested loops.
- How many times: Outer loop runs roughly up to ān, inner loop runs for multiples of each prime.
As n grows, the number of operations grows a bit faster than n but much slower than n squared.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | ~20 |
| 100 | ~300 |
| 1000 | ~5000 |
Pattern observation: The work grows roughly proportional to n times the logarithm of logarithm of n, which is slower than n log n but faster than linear.
Time Complexity: O(n log log n)
This means the time grows a little faster than n but much slower than n squared, making it efficient for large n.
[X] Wrong: "The algorithm checks every number against every other number, so it must be O(n²)."
[OK] Correct: The sieve only marks multiples of primes, skipping many numbers early, so it does far fewer operations than n squared.
Understanding this time complexity shows you can analyze nested loops with decreasing work and recognize efficient prime-finding methods.
"What if we started marking multiples from 2*p instead of p*p? How would the time complexity change?"
Practice
Sieve of Eratosthenes algorithm?Solution
Step 1: Understand the algorithm's goal
The Sieve of Eratosthenes is designed to identify prime numbers efficiently.Step 2: Recognize the output of the algorithm
It marks multiples of primes as non-prime, leaving primes unmarked up to a limit.Final Answer:
To find all prime numbers up to a given limit -> Option CQuick Check:
Sieve of Eratosthenes = Find primes [OK]
- Confusing it with sorting algorithms
- Thinking it calculates factorials
- Assuming it finds divisors instead of primes
is_prime of size n+1 with all values set to true except for indices 0 and 1?Solution
Step 1: Initialize vector with true values
We want a mutable vector of size n+1 with all true values initially.Step 2: Set indices 0 and 1 to false
Since 0 and 1 are not prime, we set those positions to false.Final Answer:
let mut is_prime = vec![true; n+1]; is_prime[0] = false; is_prime[1] = false; -> Option BQuick Check:
Initialize true vector and mark 0,1 false [OK]
- Using size n instead of n+1
- Not making vector mutable
- Setting wrong indices to true/false
let n = 10;
let mut is_prime = vec![true; n+1];
is_prime[0] = false;
is_prime[1] = false;
for i in 2..=((n as f64).sqrt() as usize) {
if is_prime[i] {
let mut j = i * i;
while j <= n {
is_prime[j] = false;
j += i;
}
}
}
let primes: Vec<usize> = is_prime.iter().enumerate()
.filter(|&(_, &prime)| prime)
.map(|(index, _)| index)
.collect();
println!("{:?}", primes);Solution
Step 1: Initialize and mark non-primes
Indices 0 and 1 are false. Loop marks multiples of 2 and 3 as false.Step 2: Collect indices marked true
Only 2, 3, 5, and 7 remain true and are collected as primes.Final Answer:
[2, 3, 5, 7] -> Option AQuick Check:
Primes up to 10 = [2, 3, 5, 7] [OK]
- Including 1 or 0 as prime
- Including 4 which is not prime
- Not starting j from i*i
n = 15:
let n = 15;
let mut is_prime = vec![true; n+1];
is_prime[0] = false;
is_prime[1] = false;
for i in 2..n {
if is_prime[i] {
let mut j = i * 2;
while j <= n {
is_prime[j] = false;
j += i;
}
}
}Solution
Step 1: Check inner loop start index
Starting from i*2 marks some numbers multiple times, less efficient.Step 2: Correct start is i*i for marking multiples
Because smaller multiples are already marked by smaller primes.Final Answer:
The inner loop should start from i * i, not i * 2 -> Option DQuick Check:
Start marking multiples from i*i for efficiency [OK]
- Starting inner loop from i*2 wastes time
- Looping outer from 2 to n instead of sqrt(n)
- Wrong vector size causing index errors
let n = 50;
let mut is_prime = vec![true; n+1];
is_prime[0] = false;
is_prime[1] = false;
for i in 2..=((n as f64).sqrt() as usize) {
if is_prime[i] {
let mut j = i * i;
while j <= n {
is_prime[j] = false;
j += i;
}
}
}
let primes: Vec<usize> = is_prime.iter().enumerate()
.filter(|&(_, &prime)| prime)
.map(|(index, _)| index)
.collect();Solution
Step 1: Understand the sieve marks primes correctly
All primes including 2 are marked true inis_prime.Step 2: Exclude 2 after collecting primes
Filtering out 2 after collecting primes avoids affecting sieve logic.Final Answer:
Add a filter after collecting primes: primes.into_iter().filter(|&x| x != 2).collect() -> Option AQuick Check:
Filter out 2 after sieve to exclude it [OK]
- Setting is_prime[2] = false breaks sieve correctness
- Starting loop from 3 misses marking multiples of 2
- Removing is_prime[0] = false causes wrong prime marking
Ready to prove it?
Pass the test and this pattern moves unknown
