CS1602计算导论
Lab 4Part 1 计算基础与 Python 入门AI Level 0

控制流综合

A 段七题练条件与循环,从这次起所有函数都要写类型提示;B 段用两种完全不同的算法算 π 并当场比出高下;C 段学在终端里看文件和改文件。

截止:小作业 2 · 10 月 27 日 周二 23:59
本页目录

本次目标

  • 熟练组合条件与循环解决完整问题
  • 判断该用 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 的误差列成一张表。盯着它看,你会发现一个很整齐的规律。

请回答:

  1. n 每乘以 10,误差大致变成原来的几分之一?
  2. 照这个规律,要让误差小于 1e-6(小数点后六位准),大概需要多少项?
  3. 跑一次验证你的估计。跑之前先想想这会花多久。

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 判决

把两种方法的误差放在一张表里比较,回答:

  1. 同样撒一百万个点 / 取一百万项,哪种更准?
  2. 蒙特卡洛的误差随 n 怎么变?(提示:和 B-2 的规律不一样,试试看误差乘以 √n)
  3. 如果要算到小数点后六位,两种方法各需要多少次运算?
  4. 那蒙特卡洛还有什么用? 想一想:什么样的问题只能用它?

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 走一遍

把命令和输出截图放进提交说明:

  1. 把 B-1 的输出重定向到 pi.txt
  2. wc -l pi.txt 看它有多少行
  3. head -n 3 pi.txt 和 tail -n 3 pi.txt 各看一眼
  4. less pi.txt 翻一遍,用 / 搜一个数字,然后按 q 退出
  5. nano pi.txt,在文件开头加一行标题,保存退出,再 head -n 1 pi.txt 确认

C-2 想一想

  1. 一个一百万行的文件,为什么不该用 cat 看?
  2. tail -f 在什么场景下有用?(提示:程序正在跑,日志在不断增长)

提交前自查

  • 每个函数都有类型提示
  • 4-1 的合法性检查放在最前面
  • 4-2 处理了 n = 0
  • 4-3 用 91 测过
  • 4-4、4-6、4-7 的问题都在提交说明里回答了