线性筛也叫欧拉筛,求范围内质数的时间复杂度仅为o(n),在数据很大时非常好用。
首先准备一个mark数组来保存是否某一个数是否为质数,prim数组保存已经找到的质数。
首先要明白一点:一个合数等于其最小质因子*一个更大的数。即a=b*c,a为合数,b为a的最小质因子,c比b大。所以我们通过遍历质数的倍数来标记合数。
for(int i=2;i<=n;i++)
{
if(!mark[i])
prim[cnt++]=i;
for(int j=0;j<cnt;j++)
{
mark[i*prim[j]]=1;//prim[j]为最小质因子
if(i%prim[j]==0)//i的最小质因子为prim[j]
break;
}
}
外层遍历的是a=b*c中的c,内层遍历的是b。
从2开始如果遍历到一个数如果未被标记,则这个数是质数加入prim数组,对于每一个i,都遍历一遍已经找到的质数,由于从小到大遍历,所以找到的质数一定比i小,标记prim[j]*i为合数,即b*c,c是一定比b大的。
当首次遇到i%prim[j]==0时结束内层循环,因为prim数组也是从小到大存的质数,那么此时prim[j]表示的质数为i的最小质因子,那么后续的所有被i*质数筛掉的数,都在后面可以被prim[j]筛掉。举个例子:i为25,prim存的质数为2,3,5,7,11,13。当prim数组遍历到5的时候,标记5*25为合数,25%5==0,对于后面所有的7,11,13,我们用7筛掉7*25时,因为5为25的最小质因子,所以7*25可以后面的遍历中由5*35筛掉,而2,3不是25的质因子,2*25与3*25不能转化为更小质因子*某一个数了,也就是说,当找到一个i的最小质因子后,后续所有的i*更大的质数一定可以由这个最小质因子*比i更大的数筛掉,这样避免了重复大大减少了时间复杂度