【数论-1】质数/素数 筛法
质数/素数的筛法
1.质数/素数的朴素筛法
bool isprime[MAXN]; for(int i=2;i<=n;i++){ isprime[i]=1; for(int j=2;j*j<=i;j++){ if(i%j==0){ isprime[i]=0; break; } } }
关于此种筛法,对于一些简单的入门题目是可以通过的(P1125)
但不足之处是很明显的,对于算法竞赛,“TLE”也是不能通过题目的一项重要原因。
那么如何优化呢?枚举的次数过多时,可以减少枚举的次数。
优化:枚举到sqrt(n)即可
bool isprime[MAXN]; int prime[MAXN],cntprime=0; for(int i=3;i<=n;i++){ isprime[i]=1; for(int j=1;prime[j]*prime[j]<=i&&j<=cntprime;j++){ if(i%prime[j]==0){ isprime[i]=0; break; } } if(isprime[i]==1){ prime[++cntprime]=i; } }
2.质数/素数的埃氏筛+欧式筛
对于一个质数,它的倍数一定不是质数(可通过质数的定义来证明)
那么我们可以筛出一个质数,然后对它的倍数进行标记:
bool isntprime[MAXN]={0}; for(int i=2;i<=n;i++){ if(isntprime[i]==1) continue; for(int j=i+i;i<=n;j+=i){ isntprime[j]=1; } }
复杂度为O(nloglogn)
但其也不是完美的解决方案,比如12=2*6,12=3*4 这样的数字可能会被筛多次(>=2)
这样就造成了时间的浪费
我们对其改进:
bool isntprime[MAXN]={0}; for(int i=2;i<=n;i++){ if(isntprime[i]==1) continue; for(int j=i*i;i<=n;j+=i){ isntprime[j]=1; } }
从p^2开始筛除即可,可将其优化,但其时间复杂度不改变
欧式筛(欧拉筛法)
欧拉(Euler)筛法,简称欧式筛,或因为其线性复杂度被称呼为线性筛。由瑞士数学家欧拉提出
它在埃氏筛的基础上,用一个方法,限定了每个数只被其最小质因数 fcfc 筛到一次,从而保证时间复杂度为 O(n)O(n)
思想比较巧妙:
对于当前数字 nn ,假设它的最小质因数为 fcfc
对于已经筛出的质数,存在表 prime 中
那么,我们从质数表中,枚举最小质因数不大于 fcfc 的质数 pp
我们就能保证: p×np×n 的最小质因数一定为 pp
那么,事先没被标记最小质因数的数字就一定是质数,且最小质因数为它本身
代码实现如下:
vector<int> Prime; int fc[MAXN]; for(int i=2;i<=n;i++){ if(fc[i]==0){ fc[i]=i; Prime.push_back(i); } for(auto p : Prime) if(p>fc[i]||p*i>n) break; else fc[p*i]=p; }//STL版本
时间复杂度O(n)
3.对欧拉筛法空间的优化
对于欧式筛法,已经足够优化速度,但对于空间的优化几乎为0
如果你开一个prime[1e8+1],flag[1e8+1](int类型)
那么不难想象空间会如何超出题目限制(P3383 P3912)
如果你使用Int型数组 则难逃一死
so,可以开一个Bool 数组来储存每个数字的状态(被筛与否)
然后处理
#include <iostream> using namespace std; const int maxn=1e8+1; int n=0;int q=0; int k=0; int cnt=0; bool flag[maxn]={0}; int primes[maxn]={0}; void getprimes(int x){ for(int i=2;i<=x;i++){ if(!flag[i]){ primes[++cnt]=i; } for(int j=1;j<=cnt;j++){ if(i*primes[j]>x){ break; } flag[i*primes[j]]=1; if(!(i%primes[j])){ break; } } } } int main(){ std::ios::sync_with_stdio(0); cin>>n>>q; getprimes(n); for(int i=1;i<=q;i++){ cin>>k; cout<<primes[k]<<endl; } return 0; }
#include <iostream> using namespace std; const int max1=1e8+1; bool flag[max1]={0};//存储标记 int primes[max1];//存素数 int ans=0; void getprimes(int x){ for(int i=2;i<=x;i++){ //从2开始判断质数,1不是质数 if(!flag[i]){ //默认0,取反之后为true->执行计数和存储到数组中 primes[++ans]=i; } for(int j=1;j<=ans;j++){//搜索小于prime[i]的所有质数 if(i*primes[j]>x){ break; //1.i*prime[j]不能大于所求范围n; } flag[i*primes[j]]=1;//标记flag[质数*质数]=合数 if(!(i%primes[j])){ break;//2.如果prime[j]是i的一个质因子,则退出,作为优化(后续的i*prime[j]会有更小的质因子存在,为了不除多次) } } } } int main(){ int n; cin>>n; getprimes(n); cout<<ans; return 0; }
还可以使用<bitset>的奇怪方法(Orz大佬)

浙公网安备 33010602011771号