小白初学,还请大佬多多指教。
质数概念
质数即素数,是只能被1和它本身整除的自然数。
以遍历十万以内的质数为例逐步进行算法优化:
- 算法一:
class PrimeNumberTest{
public static void main(String[] args){
long start = System.currentTimeMillis();
boolean isFlag = true;//标识i是否被j除尽过,一旦除尽,修改其值
for(int i=2; i<=100000; i++){//遍历100000以内的自然数
//boolean isFlag = true;//标识i是否被j除尽过,一旦除尽,修改其值
for(int j=2; j<i; j++){//j:被i去除
if(i%j == 0){//i被j除尽
isFlag = false;
}
}
if(isFlag){
System.out.println(i);
}
//重置isFlag
isFlag = true;
}
long end = System.currentTimeMillis();
System.out.println("程序执行时间:" + (end - start));
}
}
相比于遍历奇数偶数来说,遍历质数的难点在于一次循环不能确定一个数是不是质数,必须经过嵌套循环完成之后才可以确定,因此引入一个布尔型变量isFlag,用来标识遍历的结果状态。其中如果把isFlag的声明放在一层循环以内,这样虽然可以简化几行代码,但是过多地消耗了内存,有点得不偿失。
这种方式可以实现遍历,但效率极低。
- 算法二:
class PrimeNumberTest{
public static void main(String[] args){
long start = System.currentTimeMillis();
boolean isFlag = true;//标识i是否被j除尽过,一旦除尽,修改其值
for(int i=2; i<=100000; i++){//遍历100000以内的自然数
//boolean isFlag = true;//标识i是否被j除尽过,一旦除尽,修改其值
for(int j=2; j<i; j++){//j:被i去除
if(i%j == 0){//i被j除尽
isFlag = false;
break;//优化一:只对本身非质数的自然数是有效的。只要有一个被除尽就不是质数了,就不需要往下执行了
}
}
if(isFlag){
//System.out.println(i);
}
//重置isFlag
isFlag = true;
}
long end = System.currentTimeMillis();
System.out.println("程序执行时间:" + (end - start));
}
}
算法二对算法一进行了第一步优化,当判定一个数是质数时,做两个工作,一是修改结果状态标识,二是终止内层循环,这样可以大大减少遍历次数。
- 算法三:
class PrimeNumberTest{
public static void main(String[] args){
long start = System.currentTimeMillis();
boolean isFlag = true;//标识i是否被j除尽过,一旦除尽,修改其值
for(int i=2; i<=100000; i++){//遍历100000以内的自然数
//boolean isFlag = true;//标识i是否被j除尽过,一旦除尽,修改其值
for(int j=2; j<=Math.sqrt(i); j++){//j:被i去除 优化二
if(i%j == 0){//i被j除尽
isFlag = false;
break;//优化一:只对本身非质数的自然数是有效的。只要有一个被除尽就不是质数了,就不需要往下执行了
}
}
if(isFlag){
System.out.println(i);
}
//重置isFlag
isFlag = true;
}
long end = System.currentTimeMillis();
System.out.println("程序执行时间:" + (end - start));
}
}
该算法使用Math.sqrt()方法,这个方法用来求解一个数的平方根,我们都知道,一个数等于它的平方根的乘积,如上图:
i = Math.sqrt(i) * Math.sqrt(i)
假设 y 小于 Math.sqrt(i) ,如果 i = x*y , 那么 x 一定大于 Math.sqrt(i) 。
当我们循环遍历时,只需要遍历到 Math.sqrt(i) 即可,因为我们从2开始遍历时,到 Math.sqrt(i) ,如果这个数不是质数,在2到 Math.sqrt(i) 这个区间内一定存在一个数 y 使得 i = x*y ,这样就能断定这时的 i 一定不是质数,因为 y 既不是1也不是i。
通过这种算法上的优化,可以大大提高代码的执行效率。
- 算法四:
class PrimeNumberTest{
public static void main(String[] args){
long start = System.currentTimeMillis();
lable:for(int i=2; i<=100000; i++){//遍历100000以内的自然数
//优化二:对本身是质数的自然数是有效的。
for(int j=2; j<=Math.sqrt(i); j++){//j:被i去除
if(i%j == 0){//i被j除尽
continue lable;//终止指定标签的for循环的本次循环操作
}
}
//凡是能执行到这个步骤的,一定是质数,因为非质数全都被终止当次的循环操作了
System.out.println(i);
}
long end = System.currentTimeMillis();
System.out.println("程序执行时间:" + (end - start));
}
}
进一步对算法三进行了代码优化,省去了结果状态标识的定义,使用continue关键字并指定for循环标签,终止指定标签的for循环的当次操作。