最近重温Java基础,感觉这个例子不错,在此做个记录与分享。
首先理解一下质数的含义:质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
下面开始写实现逻辑,因后面会有程序性能优化,基数小优化效率不明显,所以我们直接获取100000以内的所有质数并打印,第一版直接上代码,如下:
/**
* 获取质数并打印
*/
public class PrimeNumberTest {
public static void main(String args[]){
Long start = System.currentTimeMillis();
// 设置标识,用于判断是否为质数,默认为false,即默认为质数
boolean isFlag = false;
// 循环每个数
for(int i = 2; i <= 100000; i++){
// 循环每个数的除数
for(int j = 2; j < i; j++){
// j被i除尽,表示i为非质数,修改标识isFlag值为true
if(i % j == 0){
isFlag = true;
}
}
// 判断标识是否false,为false则打印质数i
if(!isFlag) {
System.out.println(i);
}
isFlag = false;
}
Long end = System.currentTimeMillis();
System.out.println("执行时间为:" + (end - start));
}
}
上面的代码已经可以实现打印100000以内的所有质数,我们来看一下程序执行时间:
.....
.....
99923
99929
99961
99971
99989
99991
执行时间为:17091
执行结果显示执行时间为17秒左右。
下面,为了提升效率,还有性能优化提升的空间。
具体优化细节如下:
优化一:当进入if(i % j == 0)判断后,说明i已经有了因子,是质数,就不需要继续循环i的因数,用break;跳出当前循环即可,代码如下:
// j被i除尽,表示i为非质数,修改标识isFlag值为true
if(i % j == 0){
isFlag = true;
break;// 优化一:只对非质数
}
我们再来看一下优化后的程序执行效率如何:
.....
.....
99923
99929
99961
99971
99989
99991
执行时间为:1511
由此可见,执行时间缩短为1.5秒,程序的执行效率成倍提升。
优化二:优化一针对的是非质数做了相关优化,其实还可以对因子循环条件进行优化。
for(int j = 2; j < i; j++),会循环2至i-1的所有数字,但其实因子在i/2之前和之后,是有一个对应关系的,如果j能被i/2前一个数整除,那么j也能被i/2后的对应的一个数整除,所以循环条件中只需要取i一半的数进行运算即可满足需求,以此翻倍提高效率。
按照上面的逻辑,可将循环条件数量缩减为i的开方进行运算,优化代码如下:
/**
* 开方在这里的作用是减少循环次数,只循环比开方后的数小的数字,
* 因为开方后的数字与开方前的数字的乘积会是结果中的某一个数,
* 所以不需要再耗费资源再去对开方后的数字进行取余。
*/
for(int j = 2; j <= Math.sqrt(i); j++){
// j被i除尽
if(i % j == 0){
isFlag = true;
break;// 优化一:只对非质数
}
}
Math.sqrt(i)方法指的是对自然数i进行开方,注意:j < i,需要修改为j <= Math.sqrt(i)。
再执行一下看下程序执行时间:
.....
.....
99923
99929
99961
99971
99989
99991
执行时间为:94
执行时间提升至0.09秒,程序效率有了质的提升。
以下为程序完整代码:
public class PrimeNumberTest {
public static void main(String args[]){
Long start = System.currentTimeMillis();
// 设置标识,用于判断是否为质数,默认为false,即默认为质数
boolean isFlag = false;
// 循环每个数
for(int i = 2; i <= 100000; i++){
//Math.sqrt()开方
/**
* 开方在这里的作用是减少循环次数,只循环比开方后的数小的数字,
* 因为开方后的数字与开方前的数字的乘积会是结果中的某一个数,
* 所以不需要再耗费资源再去对开方后的数字进行取余。
*/
for(int j = 2; j <= Math.sqrt(i); j++){
// j被i除尽,表示i为非质数,修改标识isFlag值为true
if(i % j == 0){
isFlag = true;
break;// 优化一:只对非质数
}
}
// 判断标识是否false,为false则打印质数i
if(!isFlag) {
System.out.println(i);
}
isFlag = false;
}
Long end = System.currentTimeMillis();
System.out.println("执行时间为:" + (end - start));
}
}
Java实现打印质数完成。