CS1602计算导论
Lab 11Part 5 健壮性与真实世界AI Level 1

复杂度实测与编写测试

七道题。前三题动手测复杂度,中间三题练异常处理,最后一题是这门课第一次要求你写 pytest 测试——而且是给别人的代码写。

截止:小作业 4 · 12 月 8 日 周二 23:59
本页目录

本次目标

  • 亲手测出不同复杂度的实际差别
  • 用记忆化把一个指数算法降到线性
  • 正确使用 try / except / raise,分清异常和断言
  • 写出第一套 pytest 测试

环境准备

python3 -m pip install pytest

题目

11-1 复杂度实测

写一个脚本,对下面每个操作测量在 n = 1000, 5000, 10000, 20000 时的耗时:

  1. list.append(x) 执行 n 次
  2. list.insert(0, x) 执行 n 次
  3. x in list 在长度 n 的列表里查 100 次
  4. x in set 在大小 n 的集合里查 100 次

把结果整理成一张表,在提交说明里回答:

  • n 翻倍时,每个操作的耗时大约翻几倍?
  • 由此推断它们各自的复杂度是什么

11-2 字符串拼接

用同样的方法对比这两种写法在 n = 5000, 10000, 20000 时的耗时:

# 方法一
s = ""
for w in words:
    s += w

# 方法二
s = "".join(words)

在提交说明里给出耗时表,并解释为什么方法一慢。

11-3 给递归加记忆化

拿你在 Lab 7 写的 count_ways(amount, coins)(硬币组合计数),做三件事:

  1. 加一个计数器,测出 count_ways(100, [1, 2, 5, 10]) 需要多少次函数调用
  2. 用字典给它加上记忆化
  3. 再测一次调用次数,算出加速比

在提交说明里给出两个数字和比值。

11-4 安全的转换

实现三个函数,转换失败时返回默认值而不是崩溃:

def safe_int(text: str, default: int = 0) -> int: ...
def safe_float(text: str, default: float = 0.0) -> float: ...
def safe_div(a: float, b: float, default: float = 0.0) -> float: ...

要求:只捕获具体的异常类型,不许写裸的 except:。

在提交说明里写出每个函数你捕获了哪些异常,以及为什么是这些。

11-5 找出并修正错误的异常处理

下面这段代码有三个问题。找出来,改对,并在提交说明里逐条说明。

def load_config(filename):
    try:
        f = open(filename)
        data = f.read()
        config = {}
        for line in data.split("\n"):
            key, value = line.split("=")
            config[key] = int(value)
        return config
    except:
        return {}

11-6 自定义异常

给 Lab 9 的 Temperature 类改造:把”低于绝对零度”从”打印一句话返回 False”改成抛出自定义异常。

class TemperatureError(Exception):
    """温度不合法。"""
    ...

在提交说明里回答:什么情况下抛异常比返回 False 更好?什么情况下相反?

11-7 给别人的代码写测试

下面是一个”已经写好”的模块。你的任务不是改它,是给它写一套 pytest 测试。

# textstats.py
def word_count(text: str) -> int:
    """统计单词数。"""
    return len(text.split())

def longest_word(text: str) -> str:
    """返回最长的单词。长度相同时返回先出现的。"""
    words = text.split()
    return max(words, key=len)

def average_word_length(text: str) -> float:
    """返回平均单词长度。"""
    words = text.split()
    return sum(len(w) for w in words) / len(words)

写一个 test_textstats.py,要求:

  • 每个函数至少 4 个测试
  • 必须覆盖:正常情况、空字符串、单个单词、多个空格
  • 至少用一次 pytest.raises

然后运行 python3 -m pytest -v,把输出贴到提交说明里。

最后回答:这三个函数里,有几个在空字符串上会出问题?分别是什么问题?

提交前自查

  • 11-1、11-2 有完整的耗时表和复杂度推断
  • 11-3 的记忆化键包含了所有变化的参数
  • 11-4 没有裸的 except:
  • 11-5 找出了三个问题
  • 11-6 回答了”何时抛异常、何时返回值”
  • 11-7 每个函数至少 4 个测试,用过 pytest.raises
  • 11-7 回答了空字符串的问题
  • AI 使用声明写了