线性筛选素数

侵删。。。
转自https://www.cnblogs.com/grubbyskyer/p/3852421.html

题目:给出一个正整数n,打印出所有从1~n的素数(即质数);

 

关键是要找出一个判断一个正整数n是否为素数的方法…


 

傻瓜解法–n,n/2

复制代码
 1 #include<stdio.h>
 2 int main()
 3 {
 4   int i,n;
 5   while(scanf("%d",&n)==1)
 6   { for(i=2;i<n;i++)
 7          if(n%i==0)    break; 
 8     if(i==n)    printf("YES\n");
 9     else           printf("NO\n");
10   }
11 }
复制代码

这是理所当然的想法,按照素数的定义,除了1和它本身没有其他的因数,就是素数。

这种解法的缺点就是红色标注那里,i<n,或者有的是i<n….这种循环规模n稍微大点,运行时间不谈了,都是泪…

 


 

普通解法–sqrt(n)

复制代码
 1 #include<stdio.h>
 2 #include<math.h>
 3 int main()
 4 { int i,n,x;
 5   while(scanf("%d",&n)==1)
 6   { x=(int)sqrt(n);
 7     for(i=2;i<=x;i++)
 8          if(n%i==0)    break; 
 9     if(i>x)    printf("YES\n");
10     else           printf("NO\n");
11   }
12 }
复制代码

这里循环取到sqrt(n),效率改进不少了…但显然还是不够理想….继续往下看


 

普通筛选法–埃拉托斯特尼筛法

先简单说一下原理:

基本思想:素数的倍数一定不是素数
实现方法:用一个长度为N+1的数组保存信息(0表示素数,1表示非素数),先假设所有的数都是素数(初始化为0),从第一个素数2开始,把2的倍数都标记为非素数(置为1),一直到大于N;然后进行下一趟,找到2后面的下一个素数3,进行同样的处理,直到最后,数组中依然为0的数即为素数。
说明:整数1特殊处理即可。

举个例子,N=20时,演示如下图:

最后数组里面还是0的就是素数了…

代码实现如下:

prime[]用来保存得到的素数 prime[] = {2,3,5,7,11,………} tot 是当前得到的素数的个数 check :0表示是素数  1表示合数

复制代码
 1 memset(check, 0, sizeof(check));
 2 int tot = 0;
 3 for (int i = 2; i <= n; ++i)
 4 {
 5   if (!check[i])
 6   {
 7     prime[tot++] = i;
 8   }
 9   for (int j = i+i; j <= n; j += i)
10   {
11     check[j] = 1;
12   }
13 }
复制代码

此筛选法的时间复杂度是O(nloglogn) 空间复杂度是O(n)

不足之处也比较明显,手动模拟一遍就会发现,很多数被处理了不止1遍,比如6,在素数为2的时候处理1次,为3时候又标记一次,因此又造成了比较大的不必要处理…那有没有改进的办法呢…就是下面改进之后的筛法…

 


 

线性筛法–欧拉筛法

先上代码吧…

复制代码
 1 #include<cstdio>
 2 #include<cstring>
 3 #define MAXN 100005
 4 #define MAXL 1299710
 5 int prime[MAXN];
 6 int check[MAXL];
 7 
 8 int tot = 0;
 9 memset(check, 0, sizeof(check));
10 for (int i = 2; i < MAXL; ++i)
11 {
12   if (!check[i])
13   {
14     prime[tot++] = i;
15   }
16   for (int j = 0; j < tot; ++j)
17   {
18     if (i * prime[j] > MAXL)
19     {
20       break;
21     }
22     check[i*prime[j]] = 1;
23     if (i % prime[j] == 0)
24     {
25       break;
26     }
27   }
28 }
复制代码

精华就在于红色标注那两处,它们保证每个合数只会被它的最小质因数筛去,因此每个数只会被标记一次,所以时间复杂度是O(n)

还是按上面的例子进行一遍模拟:N=20

此过程中保证了两点:

1、合数一定被干掉了…

2、每个数都没有被重复地删掉

(证明 见参考资料)


 

引申–求欧拉函数

在数论,对正整数n,欧拉函数是小于或等于n的数中与n互质的数的数目。此函数以其首名研究者欧拉命名,它又称为Euler’s totient function、φ函数、欧拉商数等。 例如φ(8)=4,因为1,3,5,7均和8互质。

求欧拉函数的方法只需在上面的程序中稍有改动即可,此处只贴出代码:

按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

若是素数,那么从1~n-1都是和它互质的数,所以phi(i) = i - 1;

另外两个是积性函数(见参考资料)的公式和欧拉函数的特性。


 

参考资料

1、http://suno.cnblogs.com/

2、http://wenku.baidu.com/view/1187eebce009581b6ad9eb12

3、http://blog.csdn.net/dinosoft/article/details/5829550

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值