前几天同学让我帮他做道题,关于求素数的,十亿两分钟内出结果 。 (编译器 mingw-g++)
最开始是用的最常见的欧几里得筛选法
{
int s=(int)sqrt(x);
for(int i=3;i<=s;i+=2)
{
if(x%i==0)
return false;
}
return true;
}
也就是一个双重循环,外层把数从1到n 跑一遍,只测试奇数。时间复杂度大约是O(N3/2).后来发现速度是相当的慢,这种算法效率无法达到要求。
改用BigMap算法。这里我先把1到10万内的素数求出来,放在一个数组里面(方法还是用的上面那种,这里对效率影响不是很大。总共用时在1秒以内)。然后以此为基数再判定后面的数。
{
for(int i=1;x[i]*x[i]<=a;i++)
{
if(a%x[i]==0)
return false;
}
return true;
}
这里x[ ]里面是1到10万的素数,x[0]=2可以不用测。时间复杂度约为O(10000N).
这个时候求1千万内的素数大概用时6.22秒,2千万 15.22秒,3千万 25.95秒。
后来测十亿时,半个小时没出结果,我把素数输出到一个文件里面,大小约320M。我看了下,这时才走到6亿9千万。
最后,考虑到函数入栈出栈的时间,我把BigMap内联了,这里是全部的代码。
#include <fstream>
#include <math.h>
#include <time.h>
using namespace std;
bool IsPrime(long x);
void MakeBasePrime(long*);
const int BASE=9592;
//9592 is the number of the primes in 1 to 100000
int main()
{
clock_t start,finish;
long range;
long count=0;
long baseprime[BASE];
cout<<"Input the max number:";
cin>>range;
start=clock();
MakeBasePrime(baseprime);
ofstream out("prime.txt");
for(int i=100001;i<=range;i+=2)
{
if(i%3==0 || i%5==0 || i%7==0 || i%11==0 || i%13==0 || i%17==0 || i%19==0)
continue;
bool flag=true;
for(int j=8;baseprime[j]*baseprime[j]<=i;j++)
{
if(i%baseprime[j]==0)
{
flag=false;
break;
}
}
if(flag)
{
out<<i<<" ";
count++;
if(count%10==0)
out<<endl;
}
}
cout<<" The least prime count = "<<count;
finish=clock();
cout<<" The whole time lapse is "<<(double)(finish-start)/CLOCKS_PER_SEC;
cin.get();
return 0;
}
bool IsPrime(long x)
{
int s=(int)sqrt(x);
for(int i=3;i<=s;i+=2)
{
if(x%i==0)
return false;
}
return true;
}
void MakeBasePrime(long x[])
{
int k=1;
x[0]=2;
ofstream out("baseprime.txt");
out<<x[0]<<" ";
for(int i=3;i<100000;i+=2)
{
if(IsPrime(i))
{
x[k]=i;
out<<x[k]<<" ";
k++;
if(k%10==0)
out<<endl;
}
}
cout<<" The count of prime range 1 to 100000 = "<<k<<endl;
}
在用BigMap判定素数前,先用if语句测试前几个。因为在多次实验中这样时间有一定的减少,但如果if语句再多测几个数,速度会变慢,具体原因还不清楚,可能是编译器优化的原因。
这里是几组测出来的时间:
1千万 5.828 s
2千万 14.313 s
3千万 24.109 s
1亿 125.094 s
如果不输出到文件,时间每1千万大概能减少1秒。这里看来十亿估计也在半小时左右。
如果有高人能有更好的解法,欢迎赐教。