在您的代码中可以节省多种效率。最有效的方法(除了对小型 (n 使用预先计算的查找表)是 sieve of Erastosthenes。
这个算法的一个非常幼稚的版本(另见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;
}
与您的原始算法相比,基本上反转了candidates 和divisors 上的循环,但仅测试满足divisor*divisor<=candidate 的divisors。
通过意识到我们只需要测试素数除数(这是筛子的主要技巧),可以大幅改进该算法
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;
}
这减少了对大型 n 的测试。甚至避免candidates 和divisors 也可以进一步节省效率(空间和时间减少约2 倍):
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;
}
该算法的内存访问模式很差(进入is_prime[]),这对于非常大的n 来说是个问题。一种更复杂的方法,分段筛,可以避免这种情况,请参见上面的链接。