松鼠的窝 2018-01-18
素数又称为质数,是指除了 1和本身之外,不能被其他数整除的一类数。应特别注意的是,1既不是素数,也不是合数。
本章将解决两个问题:1.如何判断给定的正整数 n是否是质数;2.如何在较短的时间内得到 1~n内的素数表。
只需要判定 n能否被 2,3,... ,$ \left \lfloor sqrt(n) \right \rfloor $中的一个整除,即可判断 n是否为素数。该算法的复杂度为 O(sqrt(n))。
代码如下:
1 // 判断 n 是否为素数 2 bool isPrime(int n) { 3 if(n <= 1) return false; // 特判 4 int sqr = (int)sqrt(n*1.0); // 根号 n 5 for(int i=2; i<=sqr; ++i) { // 遍历 2~根号n 6 if(n%i == 0) return false;s 7 } 8 return true; 9 }
通过上面的学习,已有办法判断单独一个数是否是素数,那么可以从 1~n进行遍历,判断每个数是否是素数,如果是素数则加入素数表。下面是完整的求解 100以内的所有素数的程序:
1 /* 2 素数 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <math.h> 8 #include <stdlib.h> 9 #include <time.h> 10 #include <stdbool.h> 11 12 // const 在 c 中不代表常量 13 #define maxn 101 // 表长 14 int pri[maxn], pNum=0; // prime 储存素数,pNum 储存素数个数 15 16 // 判断 n 是否为素数 17 bool isPrime(int n) { 18 if(n <= 1) return false; // 特判 19 int sqr = (int)sqrt(n*1.0); // 根号 n 20 // c 要求先定义变量 21 int i; 22 for(i=2; i<=sqr; ++i) { // 遍历 2~根号n 23 if(n%i == 0) return false; 24 } 25 return true; 26 } 27 28 // 素数表获取 29 void findPrime() { 30 int i; 31 for(i=1; i<101; ++i) { 32 if(isPrime(i)) { 33 pri[pNum++] = i; 34 } 35 } 36 } 37 38 int main() { 39 findPrime(); 40 int i; 41 for(i=0; i<pNum; ++i) { 42 printf("%d ", pri[i]); 43 } 44 45 return 0; 46 }
下面介绍一个更高效的算法,埃氏筛选。算法从小到大枚举所有数,对每一个素数,筛去其所有的倍数,剩下的就都是素数。至于“筛”这个动作的实现,可以使用一个 bool型数组 p来标记,如果 a被筛掉,那么 p[a]为 true;否则,p[a]为false。在程序开始时可以初始化 p数组全为 false。
下面是完整的用素数筛法求解 100以内的所有素数的程序:
1 /* 2 素数_埃氏筛选 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <math.h> 8 #include <stdlib.h> 9 #include <time.h> 10 #include <stdbool.h> 11 12 #define maxn 101 13 int pri[maxn], pNum=0; 14 bool p[maxn] = {0}; // 表示数是否被筛掉 15 16 // 素数表的获取,埃氏筛选 17 void findPrime() { 18 int i; 19 for(i=2; i<maxn; ++i) { 20 if(!p[i]) { // 没有被筛掉,为素数 21 pri[pNum++] = i; 22 int j; 23 for(j=i+i; j<maxn; j+=i) { // 筛掉倍数 24 p[j] = true; 25 } 26 } 27 } 28 } 29 30 int main() { 31 findPrime(); 32 int i; 33 for(i=0; i<pNum; ++i) { 34 printf("%d ", pri[i]); 35 } 36 37 return 0; 38 }
题目:令Pi表示第i个素数。现任给两个正整数M <= N <= 104,请输出PM到PN的所有素数。
思路:把素数表打至第 N个素数,然后按格式输出即可。
下面是完整的程序:
1 /* 2 【PAT B1013】数素数 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <math.h> 8 #include <stdlib.h> 9 #include <time.h> 10 #include <stdbool.h> 11 12 #define maxn 1000001 13 int pri[maxn], pNum=0; 14 bool p[maxn] = {0}; // 表示数是否被筛掉 15 16 // 素数表的获取,埃氏筛选 17 void findPrime(int n) { 18 int i; 19 for(i=2; i<maxn; ++i) { 20 if(!p[i]) { // 没有被筛掉,为素数 21 pri[pNum++] = i; 22 if(pNum >= n) break; // 只把素数表打至第 N 个素数 23 int j; 24 for(j=i+i; j<maxn; j+=i) { // 筛掉倍数 25 p[j] = true; 26 } 27 } 28 } 29 } 30 31 int main() { 32 int m, n; 33 scanf("%d%d", &m, &n); // 输入 m,n 34 findPrime(n); // 生成素数表 35 int i, cnt=1; 36 for(i=m; i<=n; ++i) { 37 printf("%d", pri[i-1]); // 数组下标从 0 开始 38 if((cnt++)%10 && i<n) printf(" "); 39 else printf("\n"); // 每10个数字占1行 40 } 41 42 return 0; 43 }