There are several efficiency savings possible in your code. The most efficient method (apart from using a pre-calculated look-up table for small (<1000) n
) is the sieve of Erastosthenes.
A very naive version of this algorithm (see also the answer by
coderredoc)
std::vector<int> primes(int n)
{
std::vector<bool> is_prime(n+1,true);
for(auto divisor=2; divisor*divisor <= n; ++divisor)
for(auto candidate=divisor*divisor; candidate <= n; candidate+=divisor)
is_prime[candidate]=false;
std::vector<int> result;
for(auto candidate=2; candidate <= n; ++candidate)
if(is_prime[candidate]) result.push_back(candidate);
return result;
}
essentially reverses the loops over candidate
s and divisor
s compared to your original algorithm, but only tests for divisor
s satisfying divisor*divisor<=candidate
.
This algorithm can be substantially improved by realizing that we only need to test for prime divisors (which is the main trick of the sieve)
std::vector<int> primes(int n)
{
std::vector<bool> is_prime(n+1,true);
for(auto divisor=2; divisor*divisor <= n; ++divisor)
if(is_prime[divisor])
for(auto candidate=divisor*divisor; candidate <= n; candidate+=divisor)
is_prime[candidate]=false;
std::vector<int> result;
for(auto candidate=2; candidate <= n; ++candidate)
if(is_prime[candidate]) result.push_back(candidate);
return result;
}
which cuts down on the testing for large n
. A further efficiency saving (by a factor ~2 in space and time) is possible by avoiding even candidate
s and divisor
s:
std::vector<int> primes(int n)
{
if(n<2) return {};
if(n==2) return {2};
std::vector<bool> is_prime((n+1)>>1,true);
for(auto divisor=3; divisor*divisor <= n; divisor+=2)
if(is_prime[divisor>>1])
for(auto candidate=divisor*divisor; candidate <= n; candidate+=2*divisor)
is_prime[candidate>>1]=false;
std::vector<int> result(1,2);
for(auto candidate=3; candidate <= n; candidate+=2)
if(is_prime[candidate>>1]) result.push_back(candidate);
return result;
}
This algorithm has a poor memory access pattern (into the is_prime[]
), which becomes a problem for very large n
. A more sophisticated method, segmented sieve, can avoid that, see the above link.