素数筛选目的是筛选出某一区间[m, n)内的所有素数,常见方法包括如下几种:
1.朴素的筛选法:
先写出判断函数isPrime(),再对区间内的数依次调用isPrime()进行判断,算法核心是以2~根号n作为除数。
#include <math.h> bool isPrime(int n) { /* C++中sqrt有两个重载函数,参数可以是double或float,由于传入参数是int,在默认的隐式类型转换中int可以 转换成float也可以转换为double,则须通过强转告诉编译器该调用哪一个函数。 */ int sqr = (int)sqrt(double(n)); for(int i = 2; i <= sqr; i++) { if(n%sqr == 0) return false; } return true; }
2.高效的筛选法之一:
借助标记数组,对于2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23……初始时均标记素数,模拟如下剔除合数的过程:
从区间首部开始往后依次考察,如果当前m被标记为素数,那么2*m, 3*m, 4*m……都要标记为合数;如果m被标记为合数,则跳过继续考察m+1……当整个区间全部考察完后,所有素数和合数对照标记数组一目了然。
考察2时:2,3,5,7,9,11,13,15,17,19,21,23……;
考察3时:2,3,5,7,11,13,17,19,23……;
考察4时:直接跳过,因为其后如果有4的倍数而作为合数被剔除,那么在考察2时就已执行,此时已无意义;
考察5时:2,3,5,7,11,13,17,19,23……;
……
这种算法的核心思想:
如果在考察到某一个数时,发现它仍然没有在前面多轮剔除合数的过程中被剔除掉,那么它不是此前任何数的倍数,即必定是素数;
接下来剔除其后与其成倍的数,因为这个剔除过程并不是前面某个剔除过程的子集,所以必定有意义。
#include <stdio.h> #include <string.h> #include <iostream> using namespace std; #define N 100 bool isPrime[N+2]; void process() { int i, j; memset(isPrime, true, sizeof(isPrime)); for(i=2; i<=N; i++) { if(isPrime[i]) { for(j=i+i; j<=N; j+=i) isPrime[j] = false; } } } int main() { process(); for(int k=2; k<=N; k++) { if(isPrime[k]) cout << k << " "; } return 0; }
3.高效的筛选法之二:
从区间首部开始遍历,并始终维护一个当前已找到素数的集合,如果当前考察数据都不能被素数集合中的数整除,则作为素数将其加入集合中……依次迭代到区间末尾,最终集合即为结果。
#include <stdio.h> #include <string.h> #include <iostream> using namespace std; #define N 100 int prime[N+2]; int pLen; void setPrime() { prime[0] = 2; pLen = 1; bool isPrime; for(int i=3; i<=N; i++) { isPrime = true; for(int j=0; j<pLen; j++) { if(i%prime[j] == 0) { isPrime = false; break; } } if(isPrime) prime[pLen++] = i; } } int main() { setPrime(); for(int k=0; k<pLen; k++) { cout << prime[k] << " "; } return 0; }