浅谈筛素数
假设一个数N,我们现在要求1-N之间哪些数是素数,那些不是素数?
下面介绍几种解法(当然打表就免了吧)
(1)最简单最暴力,也是最慢的解法:
枚举 2-N/2 之间的所有数试试可不可以与 N 相除没有余数。
有人说:为啥不枚举N。-----自己悟吧;
#include<stdio.h> int main() { bool flag; int n; while(scanf("%d",&n)==1) { for(i=2;i<=n/2;i++)if(n%i==0) { flag=1; break; } if(flag==0)printf("YES\n"); else printf("NO\n"); } }
(2)最普通最常见的解法:
枚举 2-sqrt(N) 之间的所有数试试可不可以与 N 相除没有余数。
为什么要枚举到sqrt(N)呢?
证明:因为对一个数n,如果他能分解成n=pq,那么pq里必然有一个大于等于根号n一个小于等于根号n,也就是说一个合数必然有一个因子是小于等于根号n的,所以对一个数n,只要检验他有没有小于等于根号n的因子就可以了。
所以代码就好写了.
#include<cstdio> #include<cmath> using namespace std; int main() { bool flag; int n,x; while(scanf("%d",&n)==1) { x=sqrt(n); for(i=2; i<=x; i++) if(n%i==0) { flag=1; break; } if(flag==0)printf("YES\n"); else printf("NO\n"); } }
(4)普通筛选法--埃拉托斯特尼筛法:
听名字,哇哦,如此高大上,一定特别好用吧;
WOC!枚举每一个质因子,能与它乘的都记录为不是。
基本思想:素数的倍数一定不是素数
实现方法:用一个长度为N+1的数组保存信息(0表示素数,1表示非素数),先假设所有的数都是素数(初始化为0),从第一个素数2开 始,把2的倍数都标记为非素数(置为1),一直到大于N;然后进行下一趟,找到2后面的下一个素数3,进行同样的处理,直到最后,数组中依然为0的数即为素数。
说明:整数1特殊处理即可。
prime[]用来保存得到的素数 prime[] = {2,3,5,7,11,.........} tot 是当前得到的素数的个数 check :0表示是素数 1表示合数。
memset(check, 0, sizeof(check)); int tot = 0; for (int i = 2; i <= n; ++i) { if (!check[i]) { prime[tot++] = i; } for (int j = i+i; j <= n; j += i) { check[j] = 1; } }
目测或者手动模拟的话,明显有许多冗余(就是重复的操作),会消耗不少时间。
有没有啥其他的改进方法呢?----下面有请出我们的欧拉吧。
(4)欧拉线性筛:
#include<cstdio> #include<cstring> using namespace std; int prime[]; int check[]; int tot = 0; memset(check, 0, sizeof(check)); for (int i = 2; i < MAXL; ++i) { if (!check[i])prime[tot++] = i; for (int j = 0; j < tot; ++j) { if (i * prime[j] > MAXL)break; //超过了要所求的数,计算就没必要了; check[i*prime[j]] = 1; //可以被乘出来,就一定不是素数咯。 if (i % prime[j] == 0)break; //这个数被它%等于0了,说明已经出现了重复,break。 } } // check中没有被标记过的就是素数咯。
prime[]数组中的素数是递增的,当i能整除prime[j],那么i*prime[j+1]这个合数肯定被prime[j]乘以某个数筛掉。因为i中含有prime[j],prime[j]比prime[j+1]小,即i=k*prime[j],那么i*prime[j+1]=(k*prime[j])*prime[j+1]=k’*prime[j],接下去的素数同理。所以不用筛下去了。因此,在满足i%prime[j]==0这个条件之前以及第一次满足改条件时,prime[j]必定是prime[j]*i的最小因子。
除特别注明外,本站所有文章均为Manjusaka丶梦寒原创,转载请注明来自出处