控制流综合
A 段七题练条件与循环,从这次起所有函数都要写类型提示;B 段用两种完全不同的算法算 π 并当场比出高下;C 段学在终端里看文件和改文件。
本页目录
本次目标
- 熟练组合条件与循环解决完整问题
- 判断该用
for还是while - 开始给所有函数写类型提示
- 为下周的个人项目 A(递归)做准备
A 段 · 必做
4-1 成绩分级
实现 grade(score: int) -> str:90 及以上 "A",80–89 "B",70–79 "C",60–69 "D",60 以下 "F"。
分数不在 0–100 之间时返回 "Invalid"。
grade(95) → "A"
grade(60) → "D"
grade(59) → "F"
grade(101) → "Invalid"
grade(-1) → "Invalid"
4-2 数位之和
实现 digit_sum(n: int) -> int,求一个非负整数各位数字之和。不许把它转成字符串。
digit_sum(0) → 0
digit_sum(12345) → 15
digit_sum(999) → 27
4-3 判断质数
实现 is_prime(n: int) -> bool。
is_prime(1) → False
is_prime(2) → True
is_prime(97) → True
is_prime(91) → False # 91 = 7 × 13,很多人会漏
4-4 考拉兹步数
实现 collatz_steps(n: int) -> int:偶数除以 2,奇数乘 3 加 1,返回到达 1 所需的步数。
collatz_steps(1) → 0
collatz_steps(6) → 8
collatz_steps(27) → 111
然后在提交说明里回答:1 到 1000 之间,哪个起点需要的步数最多?是多少步?
4-5 打印图形
实现 triangle(n: int) -> str,返回一个 n 行的星号三角形字符串,行之间用 \n 分隔,末尾不要换行。
triangle(4) 返回的字符串打印出来是:
*
**
***
****
4-6 猜数游戏
写一个程序(不是函数),随机生成 1–100 之间的一个整数,反复读入用户的猜测并提示”大了""小了”,猜中后输出用了几次。
import random
answer = random.randint(1, 100)
这道题是交互式的,用 print 输出,不用 return。在提交说明里写下你玩一局的完整过程。
4-7 项目预热:汉诺塔的步数
实现 hanoi_steps(n: int) -> int,返回把 n 个盘子从一根柱子移到另一根所需的最少步数。
hanoi_steps(1) → 1
hanoi_steps(3) → 7
hanoi_steps(10) → 1023
这道题用循环就能做。 下周学了递归,你会用完全不同的方式重做一遍—— 到时候回头看这两种写法的对比,是个人项目 A 的起点。
在提交说明里写下你发现的规律。
B 段 · 深入:两种方法算 π,比一比
这一段要你用两种完全不同的算法算同一个常数,然后比较它们。 这是这门课第一次让你用实验的方法评价算法——不是听谁说哪个好,是自己跑出来。
只需要循环和算术,不用列表。
B-1 莱布尼茨级数
三百多年前莱布尼茨发现:
π/4 = 1 − 1/3 + 1/5 − 1/7 + 1/9 − ⋯
实现 leibniz(n: int) -> float,取前 n 项算出 π 的近似值。
分别取 n = 10, 100, 1000, 10000,把结果和真值 math.pi 一起打出来,
并算出误差(abs(近似值 - math.pi))。
B-2 找出误差的规律
把 B-1 的误差列成一张表。盯着它看,你会发现一个很整齐的规律。
请回答:
- n 每乘以 10,误差大致变成原来的几分之一?
- 照这个规律,要让误差小于
1e-6(小数点后六位准),大概需要多少项? - 跑一次验证你的估计。跑之前先想想这会花多久。
B-3 蒙特卡洛:用随机数算 π
完全不同的思路:在边长为 1 的正方形里随机撒点,落在四分之一圆内的比例
应该接近 π/4。
import random
x, y = random.random(), random.random() # 各是 [0, 1) 里的随机数
# 点 (x, y) 落在四分之一圆内 ⟺ x² + y² ≤ 1
实现 monte_carlo(n: int) -> float,撒 n 个点估计 π。
同样取 n = 1000, 10000, 100000,记录结果和误差。
B-4 判决
把两种方法的误差放在一张表里比较,回答:
- 同样撒一百万个点 / 取一百万项,哪种更准?
- 蒙特卡洛的误差随 n 怎么变?(提示:和 B-2 的规律不一样,试试看误差乘以 √n)
- 如果要算到小数点后六位,两种方法各需要多少次运算?
- 那蒙特卡洛还有什么用? 想一想:什么样的问题只能用它?
C 段 · 基本功:在终端里编辑和查看文件
上周学了把输出重定向到文件。这周学怎么看这个文件,以及怎么在终端里直接改它。
看文件:四个命令
| 命令 | 作用 |
|---|---|
cat 文件 | 全部打印出来。文件小才用它 |
head 文件 | 只看前 10 行;head -n 3 看前 3 行 |
tail 文件 | 只看后 10 行;tail -f 会盯着文件,有新内容就显示 |
less 文件 | 翻页看。空格下翻,b 上翻,/词 搜索,q 退出 |
wc -l 文件 | 数有多少行 |
改文件:nano
nano 文件名 打开一个最简单的终端编辑器。屏幕最下面两行就是帮助:
| 按键 | 作用 |
|---|---|
Ctrl+O 然后回车 | 保存(O 是 write Out) |
Ctrl+X | 退出 |
Ctrl+W | 搜索(Where is) |
C-1 走一遍
把命令和输出截图放进提交说明:
- 把 B-1 的输出重定向到
pi.txt wc -l pi.txt看它有多少行head -n 3 pi.txt和tail -n 3 pi.txt各看一眼less pi.txt翻一遍,用/搜一个数字,然后按q退出nano pi.txt,在文件开头加一行标题,保存退出,再head -n 1 pi.txt确认
C-2 想一想
- 一个一百万行的文件,为什么不该用
cat看? tail -f在什么场景下有用?(提示:程序正在跑,日志在不断增长)
提交前自查
- 每个函数都有类型提示
- 4-1 的合法性检查放在最前面
- 4-2 处理了
n = 0 - 4-3 用
91测过 - 4-4、4-6、4-7 的问题都在提交说明里回答了