所谓素数,就是因数只有1和自身的数(废话),但我们不讨论1(废话*2)
如果一个数n是合数,那么每一个{d| d|n,d<=√n}都会有一个大于√n的的数c使c*d=n
因此,暴力筛的话只要枚举到√n即可,不过判断一个数就是O(√n),那么筛多了复杂度就无法想象,这种方法多用于判断而不是筛
那么我们就不要暴力了。
筛法1:埃氏筛 复杂度O(nloglogn)
原理:若一个数是质数,那它的倍数都是合数。(显而易见的东西不需要证明)
代码:
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; int n,pri[100001]; bool bj[10000001]; void getpri() { int k=0; for(int i=2;i<=n;i++) {if(!bj[i]){pri[++k]=i; for(int j=2;i*j<=n;j++) bj[i*j]=1; } } } int main() { scanf("%d",&n); getpri(); for(int i=1;pri[i]<=n;i++) {if(pri[i]) printf("%d ",pri[i]); else break; } return 0; }
当然这还是太慢了(只能筛到1e6,大了就爆了)
对于大数据,我们还是希望它的复杂度逼近线性
so,就有了线性筛
线性筛是什么呢?
我们从埃氏筛讲起。埃氏筛会将一个合数重复标记,造成时间的浪费,而线性筛就是避免重复标记的一种筛法。
埃氏筛中j*i枚举的是质数i的倍数,但当j1%i==0时,i*j1一定可以被j/i所标记。那么当j>j1的时候,就可以让另一个数来标记,从而跳出内循环,这样就不会造成重复标记,相当于对于每个合数n,都会被n的最小质因子标记
代码如下:
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; int n,pri[100001]; bool bj[10000001]; void getpri() { int k=0; for(int i=2;i<=n;i++) {if(!bj[i]){pri[++k]=i; for(int j=1;j<=k;j++) {bj[i*pri[j]]=1; if(i%pri[j]==0)//这里枚举i的质数倍,如果i中已经有质因子了,那么pri[j]*i一定可以被另一个数标记 {break; } } } } int main() { scanf("%d",&n); getpri(); for(int i=1;pri[i]<=n;i++) {if(pri[i]) printf("%d ",pri[i]); else break; } return 0; }