With n as input, I am trying to output the nth prime. The Eratosthenes Sieve seemed a good way to do this, but I am having trouble with the size of the array to be sieved.
I use an array in which every member is 1, and represents a number. If the sieve filters out the number, the value is changed to 0, which means the number is not prime. Once it arrives at the nth member which has a value of 1, the index value is returned.
I am trying to set a reasonable array size for any given n. But I have two problems.
Because I seem to be required to set the size of the array to a constant value, I cannot find a way to use the size of n to approximate the required array size. This means I am always using an array of 10e6 magnitude, even when n is small.
This approach relies on having a large array, because it uses early values to change later values. This means that for n > 10e7, my array blows the stack. Is there a way around this problem without going to the heap?
I've tried to solve the first problem by using const like this:
pub fn nth(n: u32) -> u32 {
const ARRAY_SIZE : usize = f(n) // where f(n) is some approximation of required array size
let mut primes: [usize; ARRAY_SIZE] = [1; ARRAY_SIZE];
...
}
However, it does not get around the requirement to have a fixed array size.
Any suggestions? Also I am very new to rust and any suggestions to make it more rust-like are welcome.
Below is my attempt which has a fixed size array and works for values of n <= 10,000:
pub fn nth(n: u32) -> u32 {
// Eratosthene's Sieve Algorithm
let mut output: u32 = 0;
let mut primes: [usize; 104_827] = [1; 104_827];
primes[0] = 0;
primes[1] = 0;
let mut prime_count = 0;
for i in 2..(primes.len() as usize) {
if primes[i] == 1 {
if prime_count == n as usize { output = i as u32; }
prime_count += 1;
let mut j: usize = 2;
while i * j <= primes.len() as usize {
primes[i * j] = 0;
j += 1;
}
}
}
output
}
edit:
Thanks for the great answers! I've changed the function to use the (n * (n * n.ln()).ln()) estimation, replaced the array of usize with a vector of bools. Shame I can't seem to make the lower values of this work on just the stack, because the array-based method is faster than the vector-based method, when the array size is close to the approximation.
I tried to use the early return and dispense with output but couldn't figure out how to make it work. I have my code at the end of this edit with lines commented out with where I tried to put the early return and unreachable!() but it always panicked on me. If someone can point out my mistake that would be great.
I was going to change the inner-most while loop into a for loop as suggested by NieDzejkob, but when I ran the numbers for speed of while vs for, I found the while loop to be faster. Does anyone know why this might be?
Also I had some problems with 0-9th, 11th, and 12th primes, so I just set the approximation to work above this range.
100th prime while_func = 547, for_func = 547
prime while_func 152.431µs vs for_func 216.043µs
1000th prime while_func = 7927, for_func = 7927
prime while_func 2.099213ms vs for_func 3.362854ms
10000th prime while_func = 104743, for_func = 104743
prime while_func 28.162967ms vs for_func 44.967197ms
100000th prime while_func = 1299721, for_func = 1299721
prime while_func 339.324756ms vs for_func 559.755934ms
1000000th prime while_func = 15485867, for_func = 15485867
prime while_func 4.151047728s vs for_func 6.950281943s
pub fn nth(n: u32) -> u32 {
// Eratosthene's Sieve Algorithm
let estimate = estimate_size(n);
let mut primes: Vec<bool> = vec![true; estimate + 1];
primes[0] = false;
primes[1] = false;
let mut output: u32 = 0; // remove if unreachable!() works
let mut prime_count: u32 = 0;
for i in 2..(estimate) {
if primes[i] == true {
if prime_count == n { output = i as u32; }
// if prime_count == n { return i as u32; }
prime_count += 1;
let mut j: usize = 2;
while i * j <= estimate {
primes[i * j] = false;
j += 1;
}
// for j in (2*i ..= estimate).step_by(i) {
// primes[j] = false;
// }
}
}
output
// unreachable!()
}
fn estimate_size(n: u32) -> usize {
if n < 14 {
44 as usize
} else {
let n = n as f64;
(n * (n * n.ln()).ln()).ceil() as usize
}
}