线性筛法求素数
题目:给出一个正整数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互质。
求欧拉函数的方法只需在上面的程序中稍有改动即可,此处只贴出代码:
1 #include<cstdio> 2 #include<cstring> 3 #define MAXN 100005 4 #define MAXL 1299710 5 int prime[MAXN]; 6 int check[MAXL]; 7 int phi[MAXL]; 8 int tot = 0; 9 phi[1] = 1; 10 memset(check, 0, sizeof(check)); 11 for (int i = 2; i < MAXL; ++i) 12 { 13 if (!check[i]) 14 { 15 prime[tot++] = i; 16 phi[i] = i - 1; 17 } 18 for (int j = 0; j < tot; ++j) 19 { 20 if (i * prime[j] > MAXL) 21 { 22 break; 23 } 24 check[i*prime[j]] = 1; 25 if (i % prime[j] == 0) 26 { 27 phi[i*prime[j]] = phi[i] * prime[j]; 28 break; 29 }else 30 { 31 phi[i*prime[j]] = phi[i] * (prime[j]-1); 32 } 33 } 34 }
若是素数,那么从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