循环结构(五)
综合循环Ⅰ:状态迭代与筛选

从最高分、Fibonacci到区间素数,训练循环程序的模型识别能力

本课不增加新语法,而是训练模型识别

看到一个循环问题,先判断它属于哪一种“重复模式”。

求最值

保留“到目前为止最好的结果”。

状态迭代

新状态由旧状态产生。

批量筛选

对一批候选对象逐个判断。

第2课时学习目标

知识目标

  • 理解求最值的循环结构
  • 理解 Fibonacci 数列中的状态更新
  • 理解“外层枚举 + 内层判断”的素数筛选结构

能力目标

  • 能从题目中识别循环模型
  • 能用表格跟踪关键变量变化
  • 能把已学的素数判断组合进区间输出问题
教材例4-8
求最高分:
记录“到目前为止”的最大值
最值问题的关键,是让一个变量保存当前最优结果。
max状态保持比较更新

教材例4-8 题面描述

📋 题目要求

输入一批学生的成绩,找出最高分。
(注:当输入负数时标志数据输入结束)

从脉搏记录看“当前最大值”

7268757169
读入当前值
→
与 max 比较
→
更大则更新
→
继续下一条

求一批成绩中的最高分

scanf("%d", &mark);
max = mark;          /* 假设第一个成绩是最高分 */

while (mark >= 0) {
    if (mark > max)
        max = mark;
    scanf("%d", &mark);
}
求最值的核心:先让 max 有一个合理初值,再不断用新数据挑战它。

跟踪 max:它什么时候变化?

输入成绩 比较前 max 是否更新 比较后 max
82 - 初始化 82
76 82 否 82
91 82 是 91
88 91 否 91
-1 91 结束标志 91

🤖 AI 协作互动:极值初始化陷阱

在循环求一组数据的最大值 max 时,如果我把 max 的初值设为 0,当输入的全部都是负数(如 -5, -12, -3)时会发生什么?请提示我如何正确初始化 max。

💡 课堂互动:向 AI 提问“初值设为 INT_MIN 或第一个输入数据”的严密逻辑。

求最值常见错误:初值随便给

可能出错

max = 0;
while (...) {
    ...
}

如果数据范围不是0以上,就可能出错。

更稳妥

scanf("%d", &mark);
max = mark;

用第一个有效数据初始化最大值。

初值不是形式问题,而是算法正确性的起点。

教材例4-9
Fibonacci数列:
新状态由旧状态产生
循环中最重要的变量,常常不是输入,而是“上一轮留下来的状态”。
x1x2状态迭代

教材例4-9 题面描述

📋 题目要求

输入正整数 \(n\) (\(1 \le n \le 46\)),输出斐波那契(Fibonacci)数列的前 \(n\) 项:\(1, 1, 2, 3, 5, 8, 13, \ldots\),每行输出 5 个。
(注:Fibonacci 数列满足任一项数字是前两项的和,最开始的两项均定义为 1)

Fibonacci数列规则

1, 1, 2, 3, 5, 8, 13, ...

前两项

x1 = 1, x2 = 1

新项

x = x1 + x2

状态移动

x1 = x2;
x2 = x;

状态如何向前移动?

轮次 x1 x2 x=x1+x2 更新后
开始 1 1 2 x1=1, x2=2
下一轮 1 2 3 x1=2, x2=3
再下一轮 2 3 5 x1=3, x2=5
本题的循环体不是简单输出,而是不断更新“前两项”的状态。

输出Fibonacci数列前 n 项

x1 = 1;
x2 = 1;
printf("%10d%10d", x1, x2);
for (i = 3; i <= n; i++) {
    x = x1 + x2;
    printf("%10d", x);
    x1 = x2;
    x2 = x;
}

先输出前两项,再循环产生后面的 n-2 项。

每行输出5个:循环中的格式控制

printf("%10d", x);
if (i % 5 == 0)
    printf("\n");

计算任务

产生数列的下一项。

显示任务

每输出5项后换行,便于阅读。

循环体中可以同时包含“计算”和“输出控制”。

🤖 AI 协作互动:状态平移逻辑推演

在用循环输出 Fibonacci 数列时,更新状态的逻辑是 f1 = f2; f2 = f3;。如果我把赋值顺序颠倒写成 f2 = f3; f1 = f2; 会发生什么?请帮我推演变量覆盖过程。

💡 课堂互动:利用 AI 推演顺序列出变量临时覆盖,体会多状态同步平移的顺序要求。

Fibonacci状态滚动

Fibonacci状态滚动
教材例4-10
输出区间内所有素数:
枚举 + 判断
把“判断一个数是否为素数”组合进“遍历一个区间”。
外层枚举内层判断组合算法

教材例4-10 题面描述

📋 题目要求

输入 2 个正整数 \(m\) 和 \(n\) (\(1 \le m \le n \le 500\)),输出 \(m\) 到 \(n\) 之间的全部素数,每行输出 10 个。
(注:素数指的是只能被 1 和自身整除的正整数,1 不是素数,2 是素数)

从“一个素数”到“一批素数”

枚举 k=m...n
→
判断 k 是否为素数
→
若是则输出
→
控制每行数量

外层

候选数 k 从 m 到 n。

内层

对当前 k 尝试寻找因子。

复用上一课:判断 k 是否为素数

flag = 1;
limit = sqrt(k) + 1;
for (i = 2; i <= limit; i++) {
    if (k % i == 0) {
        flag = 0;
        break;
    }
}
if (flag == 1)
    printf("%d ", k);
这里的 break 表示:只要找到一个因子,就不需要继续检查。

输出 m 到 n 之间的全部素数

count = 0;
for (k = m; k <= n; k++) {
    flag = 1;
    limit = sqrt(k) + 1;
    for (i = 2; i <= limit; i++) {
        if (k % i == 0) {
            flag = 0;
            break;
        }
    }
    if (flag == 1) {
        printf("%5d", k);
        count++;
        if (count % 10 == 0) printf("\n");
    }
}

代码诊断:区间素数中最容易错在哪里?

边界

1 不是素数,2 是素数。

flag重置

每判断一个新的 k,都要重新设置 flag。

循环条件

i <= limit 与 i < limit 要保持逻辑一致。

组合程序最容易出错的地方,往往不是语法,而是“每一轮是否重新初始化状态”。

🤖 AI 协作互动:算法开销与优化评估

求 m 到 n 之间的所有素数时,内层判断素数的循环写成 k <= sqrt(i) 比 k < i 能节省多少计算量?请用 O(N*sqrt(N)) 与 O(N^2) 帮我做简单的算力对比。

💡 课堂互动:向 AI 提问开方优化的数学原理,理解为什么超过 sqrt(i) 的因子必然成对出现。

三类综合循环模型

模型 关键变量 典型例题
求最值 max 输入一批成绩找最高分
状态迭代 x1, x2, x Fibonacci前n项
枚举筛选 k, flag, count 输出m和n之间所有素数

先识别模型,再选择循环结构。