Greeting!
今天记录一下如何用埃拉托色尼筛选法实现输出小于某个数n的所有质数,大家都知道质数是除了自己和1之外的所有数整除的数。最粗暴的实现方法就是用两个for循环,在最里层进行取余操作,如果取余为0则不是质数。除此之外,还可以用一种叫埃拉托色尼筛选法(the Sieve of Eratosthenes)的办法,节省内存和运算时间。
埃拉托色尼筛选法的原理
(1)先把1删除(现今数学界1既不是质数也不是合数)
(2)读取队列中当前最小的数2,然后把2的倍数删去
(3)读取队列中当前最小的数3,然后把3的倍数删去
(4)读取队列中当前最小的数5,然后把5的倍数删去
(5)读取队列中当前最小的数7,然后把7的倍数删去
(6)如上所述直到需求的范围内所有的数均删除,剩下的就是质数图片来自百度百科
实现代码
代码由java语言实现,源代码来自 CS 61B (2014 Spring):
public static void printPrimes(int n) {
boolean[] prime = new boolean[n + 1]; // Numbered 0...n.
int i;
for (i = 2; i <= n; i++) {
prime[i] = true;
}
for (int divisor = 2; divisor * divisor <= n; divisor++) {
if (prime[divisor]) {
for (i = 2 * divisor; i <= n; i = i + divisor) {
prime[i] = false; // i is divisible by divisor.
}
}
}
for (i = 2; i <= n; i++) { //print the prime
if (prime[i]) {
System.out.print(" " + i);
}
}
}
代码原理分析这套code的特点在于,创造性地建立了大小为n+1的,bool类型的char数组。
依据char的序号作为筛选质数的作为循环的数本体(从0到n),然后根据对应的char数组里面的bool类型来判断是否为质数。True为是质数,False为不是质数。
注意1:为什么要n+1个数组单位?因为在数组中,序号从0开始计数,所以我们要将数字大小定为n,数组的大小就必须为n+1。(在这里,序号0和1都是没有必要考虑的,原因见原理部分)在第一个循环中,我们假定所有的char数组暂时都为true(即0到n都为质数)。
利用iteration,将所有队列中最小的数(2,3,5,7,11….)的倍数给删去,即下图表格所显示的值都不是质数,所以标为false。
注意2:可以看出这里的divisor为质数从小到大一直到
(后面说明原因),那么如何确定divisor呢?我们根据divisor为质数,可知某质数作为char的序号对应的bool类型为True。可使用if语句:
if (prime[divisor])
如果满足True,则进行下面的循环:
for (i = 2 * divisor; i <= n; i = i + divisor){
prime[i] = false;
}
例如,这里的4所对应的布尔数组已经在第一次循环中(2×2)被标为了false,因此只有这个char[]为true,才进行iteration,因此选出了队列中当前最小的数。作者用Excel画的渣图
注意3:为什么divisor只要到
呢?我们看到图中标同样颜色的框,其实是重复的,事实上我们如果去除重复的话,表中的信息呈现一个倒三角形状。2从2×2开始,3从3×3开始,5从5×5开始等等,类似于我们小时候背的乘法口诀表。所以当divisor为
及附近时,要从
的平方开始,我们要求n以内的质数,再往上就没意义了。在代码段的最后一段中,剩下位置对应的数就是质数,因为在上一个操作中没有被删掉。
利用循环输出标记仍为true的char所对应的位置序号。
最终,我们就可以在屏幕中输出想要的n以内质数啦!