素数基本(埃氏筛法/线性筛法)

举报
兔老大 发表于 2021/05/07 04:13:08 2021/05/07
【摘要】 一、检查n是否为素数    最简单思路:所有可能的因数全部试一遍。   int gg(int n){ for(int i=2;i<n;i++){ if((n%i)==0)return 0;//有因数就不是素数咯 } return 1;}   进一步思考:没必要枚举所有的数,每一个小于n^(1/2)的因数i,一定有一个大...

一、检查n是否为素数

  

最简单思路:所有可能的因数全部试一遍。

 


  
  1. int gg(int n)
  2. {
  3. for(int i=2;i<n;i++){
  4. if((n%i)==0)return 0;//有因数就不是素数咯
  5. }
  6. return 1;
  7. }

 

进一步思考:没必要枚举所有的数,每一个小于n^(1/2)的因数i,一定有一个大于n^(1/2)的因数j与之对应,也就是i*j=n,所以枚举小于等于n^(1/2)的因数即可

 


  
  1. int gg(int n)
  2. {
  3. for(int i=2;i*i<=n;i++){
  4. if((n%i)==0)return 0;
  5. }
  6. return 1;
  7. }

 

二、约数枚举

 上面已经说过,不需要枚举所有因数,枚举出某小因数以后算出对应的大因数即可。


  
  1. vector<int> gg(int n)
  2. {
  3. vector<int> a;
  4. for(int i=2;i*i<=n;i++){
  5. if((n%i)==0){
  6. a.push_back(i);
  7. if((n/i)!=i)a.push_back(n/i);//根号n的情况不要重复添加
  8. }
  9. }
  10. return a;
  11. }

 

三、埃氏筛法

 只对一个整数操作,O(N),已经足够了,如果对许多整数进行素性检测,还有更高效的算法,比如埃氏筛法。

问题:枚举n以内所有素数

操作:先把所有整数列出来,然后把2的倍数全部剔除,然后是三的,以此类推,遍历所有素数,把倍数全部划去。

对于每个数字i,如果没被划去,他一定是素数,因为他不是任何2到i-1数字的倍数。然后就开始划它的倍数就好。


  
  1. int a[maxx];
  2. int b[maxx+1];
  3. int gg(int n)
  4. {
  5. int p=0;//记录素数个数
  6. for(int i=0;i<n+1;i++)b[i]=1;
  7. b[0]=0;
  8. b[1]=0;
  9. //准备完毕
  10. for(int i=2;i<=n;i++){
  11. if(b[i]){
  12. a[p++]=i;//记录素数和个数
  13. for(int j=2*i;j<=n;j+=i)b[j]=0;//剔除倍数
  14. }
  15. }
  16. return p;//返回素数个数
  17. }

四、区间筛法

 给定整数a和b,请问区间[a,b)内有多少个素数? 

思路:之前说过,因为b以内合数的最小质因数一定不超过sqrt(b),如果有sqrt(b)以内的素数表的话,就可以把筛选法用在[a,b)上了,先分别做好[2,sqrt(b))的表和[a,b)的表,然后从[2,sqrt(b))的表中筛得素数的同时,也将其倍数从[a,b)的表中划去,最后剩下的就是区间[a,b)内的素数了。


  
  1. //不gg了,这次就来个标准一点的吧
  2. typedef long long ll;
  3. bool is_prime[maxn];
  4. bool is_prime_small[maxn];
  5. void segment_sieve(ll a,ll b)
  6. {
  7. for(ll i=0;i*i<b;++i) is_prime_small[i]=true; //初始化
  8. for(ll i=0;i<b-a;++i) is_prime[i]=true; //初始化,注意下标变化,为了省空间
  9. for(ll i=2;i*i<b;++i) {
  10. if(is_prime_small[i]) {
  11. for(ll j=2*i;j*j<b;j+=i) is_prime_small[j]=false; //筛选[2,sqrt(b));
  12. //(a+i-1)/i得到最接近a的i的倍数,最低是i的2倍,然后筛选
  13. for(ll j=max(2LL,(a+i-1)/i)*i;j<b;j+=i) is_prime[j-a]=false;
  14. }
  15. }
  16. }

五、线性实现

筛法很多数被处理了不止1遍,比如6,在素数为2的时候处理1次,为3时候又处理一次,因此又造成了不必要处理。O(nloglogn)已经基本可以满足一般需要了。

本代码保证每个合数只会被它的最小质因数筛去,因此每个数只会被标记一次,所以时间复杂度是O(n)

证明略

话不多说,上板子


  
  1. #include<cstdio>
  2. #include<cstring>
  3. #define MAXN 100005
  4. #define MAXL 1299710
  5. int prime[MAXN];
  6. int check[MAXL];
  7. int tot = 0;
  8. memset(check, 0, sizeof(check));
  9. for (int i = 2; i < MAXL; ++i)
  10. {
  11. if(!check[i])prime[tot++] = i;
  12. for (int j = 0; j < tot; ++j)//****************************************
  13. {
  14. if (i * prime[j] > MAXL)break;//*******************
  15. check[i*prime[j]] = 1;
  16. if (i % prime[j] == 0)break;//******
  17. }
  18. }

素数基本就这些内容咯。。。。

文章来源: fantianzuo.blog.csdn.net,作者:兔老大RabbitMQ,版权归原作者所有,如需转载,请联系作者。

原文链接:fantianzuo.blog.csdn.net/article/details/81486370

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。