外观
2026-计算概论C-py作业5
本次作业对应第5章《模块与库》,熟悉 turtle 的基本绘图函数与 random 随机数函数。
提交要求
完成下列 必做题 1、2,将 源代码和运行结果截图 提交到 教学网。截止时间为 2026 年 10 月 27 日(周二)中午 12:00(北京时间)。
本次在本地运行程序并提交到教学网,不在 OpenJudge 判题。另有 py作业4 于当天 13:00 在 OJ 截止,请区分两个时间。
必做题 1:正方形螺旋线
题目要求
利用 turtle 库从外往里绘制以坐标原点为对称点的正方形螺旋线,如下图所示。

- 最大正方形的边长由用户输入,范围为 100–400,且必须是 偶数;程序需要判断输入是否符合要求。
- 两个正方形之间的间距为 2。
- 垂直边为绿色,水平边为黄色。
- 可以使用
turtle.heading()获取当前方向角度。
知识点与 PPT
第5章第10–16页:turtle 的窗体、坐标、画笔、运动和方向;第4章第4–8页:自定义函数、参数和返回值;第3章:分支判断与循环。
老师给出的程序框架
下面的主函数和辅助函数按作业文档截图转写。需要补充 getMaxSide() 与 drawSquareHelix(length) 的具体实现。
python
import turtle as t
def initializeBrush(length):
t.mode('standard')
t.penup()
t.goto(-length / 2, -length / 2)
t.pendown()
t.seth(90)
def recoverBrush():
t.penup()
t.goto(0, 0)
t.pendown()
t.seth(0)
t.hideturtle()
def main():
length = getMaxSide()
t.setup(500, 500, 200, 200)
initializeBrush(length)
drawSquareHelix(length)
recoverBrush()
t.done()这里是待补全的框架;先定义缺少的两个函数,再在文件末尾调用 main()。
解题提示
1. 输入条件要同时满足。 先确认输入能解释为整数,再检查 100 <= length <= 400 和 length % 2 == 0。输入非法时提醒并重新输入,合法后由 getMaxSide() 返回数值。不要只把 int(input()) 写完就认为已经检查了范围和奇偶。
2. 理解起点与方向。 (-length / 2, -length / 2) 是外框的左下角,t.seth(90) 表示朝上。移动到起点前抬笔,避免从原点多画出一条斜线。
| heading 角度 | 方向 | 本题颜色 |
|---|---|---|
| 0 | 右 | yellow |
| 90 | 上 | green |
| 180 | 左 | yellow |
| 270 | 下 | green |
画每条边之前读取当前方向,设置 t.pencolor(...),再 t.fd(...),最后 t.right(90)。heading() 是查询方向,right(90) 才是转向。
3. 先在纸上追踪前几条边。 外侧从左下角朝上走,前两段长度均为 L;接下来的两段为 L-2,再接下来为 L-4。同一长度走两段,再缩短 2,保持从外向内的连续螺旋。可以先用较小的示意边长手算坐标,实际输入仍需满足 100–400。
4. 结束条件看剩余边长。 边长应保持正数;变成 0 后就停止,不要继续用负数让画笔往回走。recoverBrush() 只恢复画笔状态,不能代替绘图循环的结束条件。
易错点
- 画成独立正方形: 每次画四条相同长度的边会闭合;本题要画一条连续向内的螺旋。
- 每条边都减 2: 先检查前几段长度和起点轨迹,不能不追踪坐标就机械缩短。
- 颜色晚设一步: 先设置颜色再画当前边;转弯后查到的是下一条边的方向。
- 忘记调用 main 或 done: 定义函数不代表已经运行;
t.done()让绘图窗口保持打开。 - 运行环境不对: turtle 需要支持图形窗口的本地 Python 环境。程序文件不要命名为
turtle.py,否则可能遮蔽标准库。
必做题 2:随机发红包
题目要求
应用随机数函数模拟微信发红包。用户只输入一次 红包总金额(整数)和红包数量;计算机随机分配每个红包的金额,每个红包至少 1 元,并给尚未分配的每个红包留出至少 1 元。
在相同总金额、相同红包数量下模拟 10 次,每次的红包结果存入一个列表,并输出 10 次结果。作业给出两种方法的主程序:方法 1 已提供;需要根据提示补全方法 2,并观察两种方法的结果。
知识点与 PPT
第5章第27–28页:random、randint、uniform 和随机种子;第4章第4–8页:函数参数与返回值;第6章列表知识:[]、append()、sum()。
老师给出的主程序
文档主程序先读取红包数,再读取总金额;只读入一次,在循环里重复调用函数。每轮分别输出两种方法的结果,共 10 轮。
python
import random
number = int(input("红包总数:"))
totalMoney = int(input("红包总金额(整数):"))
for i in range(10):
each1 = redEnvelope1(totalMoney, number)
print(each1)
each2 = redEnvelope2(totalMoney, number)
print(each2)
print()调用前要先定义两个函数,并检查 number > 0、totalMoney >= number;否则无法保证每份至少 1 元。
方法 1:文档已提供的参考代码
python
def redEnvelope1(total, num):
r = []
already = 0
for i in range(1, num):
money = random.randint(1, (total - already) - (num - i))
r.append(money)
already += money
r.append(total - already)
return rrandint(a, b) 包含两个端点。第 i 次分配前,尚有 total - already 元;本次之后还剩 num - i 个红包,因此本次最多取“余额减去后续红包数”。最后一个领取所有余额,保证总额不变。这个顺序分配方式容易让较大的红包出现在前面。
方法 2:根据随机权重分配
文档已经给出生成权重的部分,需要补全计算红包金额、处理最后一份并返回列表的部分:
python
def redEnvelope2(total, num):
r = []
n = []
for i in range(num):
n.append(random.uniform(1, 100))
sn = sum(i for i in n)
# 待补:根据权重计算前 num - 1 份红包
# 待补:剩余金额分配给最后一份
return r解题提示
1. 随机数是权重,不直接是金额。 文档用 uniform(1, 100) 为每份生成一个正权重。某份的占比为“该权重 ÷ 所有权重之和”。正文中的“随机数函数 random()”是在介绍随机分配,给定框架实际使用 randint 和 uniform,实现时应理解各函数的区别。
2. 先满足最低金额,再按权重分配余额。 若把全部金额直接乘占比,较小权重可能分不到 1 元。例如 10 元分给 3 人,权重为 1、1、98,直接按比例只有 0.1、0.1、9.8 元。要同时满足题目“每份至少 1 元”,可以先给每份预留 1 元,再分配剩下的 total - num 元。
3. 用一个手算例子理解。 总金额 10 元、3 个红包,权重假设为 1、2、4:预留 3 元后剩 7 元,按权重分成 1、2、4 元;加上各自的保底,结果为 2、3、5 元,总额仍为 10 元。这是便于推演的示例,程序的权重由随机函数生成。
4. 取整与余款要一起考虑。 文档明确总金额为整数,未限定每份必须整数还是保留两位。可以沿方法 1 使用整数元,也可以使用“分”保存金额再显示两位小数。采用整数元时,对前 num-1 份的额外分配向下取整,最后一份由总额减去已发金额计算;采用“分”时,每份先预留 100 分,后续同样按整数处理。不要每份独立 round 后就直接返回,舍入可能使总和偏离总额。
5. 每次调用都重新开始。 结果列表、权重列表、已发金额都在函数内部初始化。输入放在 10 次循环外,函数调用放在循环内;不要每轮让用户重新输入,也不要在每次调用中重新设置同一个随机种子,否则每轮结果可能完全相同。
易错点
random.uniform()生成浮点数;random.randint()生成整数,两个函数不是同一种输出。append()修改列表,返回None;写r = r.append(money)会丢掉原来的列表。- 最后一份应使用余额,不能再独立随机抽取;否则总额不一定正确。
- 检查每次结果的 长度、最小金额和总和:分别是
num、至少 1 元、等于total。总金额恰好等于红包数时只能全是 1 元,这种情况没有金额随机变化是正常的。 - 用随机权重不等于所有合法分配方案“等概率”;最后一份接收余款也会有取整影响,不应把随机性描述为严格公平。
练习题 1:自动出题并判分
在第5章 random 模块示例 1 的基础上出 10 道题,包含除法和乘方。一般题目答对得 1 分;结果为负数的减法题、除法题、乘方题,每题答对多得 1 分。最后输出答对的题数与总得分。
知识点与 PPT: 第5章第29页(自动出题示例)、第27页(随机选择);第2章数字运算;第3章循环与分支。
解题提示
- 用两个变量分别统计“答对题数”和“得分”。答对一题,前者加 1;得分根据题型加 1 或 2。答错不加分。
- 先随机生成操作数与运算符,再计算正确结果。Python 乘方用
**,不是^。 - 加分条件为:减法且结果小于 0,或除法,或乘方。不要让所有减法都得 2 分,也不要把“负数减法”的条件套到除法和乘方上。
- 除法的除数不能为 0。课件原例操作数为 1–9,沿用此范围即可避开;若修改范围,需要相应检查。
- 本题没有规定除法回答保留几位。程序出题时应先明确约定,例如提示保留两位,再按同样精度比较;不要把自行采用的规则写成原题要求。
- 先将每种题型各手动检查一次,再加入随机选择;固定随机种子可用于调试,但不要在每轮出题前重复设同一个种子。
练习题 2:同心圆
自定义函数 drawMCircle(radius, dist, num):radius 为最小圆半径,dist 为相邻圆半径之差,num 为同心圆数量。使用 turtle.circle() 绘制同心圆,关键是确定每个圆的起笔位置。
知识点与 PPT: 第5章第12–16页(坐标、移动与画圆);第4章参数与函数调用。
解题提示
第 i 个圆的半径为 radius + i * dist,其中 i 从 0 开始。若希望圆心在原点,在 standard 模式下,可先抬笔移到 (0, -r),将方向设为 0(朝右),落笔后调用 circle(r)。正半径的圆心在画笔左侧,此时恰好是原点。
每画一个圆都重新确定位置和朝向;如果始终站在原点朝右画不同半径的圆,圆心会位于不同的 (0, r),得到的不是同心圆。抬笔移动还可以避免圆之间出现连接线。
练习题 3:随机多边形装饰画
定义一个绘制正多边形的函数,随机生成边数、边长、颜色与位置,组合成抽象装饰画;还可以将数学曲线定义成函数并加入图案。
原文函数签名为 polygon(a, size, color),说明中称“正 n 边形”。这里将 a 解释为边数 n,保持参数含义一致:边数、边长和填充色。
知识点与 PPT: 第5章第14–16页(画笔、运动、方向)、第27–28页(随机数和随机选择);第4章自定义函数。
解题提示
- 边数至少为 3。画一条边后转的角度是 外角
360 / a,不是内角;共重复a次。 - 填充颜色用
fillcolor(color);绘制轮廓前begin_fill(),完成后end_fill()。只设置画笔颜色不会自动填充内部。 - 在主程序里随机选择边数、边长、颜色和位置,再调用
polygon(...);函数本身负责画一个图形。 - 移动到下一幅图形前先抬笔,到达后落笔,避免装饰画之间多出连线。位置范围也要考虑图形大小,不要让大部分图形跑到画布外。
- 颜色可从预先准备的颜色名称列表中
random.choice(...),不必一开始就处理 RGB 模式。