假设一个数N,我们现在要求1-N之间哪些数是素数,那些不是素数?
下面介绍几种解法(当然打表就免了吧)
(1)最简单最暴力,也是最慢的解法:
枚举 2-N/2 之间的所有数试试可不可以与 N 相除没有余数。
有人说:为啥不枚举N。-----自己悟吧;
#includeint 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#include 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#include 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的最小因子。