最近重温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实现打印质数完成。