【模板】素数筛法
(好-----快------)
素数是什么就不用介绍了吧。。。先介绍判断素数的方法
判断素数
先看朴素算法:
for (i=2;i<n;i++) if (n%i==0) break; if (i==n) printf(“n is prime”); else printf(“n is not prime”);
(真的好朴素。。)
用时O(n)
(肯定不行啊,吃枣药丸的。。)
怎么优化呢?
不难发现,如果a是n的约数,那么n/a也是n的约数
所以就有以下代码:
n2=int(sqrt(n)+0.5); for (i=2;i<=n2;i++) if (n%i==0) break; if (i>n2) printf(“n is prime”); else printf(“n is not prime”);
这个用时O(n^0.5)
一般使用的话这样其实差不多了。。(其实有一个更快的只是我不会。。)
筛选素数
进入正题啦!
目标:
尽量快的找出1---n之间的所有素数
朴素算法:
for(int i=2;i<=n;i++) { for(int j=2;j<=sqrt(i);j++) { if(i%j==0) { flag=1; break; } } if(!flag) cout<<i<<" "; flag=0; }
其实也不是很朴素啦。。用了上面介绍的优化,但是速度还是很慢
怎么优化呢?
对于任意数a,a所有的整数倍都是合数
int flag[2333]; int prime[2333]; int primes; for (int i=2;i<=n;i++) flag[i]=0; for (int i=2;i<=n;i++) for (int j=i*i;j<=n;j+=i) flag[j]=1; //表示j被标记为合数 for (int i=2;i<=n;i++) if (flag[i]==0) prime[primes++]=i;
有了这个想法,还可以继续优化
对于质数a,a的所有整数倍都是合数
int flag[2333]; int prime[2333]; int primes; for (int i=2;i<=n;i++) if (flag[i]==0) { prime[primes++]=i; for (int j=i*i;j<=n;j+=i) flag[j]=1; }
还可以继续优化吗?
其实还可以进一步优化,只要每个合数都只被自己能整除的最小的质数标记一次,速度就可以达到线性级别(好快!)
说白了就是每个质数尽可能多的标记合数(也不是很好懂的样子?)
看代码吧:
#include<iostream> using namespace std; int prime[100001]; int f[100001],primes; int n; int main() { cin>>n; for(int i=2;i<=n;i++) { if(!f[i])//如果i还没有被标记,就说明i是新出现的素数 { prime[primes]=i; f[i]=-1; primes++;//标记 } for(int j=0;j<primes;j++)//枚举当前找到的所有素数 { int ans=prime[j]*i; if(ans>n) break;//越界就停止 f[ans]=j+1;//标记每一个含有prime[j] 这个因数的合数 if(i%prime[j]==0)//如果当前 i可以整除prime[j],就说明后面的prime[j]都不会是ans的最小质因子了 break; } } cout<<primes<<endl;//素数个数 for(int i=0;i<primes;i++) { cout<<prime[i]<<" ";//所有素数 } return 0; }
(RP++!)