求N以内所有素数的和,最普通的方法是对N以内所有自然数进行一次是否为素数的判断,然后将所有素数相加。
然而对某个自然数s是否为素数的判断是相当费时间的,需要判断其是否可以整除0到s^0.5之间的所有自然数,如果s很大的话,这将相当耗时。
两千多年前的数学家厄拉多塞提出的厄拉多塞筛法可以一定程度上减少计算素数和时的工作量。
维基百科上埃拉托斯特尼筛法词条中有一个很直观的示意图,如下:
例如现在要求自然数N以内所有素数的和,那么此筛法的基本方法是从2开始,把2作为素数,然后把N以内的所有2的倍数全部除去。此时下一个数字为3,将3作为素数,然后把N以内的所有3的倍数全部除去。下一个数字是5,把5作为素数,然后把N以内的所有5的倍数全部除去……一直这样下去,直到N^0.5,此时剩下的没有被除去的数全都是素数,把这些数和之前的所有素数相加即可得到结果。
这样的话,省掉了很复杂的判断某自然数是否为素数的过程。在N很大的时候,效率是秒杀开头提到的普通方法的。
代码如下:
#include "TimerCounter.h"
#include "TimerCounter.cpp"
#include <iostream>
using namespace std;
int main()
{
uint64_t MAX;
while(cin>>MAX)
{
TimerCounter tc;
tc.Start();
//计时开始
bool *isprime=new bool[MAX];
//创建一个大小为MAX的bool类型数组
uint64_t primeSum=0;
//创建一个uint64_t类型的变量用来存储素数的和
memset(isprime, true, MAX);
//使用memset填充bool类型数组
for(uint64_t i = 2; i * i < MAX; ++i)
{
if(isprime[i])
{
for(uint64_t j = i * i; j < MAX; j += i)
isprime[j] = false;
}
}
for(uint64_t i = 2; i < MAX; ++i)
{
if(isprime[i])
primeSum+=i;
}
delete[] isprime;
tc.Stop();
//计时结束
cout<<primeSum<<" ("<<tc.dbTime<<"s)"<<endl;
//输出素数和,所用时间
}
return 0;
}
测试这段代码,截图如下:
输入自然数N,输出N以内的所有素数的和,程序运行时间。
可以看到随着N的大小逐渐增大,时间的增长非常明显,可见该算法还是有一些缺点的。