筛素数
筛素数
相信你肯定做过这样的题:给你一个m,求1~m素数的数量
或者在某道题中需要做这样的处理。
当然你构造出来这么一个素数表以后就可以干其他更多有意思的事情,请读者自行探索(丢下了我觉得最不负责任的一句话。。。)
怎么办?大多数人都会for一遍,再去检查每一个数是不是素数,时间效率极为低下。
核心思路
先说清楚:这里不会教你如何判定一个数字是不是素数。
道理很简单,对于任何一个数字a(不管这个a是不是素数),a的倍数(2a、3a、4a、5a。。。)都不可能是素数。
这个a先从2开始,依次排除2 * 2 , 3 * 2 , 4 * 2 …显然,因为是要求1~m素数的数量,所以n*a<=m。多算的就没有用了。
然后是n * 3,n * 4,n * 5,n * 6,n * 7。。。。
值得一提的是,每一个数在轮到自己排除自己的倍数的时候,可以从自己的平方开始排除,即跳过2 * a, 3 * a…直接从a * a , (a+1) * a , (a+2) * a…开始排除。原因很简单,拿2举例, 2 * a ,在排除2的倍数(a * 2)的时候就已经排除了。那么 a * a之前的都是在重复排除。根据这个道理,a的取值到sqrt(m)就够了(因为此时a * a == m)。
排除一个数字的操作也很简单:开一个数组,在被排除掉的数字上打上标记就好了。
下面的代码实现按照我的习惯写了非常详细的注释,看不懂的可以准备喷了。
代码实现
int a = sqrt(m+1);//对于不是完全平方数的m,+1可以保证计算得的结果>=sqrt(m),宁愿多算一两次也不能少算。+0.5效果好像是一样的
memset(vis,0,sizeof(vis));//好像还有个algorithm库里的fill函数效果差不多,没用过,感兴趣的读者可以去看看
for(int i=2;i<=a;i++){
if(vis[i]==false)//vis数组来做标记
{
for(int j=i*i;j<=m;j+=i)//解释一下为什么是j+=i,这里就是在做a*a后的 (a+1) * a , (a+2) * a...的过程
vis[j]=true;//打上标记
}
}
写在最后
截止我写这篇文章的时候,我写的尺取法已经有930阅读量了!(exm?)
链接留下:https://www.cnblogs.com/opbnbjs/p/9503797.html
没有别的意思,希望各位看了我文章的带佬写几个评论呗。。。说个 写得好!或者喷几句我这只菜鸡 也行啊QAQ
谢谢你能看到这里,数学这个坑我又跳进去了(上次的指北才只写了一篇ToT)。。。
祝ak抄手皮、吃薯片(csp),ioi,noi,acm(话说真这么强的人应该也不会看这个了吧)
(这好像是我上高中以后写的第一篇呢)