I'm working through some exercises and trying different approaches. For the problem of counting primes less than a given limit, I have used two sieve approaches that I think should be equivalent. The first uses for loops plus iterators and the second avoids for loops for just map and filter methods on iterators. The for loop version is about 3 times faster and uses 3 times less memory, according to the leetcode site. Can anyone offer insight into why that is?
For loop version:
pub fn count_primes(n: i32) -> i32 {
let upper_limit = n as usize;
let mut prime_indices = vec![true; upper_limit];
for p in prime_indices.iter_mut()
.skip(4)
.step_by(2) {
*p = false;
}
for i in (3..upper_limit).step_by(2) {
if prime_indices[i] {
for p in prime_indices.iter_mut()
.skip(i * i)
.step_by(2 * i) {
*p = false;
}
}
}
prime_indices
.into_iter()
.enumerate()
.skip(2)
.filter(|&(_, p)| p)
.map(|(i, _)| i as u64)
.count() as i32
}
Iterator/maps version:
pub fn count_primes(n: i32) -> i32 {
let upper_limit = n as usize;
let mut prime_indices = vec![true; upper_limit];
prime_indices.iter_mut()
.skip(4)
.step_by(2)
.map(|p| *p = !*p)
.count(); //necessary to consume the iterator, seemed less costly than a `collect`
let candidates = (3..upper_limit).into_iter()
.step_by(2)
.filter(|&i| prime_indices[i])
.collect::<Vec<_>>();
candidates.iter()
.map(|i| prime_indices.iter_mut()
.skip(i*i)
.step_by(2*i)
.map(|p| *p = false)
.count())
.count();
prime_indices
.into_iter()
.skip(2)
.filter(|&p| p)
.count() as i32
}