Java讲课笔记07:计数循环与嵌套循环

零、本讲学习目标

1、掌握for循环语句的使用

2、掌握循环嵌套的使用

3、掌握跳转语句的使用

一、计数循环(for循环)

for循环语句是最常用的循环语句,一般用在循环次数已知的情况下,通常情况下可以代替while循环。

1、语法格式

for(初始化表达式; 循环条件; 操作表达式){
    执行语句
    ...
    break | continue
    ...
}

2、执行流程

在这里插入图片描述
当循环条件成立时,执行循环,直到循环条件不成立时,终止循环。

3、案例演示

任务1、演示死循环(永真循环)

在这里插入图片描述

package net.hw.lesson07;

/**
 * 功能:利用for循环实现死循环
 * 作者:华卫
 * 日期:2020年4月14日
 */
public class Example701 {
    public static void main(String[] args) {
        // 理论上是死循环,但实际上会结束
        int i;
        for (i = 1; i >= 1; i = i + 10000) {
            System.out.println("i = " + i);           
        }
        System.err.println("i = " + i);

        // 最直接了当的死循环,相当于while(true)
        for (; ; ) {
            System.out.println("我要出去玩!");
        }
    }
}

运行程序,查看结果:
在这里插入图片描述
会一直输出“我要出去玩!”,永远都不会结束,除非我们强制终止应用程序。
在这里插入图片描述
在这里插入图片描述

任务2、计算1 + 2 + 3 + …… + 100的值

等差数列求和问题,公差为1,如果循环变量是i,那么循环的更新条件就是i++。
在这里插入图片描述

package net.hw.lesson07;

/**
 * 功能:计算1 + 2 + 3 + …… + 100的值
 * 作者:华卫
 * 日期:2020年4月14日
 */
public class Example702 {
    public static void main(String[] args) {
        int sum = 0;

        for (int i = 1; i <= 100; i++) {
            sum += i;
        }

        System.out.println("1 + 2 + 3 + …… + 100 = " + sum);
    }
}

运行程序,查看结果:
在这里插入图片描述
可以参看实现相同任务的Python代码:
在这里插入图片描述

课堂练习1:编程计算1 + 3 + 5 + …… + 99的值

提示:有三种方法可以解决此问题。
(1)修改循环的更新条件:i = i + 2;
(2)在循环结构里嵌套一个过滤器:if (i % 2 == 1) sum += i;
(3)修改循环条件:i <= 50,修改累加语句 sum += 2 * i - 1;
在这里插入图片描述

任务3、打印全部水仙花数

所谓水仙花数,是指等于其各位数字立方和的三位数。

153 = 1 3 + 5 3 + 3 3 153=1^3+5^3+3^3
370 = 3 3 + 7 3 + 0 3 370=3^3+7^3+0^3
371 = 3 3 + 7 3 + 1 3 371=3^3+7^3+1^3
407 = 4 3 + 0 3 + 7 3 407=4^3+0^3+7^3

扫描二维码关注公众号,回复: 11266771 查看本文章

分析问题,首先水仙花数是三位数,那么我们可以确定范围:100~999,这个我们可以通过循环结构来搞定:

for (int n = 100; n <= 999; n++) {
    ...
}

然后对于这个范围的每个数n,我们要去判断它是否等于其各位数字的立方和,这里的难点或关键在于如何分解一个三位数,得到它的每位数字。

假设我们已经把三位数n分解成百位数p3,十位数p2,个位数p1,
这样我们的筛选条件就可以写出来:n == p3 * p3 * p3 + p2 * p2 * p2 + p1 * p1 * p1。

如何拆分一个三位数n呢?

首先求n的个位数:n % 10
然后要将三位数变成两位数:n = n / 10;
对于新的两位数n,又求它的个位数:n % 10
然后要将两位数变成一位数:n = n / 10;

也就是说我们可以交替使用求余和整除运算将一个三位数拆分,得到它的个位数、十位数和百位数。当然这个分解方法可以推广到任何多位数的拆分。

在这里插入图片描述

package net.hw.lesson07;

/**
 * 功能:打印水仙花数
 * 作者:华卫
 * 日期:2020年4月14日
 */
public class Example703 {
    public static void main(String[] args) {
        // 声明部分
        int p3; // 百位数
        int p2; // 十位数
        int p1; // 个位数

        // 处理部分
        for (int n = 100; n <= 999; n++) { // 循环头:初始条件、循环条件、更新条件
            p1 = n % 10; // 得到个位数
            p2 = n / 10 % 10; // 得到十位数
            p3 = n / 100; // 得到百位数
            if (n == p1 * p1 * p1 + p2 * p2 * p2 + p3 * p3 * p3) {// if负责筛选工作
                System.out.println(n + " = " + p3 + "^3 + " + p2 + "^3 + " + p1 + "^3");
            }
        }
    }
}

运行程序,查看结果:
在这里插入图片描述

课堂练习2:输出指定范围内的闰年

输出2000~2500之间的所有闰年,要求每行输出5个年份,并且统计总共有多少个闰年。

提示:用for循环控制范围,在循环里嵌套一个选择结构负责筛选和统计闰年个数,选择结构里还要嵌套选择结构来负责每输出5个年份就换行。

在这里插入图片描述

二、嵌套循环

1、嵌套循环的定义

嵌套循环是指在一个循环语句的循环体中再定义一个循环语句的语法结构。注意必须是包含关系,不能出现交叉。while、do…while、for循环语句都可以进行循环嵌套,并且它们之间也可以互相嵌套。在实际开发时,最常用的是for循环嵌套。

2、双重for循环

(1)语法格式

for(初始化表达式; 循环条件; 操作表达式) {
    ...
    for(初始化表达式; 循环条件; 操作表达式) {
        执行语句
        ...
    }
    ...
}

(2)执行规则

在双层for循环嵌套中,外层循环每执行一轮,都要执行完内层循环中的整个for循环,然后执行外层循环第二轮,接着再执行完内层循环中的整个for循环,以此类推,直至外层循环的循环条件不成立,才会跳出整个嵌套for循环。如果外循环有 m m 次,内循环有 n n 次,那么内循环里的操作会执行 m × n m\times n 次。
在这里插入图片描述

3、案例演示

任务1、打印字符图形

(1)打印矩形

在这里插入图片描述
内循环的输出语句如果换行,那么结果是是输出一列星号,如下图所示:
在这里插入图片描述
如果内循环的输出语句不换行,那么结果是输出一行星号,如下图所示:
在这里插入图片描述
因此,内循环完了之后,必须要换行才行,代码如下:

package net.hw.lesson07;

import java.util.Scanner;

/**
 * 功能:打印矩形字符图形
 * 作者:华卫
 * 日期:2020年4月19日
 */
public class Example704 {
    public static void main(String[] args) {
        // 声明部分
        int row, col;
        Scanner sc = new Scanner(System.in);

        // 输入部分
        System.out.print("row = ");
        row = sc.nextInt();
        System.out.print("col = ");
        col = sc.nextInt();

        // 处理与输出部分
        for (int i = 1; i <= row; i++) {
            // 负责打印星号
            for (int j = 1; j <= col; j++) {
                System.out.print("*");
            }
            System.out.println(); // 换行
        }
    }
}

运行程序,查看结果:
在这里插入图片描述

(2)打印平行四边形

平行四边形有两种情况,一个向右倾斜的平行四边形,如下图所示:
在这里插入图片描述
我们来研究一下每行前导空格数与当前行数的关系,这对我们编写程序至关重要。
假设总行数 row = 10,我们可以发现一个规律:第i行的前导空格数等于总行数减去当前行数,即 spaces = row - i = 10 - i。

i = 1    spaces = 9      1 + 9 = 10
i = 2    spaces = 8      2 + 8 = 10
……
i = i    spaces = 10 - i  i + (10 - i) = 10
……
i = 8    spaces = 2       8 + 2 = 10
i = 9    spaces = 1       9 + 1 = 10
i = 10   spaces = 0       10 + 0 = 10

在这里插入图片描述

package net.hw.lesson07;

import java.util.Scanner;

/**
 * 功能:打印平行四边形
 * 作者:华卫
 * 日期:2020年4月19日
 */
public class Example705 {
    public static void main(String[] args) {
        // 声明部分
        int row, col;
        Scanner sc = new Scanner(System.in);

        // 输入部分
        System.out.print("row = ");
        row = sc.nextInt();
        System.out.print("col = ");
        col = sc.nextInt();

        // 打印向右倾斜的平行四边形
        for (int i = 1; i <= row; i++) {
            // 负责打印前导空格
            for (int j = 1; j <= row - i; j++) {
                System.out.print(" ");
            }
            // 负责打印星号
            for (int j = 1; j <= col; j++) {
                System.out.print("*");
            }
            System.out.println(); // 换行
        }

        System.out.println();

        // 打印向左倾斜的平行四边形
        for (int i = 1; i <= row; i++) {
            // 负责打印前导空格
            for (int j = 1; j <= i - 1; j++) {
                System.out.print(" ");
            }
            // 负责打印星号
            for (int j = 1; j <= col; j++) {
                System.out.print("*");
            }
            System.out.println(); // 换行
        }
    }
}

运行程序,查看结果:
在这里插入图片描述

课堂练习:打印直角三角形

在这里插入图片描述

挑战练习:打印实心钻石

实心钻石,其实由一个正立的等腰三角形和一个倒立的等腰三角形组合而成,也就是一个菱形。
在这里插入图片描述
当然还可以更有挑战性,打印输出空心钻石。只有边上有星号,里面全部被镂空。
在这里插入图片描述

任务2、打印乘法九九表

作为启蒙教材,我们都背过九九乘法表:一一得一、一二得二、……、九九八十一。而古代是从"九九八十一"开始,因此称"九九表"。九九表的使用,对于完成乘法是大有帮助的。齐桓公纳贤的故事说明,到公元前7世纪时,九九歌诀已不稀罕。也许有人认为这种成绩不值一提。但在古代埃及作乘法却要用倍乘的方式呢。举个例子说明:比如计算23×13,就需要从23开始,加倍得到23×2,23×4,23×8,然后注意到13=1+4+8,于是23+23×4+23×8加起来的结果就是23×13。通过对比,不难看出使用九九表的优越性了。
在这里插入图片描述

package net.hw.lesson07;

/**
 * 功能:打印乘法九九表
 * 作者:华卫
 * 日期:2020年4月19日
 */
public class Example706 {
    public static void main(String[] args) {
        // 外层循环,变量i控制输出1~9行
        for (int i = 1; i <= 9; i++) {
            // 内层循环,变量j控制输出1~i列
            for (int j = 1; j <= i; j++) {
                // 输出乘法算式
                System.out.print(i + " × " + j + " = " + (i * j) + "\t");
            }
            // 控制外层循环进行换行
            System.out.println();
        }
    }
}

运行程序,查看结果:
在这里插入图片描述
注意内循环的循环条件:
在这里插入图片描述
将内循环条件改成j <= 9,结果如下:
在这里插入图片描述

任务3、百钱买百鸡问题。

我国古代数学家张丘建在《算经》一书中曾提出过著名的“百钱买百鸡”问题,该问题叙述如下:鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一;百钱买百鸡,则翁、母、雏各几何?

翻译过来,意思是公鸡一个五块钱,母鸡一个三块钱,小鸡三个一块钱,现在要用一百块钱买一百只鸡,问公鸡、母鸡、小鸡各多少只?

需要定义三个整型变量cock, hen, chick,分别代表公鸡、母鸡和小鸡的购买数量。
有两方面的条件:关于钱的条件与关于鸡的条件

  • 钱的条件: c o c k × 5 + h e n × 3 + c h i c k 3 = 100 cock \times5+hen\times3+\displaystyle\frac{chick}{3}=100
  • 鸡的条件: c o c k + h e n + c h i c k = 100 cock+hen+chick=100

(1)采用三重循环

cock:0 ~ 20
hen:0 ~ 34
chick:0 ~ 100

在这里插入图片描述

package net.hw.lesson07;

/**
 * 功能:求解百钱买百鸡问题
 *      采用三重循环
 * 作者:华卫
 * 日期:2020年4月19日
 */
public class Example707 {
    public static void main(String[] args) {
        //声明部分
        int cock, hen, chick, count = 0;

        //处理部分
        for (cock = 0; cock <= 20; cock++) {
            for (hen = 0; hen <= 34; hen++) {
                for (chick = 0; chick <= 100; chick++) {
                    if (cock + hen + chick == 100 && cock * 5 + hen * 3 + chick / 3.0 == 100) {
                        count++;
                        System.out.println("cock = " + cock + " hen = " + hen + " chick = " + chick);
                    }
                }
            }
        }
        System.out.print("百钱买百鸡总共有" + count + "种购买方案。");
    }
}

运行程序,查看结果:
在这里插入图片描述
验证四种购买方案是否正确:

  • 方案1: 0 + 25 + 75 = 100 , 0 × 5 + 25 × 3 + 75 3 = 75 + 25 = 100 0+25+75=100, 0\times5+25\times3+\displaystyle\frac{75}{3}=75+25=100
  • 方案2: 4 + 18 + 78 = 100 , 4 × 5 + 18 × 3 + 78 3 = 20 + 54 + 26 = 100 4+18+78=100, 4\times5+18\times3+\displaystyle\frac{78}{3}=20+54+26=100
  • 方案3: 8 + 11 + 81 = 100 , 8 × 5 + 11 × 3 + 81 3 = 40 + 33 + 27 = 100 8+11+81=100, 8\times5+11\times3+\displaystyle\frac{81}{3}=40+33+27=100
  • 方案4: 12 + 4 + 84 = 100 , 12 × 5 + 4 × 3 + 84 3 = 60 + 12 + 28 = 100 12+4+84=100, 12\times5+4\times3+\displaystyle\frac{84}{3}=60+12+28=100

注意一个细节问题:chick / 3.0,而不是 chick / 3。在数学上两者没有区别,但是在计算机里就有区别了,因为计算机里不同类型数据存储与运算不一样。
在这里插入图片描述
但是也可以这样来处理,再添加一个条件:chick % 3 == 0
在这里插入图片描述
采用三重循环固然可以解决百钱买百鸡问题,但是最内层循环里的基本操作次数 = 21 × 35 × 101 = 74 , 235 21\times 35 \times 101=74,235 ,时间复杂度就比较高了,我们应该优化解决问题的算法。下面,我们降维处理,采用双重循环来解决此问题。

(2)采用双重循环

在这里插入图片描述

package net.hw.lesson07;

/**
 * 功能:求解百钱买百鸡问题
 *      采用双重循环
 * 作者:华卫
 * 日期:2020年4月19日
 */
public class Example708 {
    public static void main(String[] args) {
        //声明部分
        int cock, hen, chick, count = 0;

        //处理部分
        for (cock = 0; cock <= 20; cock++) {
            for (hen = 0; hen <= 34; hen++) {
                chick = 100 - cock - hen;
                if (cock * 5 + hen * 3 + chick / 3.0 == 100) {
                    count++;
                    System.out.println("cock = " + cock + " hen = " + hen + " chick = " + chick);
                }
            }
        }
        System.out.print("百钱买百鸡总共有" + count + "种购买方案。");
    }
}

运行程序,查看结果:
在这里插入图片描述
采用双重循环解决百钱买百鸡问题,内层循环里的基本操作次数 = 21 × 35 = 735 21\times 35=735 ,跟三重循环算法相比,时间复杂度就大大降低了,因此双重循环算法是一个更好的算法。

三、循环跳转语句

跳转语句用于实现循环语句执行过程中程序流程的跳转。在Java中的跳转语句有break语句和continue语句。

(一)break语句

1、使用场景

在switch条件语句和循环结构语句中都可以使用break语句。

2、作用效果

当它出现在switch条件语句中时,作用是终止某个case并跳出switch结构;当它出现在循环结构语句中时,作用是跳出当前循环结构语句,执行后面的代码。

3、案例演示

任务1、在选择结构中使用break

参看Java讲课笔记06里的案例:

  • Example608(开关式评定成绩等级)
package net.hw.lesson06;

import java.util.Scanner;

/**
 * 功能:开关式评定成绩等级
 * 作者:华卫
 * 日期:2020年4月12日
 */
public class Example608 {
    public static void main(String[] args) {
        // 声明部分
        double score;
        String level;
        Scanner sc = new Scanner(System.in);

        // 输入部分
        System.out.print("score = ");
        score = sc.nextDouble();

        // 处理部分
        level = "";
        if (score > 100 || score < 0) {
            level = "超出范围";
        } else {
            switch ((int) score / 10) {
                case 10:
                case 9:
                    level = "优秀";
                    break;
                case 8:
                    level = "良好";
                    break;
                case 7:
                    level = "中等";
                    break;
                case 6:
                    level = "及格";
                    break;
                default:
                    level = "不及格";
            }
        }

        // 输出部分
        System.out.println("level = " + level);
    }
}

如果去掉case里的break语句,就会导致逻辑错误,如下图所示:
在这里插入图片描述

  • Example617(根据获奖名次给予奖励)
package net.hw.lesson06;

import java.util.Scanner;

/**
 * 功能:根据获奖名次给予奖励
 * 作者:华卫
 * 日期:2020年4月12日
 */

public class Example617 {
    public static void main(String[] args) {
        // 声明部分
        int rank; // 获奖名次
        String reward; // 给予的奖励
        Scanner sc = new Scanner(System.in); // 扫描器
        // 输入部分
        System.out.print("输入获奖名次:");
        rank = sc.nextInt(); // 接收一个整数
        // 处理部分
        switch (rank) {
            case 1:
                reward = "参加麻省理工大学组织的1个月夏令营!";
                break;
            case 2:
                reward = "奖励惠普笔记本电脑一部!";
                break;
            case 3:
                reward = "奖励移动硬盘一个!";
                break;
            default:
                reward = "没有任何奖励!";
                break;
        }
        //输出部分
        System.out.println(reward);
    }
}

如果将break语句去掉,那么都会犯逻辑错误(隐藏的敌人 - hidden enemy)。

程序出错,有三种情况:语法错误(grammar error)、运行错误(runtime error)、逻辑错误(logical error)。

任务2、判断一个整数是否是素数

  • 概念:什么叫素数(Prime Number)?一个整数除了1和它本身之外没有其它因子,这个整数就叫素数,否则就叫合数,但是有一个特殊情况,1既不是素数,也不是合数。比如:2、3、5、7都是素数,它们除了1和本身都没有其它因子,但是6就是合数,除了1和6之外,还有2和3都是其因子(6 = 2 * 3)。

  • 方法:判断一个整数是不是素数的方法 - 比如我们考虑一个整数 n n 2 n(n\ge2) ,就要看从 2 2 n 1 n-1 的每个数能否整除 n n ,如果能整除,那么 n n 就不是素数;如果从头到尾都不能整除,那么 n n 就是素数。

在这里插入图片描述
编程思路:我们采用一个布尔型的标志变量isPrimeNumber来表示待判断的整数n是不是素数,如果isPrimeNumber = true表明是素数,如果isPrimeNumber = false表明不是素数。先假定待判断的整数n是素数,设置isPrimeNumber = true,一旦在循环里找到能整除n的真因子,说明n不是素数,立马设置isPrimeNumber = false,并跳出循环结果。循环之后就根据isPrimeNumber的真假来输出n是素数还是合数。(这个编程思路,有点类似于法院断案,先假定你是好人,只有找到犯罪证据,才能断定你是罪犯。)

package net.hw.lesson07;

import java.util.Scanner;

/**
 * 功能:素数判断
 * 作者:华卫
 * 日期:2020年4月19日
 */
public class Example709 {
    public static void main(String[] args) {
        int n;
        boolean isPrimeNumber;
        Scanner sc = new Scanner(System.in);

        System.out.print("n = ");
        n = sc.nextInt();

        isPrimeNumber = true;
        for (int i = 2; i <= n - 1; i++) {
            if (n % i == 0) {
                isPrimeNumber = false;
                break;
            }
        }

        if (n == 1) {
            System.out.println(n + "既不是素数,也不是合数。");
        } else if (isPrimeNumber) {
            System.out.println(n + "是素数。");
        } else {
            System.out.println(n + "是合数。");
        }
    }
}

运行程序,查看结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
不用引入布尔型变量isPrimeNumber,可以换一种方式来处理:

package net.hw.lesson07;

import java.util.Scanner;

public class Example709_ {
    public static void main(String[] args) {
        int i, n;
        Scanner sc = new Scanner(System.in);

        System.out.print("n = ");
        n = sc.nextInt();

        // 考虑n除了1和本身之外有无其它因子
        for (i = 2; i <= n - 1; i++) {
            if (n % i == 0) {
                break;
            }
        }

        if (n == 1) { // 特殊情况特殊处理
            System.out.println(n + "既不是素数,也不是合数。");
        } else if (i <= n - 1) { // 循环中途跳出
            System.out.println(n + "是合数。");
        } else { // 循环自然结束
            System.out.println(n + "是素数。");
        }
    }
}

运行程序,查看结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其实该程序可以优化的,为了说明方便起见,我们考虑 n = 100 n = 100 的情况:

2 * 50 = 100
4 * 25 = 100
5 * 20 = 100
10 * 10 = 100
20 * 5 = 100
25 * 4 = 100
50 * 2 = 100

因子乘积是以100的平方根10的乘积(10 * 10)为中心对称的。于是循环范围就可以缩小,本来是从 2 2 99 99 ,现在可以缩小到从 2 2 100 \sqrt{100}

根据刚才的分析,代码可以优化如下:

for (i = 2; i <= Math.sqrt(n); i++) {            
    if (n % i == 0) {                            
        break;                                   
    }                                            
}                                                
                                                 
if (n == 1) { // 特殊情况特殊处理                        
    System.out.println(n + "既不是素数,也不是合数。");      
} else if (i <= Math.sqrt(n)) { // 循环中途跳出        
    System.out.println(n + "是合数。");              
} else { // 循环自然结束                               
    System.out.println(n + "是素数。");              
}                                                                         

循环次数明显减少,因此这是一个更好的算法。大家按照这个方式修改代码,然后进行测试。

当然案例Example709也可以缩小循环范围,如下图所示:
在这里插入图片描述
思考题: 如果判断一个整数是合数,编程将其分解成素数乘积形式。比如 45 = 3 × 3 × 5 45=3\times3\times5
在这里插入图片描述
在这里插入图片描述
再运行程序,可以判断153319是不是素数,结果的确是素数。
在这里插入图片描述

关于break的用法,大家还可以去参看Java讲课笔记06的案例Example619(多级购物菜单系统)。

4、如何利用break语句跳出外循环

对于双重循环,如果内循环里有break语句,那么其作用仅仅是跳出内循环。如果想跳出外循环,那么需要用到标签。下面以打印九九表的案例进行说明。
在这里插入图片描述

package net.hw.lesson07;

/**
 * 功能:演示break跳出外循环
 * 作者:华卫
 * 日期:2020年4月19日
 */
public class Example710 {
    public static void main(String[] args) {
        // 外层循环,变量i控制输出1~9行
        outer: for (int i = 1; i <= 9; i++) {
            // 内层循环,变量j控制输出1~i列
            for (int j = 1; j <= i; j++) {
            	System.out.print(i + " × " + j + " = " + (i * j) + "\t");
                if (j >= 3) {
                    break outer;
                }                
            }
            System.out.println();
        }
    }
}

运行程序,查看结果:
在这里插入图片描述
可以看到,当i = 3 且 j = 3时,跳出外循环,于是只打印输出九九表的前三行。

如果不是break outer,而是一个break,结果又会如何呢?
在这里插入图片描述
此时,运行程序,结果如下:
在这里插入图片描述
大家可以看到,外循环依然执行了9次,也就是输出了9行,但是每行的列数不能超过3,也就是说每行最多只有三个乘法算式。

(二)continue语句

1、使用场景

continue语句用在循环语句中。

2、作用效果

它的作用是终止本次循环,执行下一次循环。

3、案例演示:计算1 + 3 + 5 + …… + 99的值

之前我们已经使用for循环和while循环来完成过这个计算任务,更新条件采用的是 i = i + 2,现在我们换种方式来处理,更新条件依然是 i ++,在循环里嵌套一个选择结构,通过使用continue语句过滤掉偶数,然后累加的就是全部奇数。
在这里插入图片描述

package net.hw.lesson07;

/**
 * 功能:计算 1 + 3 + 5 + …… + 99的值
 * 作者:华卫
 * 日期:2020年4月19日
 */
public class Example711 {
    public static void main(String[] args) {
        int sum = 0;

        for (int i = 1; i <= 99; i++) {
            if (i % 2 == 0) {
                continue;
            }
            sum += i;
        }

        System.out.println("1 + 3 + 5 + …… + 99 = " + sum);
    }
}

运行程序,查看结果:
在这里插入图片描述
之前,我们曾经采用过三种方法来计算1 + 3 + 5 + …… + 99的值:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
俗话说:条条道路通罗马(All roads lead to Rome.)
一题多解,可以加深大家对循环结构的理解与掌握。

四、课后作业

1、计算阶乘和 1! + 2! + 3! + … + 10! 的值

提示:外循环负责累加求和,内循环负责累乘计算阶乘。
在这里插入图片描述

2、输出200~400之间的全部素数,每行输出5个

在这里插入图片描述

3、用米粒填充国际象棋盘

国际象棋盘中,第1 格放1 粒米,第2 格放2 粒米,第3格放4 粒米,第4 格放8 粒米,第5 格放16粒米,……问:64个格子总共可以放多少粒米?
在这里插入图片描述
程序运行结果:

1: 1
2: 2
3: 4
4: 8
5: 16
6: 32
7: 64
8: 128
9: 256
10: 512
11: 1024
12: 2048
13: 4096
14: 8192
15: 16384
16: 32768
17: 65536
18: 131072
19: 262144
20: 524288
21: 1048576
22: 2097152
23: 4194304
24: 8388608
25: 16777216
26: 33554432
27: 67108864
28: 134217728
29: 268435456
30: 536870912
31: 1073741824
32: 2147483648
33: 4294967296
34: 8589934592
35: 17179869184
36: 34359738368
37: 68719476736
38: 137438953472
39: 274877906944
40: 549755813888
41: 1099511627776
42: 2199023255552
43: 4398046511104
44: 8796093022208
45: 17592186044416
46: 35184372088832
47: 70368744177664
48: 140737488355328
49: 281474976710656
50: 562949953421312
51: 1125899906842624
52: 2251799813685248
53: 4503599627370496
54: 9007199254740992
55: 18014398509481984
56: 36028797018963968
57: 72057594037927936
58: 144115188075855872
59: 288230376151711744
60: 576460752303423488
61: 1152921504606846976
62: 2305843009213693952
63: 4611686018427387904
64: 9223372036854775808
sum = 18446744073709551615

提示:sum最后的值显然已经超过长整型的最大值(Long.MAX_VALUE = 9223372036854775807),需要使用大整数类BigInteger,其具体用法,大家网上搜索。

五、补充案例

1、猜数游戏(可玩多次的情况)

package net.hw.lesson07;

import java.util.Scanner;

/**
 * 功能:猜数游戏,玩多次的情况
 * 作者:华卫
 * 日期:2020年4月17日
 */
public class NumberGuessingGame {
    public static void main(String[] args) {
        int num, target;
        String choice;
        Scanner sc = new Scanner(System.in);
        
        choice = "y";
        while (choice.equalsIgnoreCase("y")) {
            target = (int) (Math.random() * 100) + 1;
            System.out.print("num = ");
            num = sc.nextInt();
            while (num != target) {
                if (num > target) {
                    System.out.println("Too high!");
                } else {
                    System.out.println("Too low!");
                }
                System.out.print("num = ");
                num = sc.nextInt();
            }
            System.out.println("That's it!");
            System.out.print("Would you like to play again? [y/n]");
            choice = sc.next();
        }
        System.out.println("Thanks for your playing.");
    }
}

运行程序,查看结果:
在这里插入图片描述
在这里插入图片描述

2、求20个学生的平均成绩

输入20个学生成绩,求平均分。
在这里插入图片描述

package net.hw.lesson07;

import java.util.Scanner;

/**
 * 功能:输入20个学生成绩,求平均分
 * 作者:华卫
 * 日期:2020年4月17日
 */
public class Example712 {
    public static void main(String[] args) {
        // 声明部分
        int i; // 循环变量
        int score; // 学生成绩
        int sum = 0; // 总分
        int average; // 平均分
        final int N = 20; // 符号常量:学生人数
        Scanner sc = new Scanner(System.in); // 扫描器

        // 处理部分
        i = 1; // 初始条件
        while (i <= N) { // 循环条件
            System.out.print("输入第" + i + "个学生成绩:");
            score = sc.nextInt();
            sum = sum + score; // 累加语句
            i++; // 更新条件
        }
        average = sum / N;

        // 输出部分
        System.out.println(N + "个学生的平均分:" + average);
    }
}

运行程序,查看结果:
在这里插入图片描述
问题:假如输入的成绩不合要求,该怎样处理才合理?
为了保证输入的成绩是合法的,那么采用循环来保证。
首先给出改进版1:Example713
在这里插入图片描述

package net.hw.lesson07;

import java.util.Scanner;

/**
 * 功能:输入20个学生成绩,求平均分
 *       要保证每次输入的成绩合法
 * 作者:华卫
 * 日期:2020年4月17日
 */
public class Example713 {
    public static void main(String[] args) {
        // 声明部分
        int i; // 循环变量
        int score; // 学生成绩
        int sum = 0; // 总分
        int average; // 平均分
        final int N = 20; // 符号常量:学生人数
        Scanner sc = new Scanner(System.in); // 扫描器

        // 处理部分
        i = 1; // 初始条件
        while (i <= N) { // 循环条件
            System.out.print("输入第" + i + "个学生成绩:");
            score = sc.nextInt();
            // 保证输入的成绩在0~100之间
            while (score < 0 || score > 100) {
                System.out.println("输入的成绩超出范围!");
                System.out.print("输入第" + i + "个学生成绩:");
                score = sc.nextInt();
            }
            sum = sum + score; // 累加语句
            i++; // 更新条件
        }
        average = sum / N;

        // 输出部分
        System.out.println(N + "个学生的平均分:" + average);
    }
}

运行程序,查看结果:
在这里插入图片描述
如果只给三次犯错机会,那么我们又该如何处理呢?

为此给出改进版2:Example714
在这里插入图片描述

package net.hw.lesson07;

import java.util.Scanner;

/**
 * 功能:输入20个学生成绩,求平均分
 * 要保证每次输入的成绩合法
 * 总共只给三次犯错机会
 * 作者:华卫
 * 日期:2020年4月17日
 */
public class Example714 {
    public static void main(String[] args) {
        // 声明部分
        int i; // 循环变量
        int score; //学生成绩
        int sum = 0; //总分
        int average; //平均分
        final int N = 20; //符号常量:学生人数
        int errorCount = 0; //犯错次数
        Scanner sc = new Scanner(System.in);

        //处理部分
        i = 1; // 初始条件
        while (i <= N) { //循环条件
            System.out.print("输入第" + i + "个学生成绩:");
            score = sc.nextInt();
            // 保证输入的成绩在0~100之间
            while (score < 0 || score > 100) {
                errorCount++;
                if (errorCount >= 3) {
                    System.err.println("已输错3次,还是去歇歇吧!");
                    System.exit(-1); // 非正常退出
                }
                System.out.println("输入的成绩超出范围!");
                System.out.print("输入第" + i + "个学生成绩:");
                score = sc.nextInt();
            }
            errorCount = 0;
            sum = sum + score; //累加语句
            i++; //更新条件
        }
        average = sum / N;

        // 输出部分
        System.out.println(N + "个学生的平均分:" + average);
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/howard2005/article/details/105504753