Java基础练习题——100以内的质数输出

一、100以内的质数输出

1. 概念解释

质数:“除了1和它本身外,没有其他约数” 的数【即,数字 n 只能被 1 和 自身n 整除,此外不能被 2 ~ (n-1) 整除】;

2. 代码实现

1)、 基础版一(不加break跳出循环)
/**
 * 基础版一:不加 break
 */
public static void forForTest4_1(){
	long start = System.currentTimeMillis(); //开始时间

	boolean isFlag = true; //标记位(标记当前i是否为质数)

	for(int i = 2; i <= 100000; i++){ //最小的质数是2
		int j = 2; //从2开始,到i-1为止,没有i的因子
		while(j <= i - 1){
			if(i % j == 0){
				isFlag = false;
				//break;
			}
			j++;
		}
		
		if(isFlag == true){ //说明if根本没进去过,即:不存在其他因子
			System.out.println(i);
		}
		
		isFlag = true; //重置isFlag标记位
	}
	
	long end = System.currentTimeMillis(); //结束时间
	System.out.println();
	System.out.println("程序运行时间为:" + (end - start) + " ms");
}

2)、 基础版二(加break)

【这里我没有使用标记位 isFlag,直接使用循环计数器 i 和 j 作为判断条件了,但实际上效果一样,效率也没什么差别】

/**
 * 基础版二:加 break
 */
public void forForTest4_2(){
		long start = System.currentTimeMillis(); //开始时间
		
		for(int i = 2; i <= 100000; i++){ //最小的质数是2
			int j = 2; //从2开始,到i-1为止,没有i的因子
			while(j <= i - 1){
				if(i % j == 0){
					break;
				}
				j++;
			}
			
			if(j == i){ //说明if根本没进去过,即:不存在其他因子
				System.out.println(i);
			}
		}
		
		long end = System.currentTimeMillis(); //结束时间
		System.out.println();
		System.out.println("程序运行时间为:" + (end - start) + " ms");
	}

3. 优化实现

1)、 优化点
  • ①. 当遍历过程中,遇到 i % j == 0 的情况时,已经判断出必然不是质数了,直接break【已经得出结论,后续循环无意义】
  • ②. 内存循环不必遍历到 (i-1),只需遍历到 i \sqrt[]{i} 即可【因为,首先,例如:10 / 7,>i/2 往后根本不必考虑,不可能除的尽;其次,例如:10=5*2,因子都是成对出现,前面能除尽,后面也不用判断了(不可能后面除的尽,前面除不尽),而此处这个临界值 i 就是 i²=10】
  • ③. //2是质数,而且除此之外,偶数不可能是质数(至少多有一个因子2),因此外层循环可以考虑 i+=2【优化为O(n/2),但实际上时间复杂度从量级上来说并没有变,仍是O(n)】

【-----记得截图:不加break,加break,范围i/2,范围根号i】

2). 代码实现
/**
 * 优化点:
 * 	①. 当遍历过程中,遇到 i % j == 0 的情况时,已经判断出必然不是质数了,直接break【已经得出结论,后续循环无意义】
 *	②. 内层循环不必遍历到 (i-1),只需遍历到 i/2,因为后面根本不可能会除的尽【例如:10 / 7,根本不必考虑,>i/2 后根本不可能除尽】
 *			---> 可以进一步优化,实际上只需要考虑到 "根号i",【因为例如:10=5*2,因子都是成对出现,前面能除尽,后面也不用判断了(不可能后面能除尽,前面除不尽),临界值就是 i²=10】
 *	③. //众所周知,2是质数,而且除此之外,偶数不可能是质数(至少多有一个因子2),因此外层循环 i+=2【优化为O(n/2),但实际上时间复杂度从量级上来说并没有变,仍是O(n)】
 */
/* 优化版 */
public void forForTest4_3(){
	long start = System.currentTimeMillis(); //开始时间
	
	boolean isFlag = true; //标记位(标记当前i是否为质数)

	for(int i = 2; i <= 100000; i++){ //最小的质数是2
		int j = 2; //从2开始,到i-1为止,没有i的因子
		while(j <= Math.sqrt(i)){ //优化点二【对本身是质数的自然数有效(非质数理论上也有效,但实际执行上来说,不等到根号i,就break了)】
			if(i % j == 0){
				isFlag = false;
				break; //优化点一【只对本身是非质数的自然数有效】
			}
			j++;
		}
		
		if(isFlag){ //说明if根本没进去过,即:不存在其他因子
			System.out.println(i);
		}
		
		isFlag = true; //重置isFlag标记位
	}
	
	long end = System.currentTimeMillis(); //结束时间
	System.out.println();
	System.out.println("程序运行时间为:" + (end - start) + " ms");
}

4. 使用带标签的 continue / break

【continue / break 默认是跳出包裹的循环中最近的一层,而使用带标签的 continue / break,则可以指定跳出某层循环(标签指定)】

/* 带标签的continue版 */
public void forForTest4_4(){
	long start = System.currentTimeMillis(); //开始时间
	
	label : for(int i = 2; i <= 100000; i++){ //最小的质数是2 //优化点③
		int j = 2; //从2开始,到i-1为止,没有i的因子
		while(j <= Math.sqrt(i)){
			if(i % j == 0){
				continue label; //【使用带标签的break或continue】
			}
			j++;
		}
		
		System.out.println(i);
	}
	
	long end = System.currentTimeMillis(); //结束时间
	System.out.println();
	System.out.println("程序运行时间为:" + (end - start) + " ms");
}

5. 效率分析(测试 100000 以内的质数)

【实际上,为了防止 System.out.println() 输出对执行时间的干扰,这里测试只是简单的对质数进行了计数 “count++”,没有进行输出,从而最大程度的显示不同实现算法的效率差异】
在这里插入图片描述

补充:衡量一个功能代码的优劣:
  • 正确性
  • 可读性
  • 健壮性
  • 高效率和低存储(时间复杂度 & 空间复杂度,衡量一个算法的好坏)

猜你喜欢

转载自blog.csdn.net/wszhbl/article/details/89502869