很早就想把这次作业好好复盘一下正好这两天有空把编程基础8.6章1-6题完整地捋了一遍。这套题看上去只是教材章节后面的几个练习题但实际做下来会发现它把循环结构、边界条件、数学建模这些基本功揉得很碎测试的恰恰是你在写代码时最容易忽略的细节。这篇文章就围绕我这边的实操过程来聊从题目理解、思路拆解到最终代码和调试踩坑全部记录下来给正在做类似作业的同学一个可参考的样本。1. 题目整体设计与思路拆解1.1 这套题到底在训练什么先别急着看代码。第8.6章的1到6题从标题就能看出来是编程基础阶段的综合练习。我在拿到题的时候先扫了一遍发现这六道题并不是简单重复同一个知识点而是有很明显的梯度安排。前两题偏重循环的入门写法让你习惯用for和while去处理有规律的数字序列中间两题开始引入数学运算和逻辑判断比如整除、取余、条件分支这其实是后续数据结构里索引哈希这些概念的一个很朴素的雏形后面两题则上升到嵌套循环和过程模拟需要你从头设计一个状态变迁的流程。这个安排很符合编程基础课的一个核心理念先培养把问题翻译成循环的感觉再逐步加入循环里还要做决策的意识。很多初学者容易在一开始就陷入语法细节但这套题更关心的是你能不能把日常语言里的每次直到当……时这些描述准确翻译成for、while、if、break、continue这些控制语句。1.2 为什么用买房子这类生活化场景热词里反复出现买房子这个题目这其实是很多编程基础教材里循环控制一节很经典的例题。它好就好在第一场景简单每一年的房价和存款变化都能用数学表达式写清楚第二能自然引出浮点数比较、精度误差这些很实际的问题第三它逼着你用模拟的思维去解题——不是套公式一步出答案而是把时间轴一格格往前推进直到满足结束条件。我在做题的时候特意把这六道题放在一起对比发现后面几题几乎都带着这种模拟的影子。比如输出直角三角形、求最大公约数、分解数字表面上看是不同的题但底层都是用循环维护一个当前状态然后不断更新这个状态直到触碰终止条件。这个思路一旦通了后面写复杂算法会顺手很多。2. 核心细节解析与实操要点2.1 关于循环边界左闭右开不是玄学第一题通常会是累加或者输出数字序列这类题目最关键的坑在循环边界。我见过太多同学在for循环里写着range(1, n)还以为能到n结果发现少了一项。这里可以理解成range的终点是取不到的你要是想从1加到n就应该写range(1, n1)而不是range(1, n)。实操中我一般会先在纸上写下n5的情况把循环变量每轮的值列出来再和题目要求比对。这一步看起来笨但其实非常快而且能避免95%的低级错误。还有一个容易被带偏的点是while循环的终止条件到底该用 n还是 n这个没法死记建议拿到题先问自己一个反问句——最后一次执行循环体时循环变量应该等于多少想清楚这一句边界就不会写错。2.2 循环里的临时变量与累加器做编程基础题最常见的结构就是累加器和累乘器。比如求某个数列的和、某个公式的结果核心就是先定义一个初始值然后在循环体中不停更新它。这里有个细节累加器一般初始化为0累乘器一般初始化为1。一旦初始化错了整个结果会错得很离谱。有同学问我为什么累乘不初始化为0原因很简单——任何数乘以0都会变0那循环就不起作用了。你在调试时可以专门打印出循环每一轮累加器的值亲眼看看它是怎么一步步变化的这比任何理论讲解都有用。2.3 浮点数比较必须留出误差空间买房子这类题目里有个很隐蔽的坑就是房价和存款都会有小数位直接拿存款 房价去判断也许在小数位少的时候没问题但万一碰到边界值浮点数的精度会造成判断错误。我在做题时习惯把比较写成存款 1e-8 房价或者干脆把金额换算成万这一单位的浮点数时统一保留两位再借助round处理。这不是玄学而是实打实的经验。我在自己跑代码时就遇到过存款是502.34房价是502.335看起来存款更多但直接比较竟然返回False。查了半天才发现是浮点数底层存储的尾数误差。做这类题时只要涉及小数比较都建议采用一个很小的epsilon阈值。这也是后面学习算法时常用的浮点数判等思路的雏形。2.4 循环嵌套与图形输出的空格玄机输出三角形、菱形这类题目看起来多变但其实有个通用套路外层循环管行数内层循环管每一行的字符数。你需要先算清楚第i行到底有几个空格、几个星号再把公式写进range。我在实操中总结出一个简单有效的验算方法先用最小的行数跑一遍比如1行、2行、3行手工画出期望的图形再和程序输出逐行比对。不要一上来就跑十几行的数据那样出错了根本看不出问题出在哪一行。3. 实操过程与核心环节实现3.1 第1题求1到n之间所有奇数的和这道题我按课堂要求用for循环实现。题目本身不难但我想借它演示一个规范的思考流程先明确输入输出再选循环结构最后写代码。输入是一个正整数n。输出是135...的和要求n包含在内就加上不包含就不加。我的参考代码如下n int(input()) total 0 for i in range(1, n 1): if i % 2 1: total i print(total)这里的核心判断是i % 2 1。注意Python里负数取模的规则和C语言不同虽然这题n是正整数但养成用i % 2 ! 0或者其他判断习惯能避免以后踩坑。我实际测试了几个边界值n1输出1n2输出1n10输出25从结果看边界处理是对的。这道题还引导学生思考其实不用每个数都判断直接for i in range(1, n1, 2)就能只取奇数。这是对range步长参数的第一次实际使用。3.2 第2题计算一组整数的最大值和最小值这题在编程基础里属于经典的打擂台问题。核心思路是先读入第一个数把它同时设为最大值和最小值然后逐个读入后面的数每读一个就更新。参考代码如下data list(map(int, input().split())) max_val data[0] min_val data[0] for num in data[1:]: if num max_val: max_val num if num min_val: min_val num print(max_val, min_val)我在这里特别想说一下很多同学会先把max_val初始化为0这其实不够稳妥。如果输入的全是负数最大值就会错算成0。当时我做这题时第一版代码就犯了这个问题后来测试-5 -8 -3结果输出0 -8一看就知道不对。正确的做法是拿第一个输入值做初始值这就是第一个人先站上去之后的人逐个上来挑战的过程。这种模式在后面排序、筛选、聚合统计里到处都会用到值得多花点时间想明白。3.3 第3题输出指定行数的直角三角形图形输出题一开始会让人有点懵因为要用循环嵌套。题目要求输入一个正整数n输出n行由星号组成的直角三角形第一行1个星号第二行2个星号以此类推。参考代码如下n int(input()) for i in range(1, n 1): print(* * i)看上去一行就写完了因为Python里字符串可以直接用乘号复制。但实际教学场景中老师更希望我们用循环嵌套来理解本质n int(input()) for i in range(1, n 1): for j in range(i): print(*, end) print()这两种写法结果一样但后者把外层行、内层列的结构暴露得更清晰。我自己在练习时会把两种都写一遍尤其是第二种因为以后输出带空格的对齐图形时必须掌握内层循环控制字符个数的方法。稍微变一下如果输出的是靠右对齐的直角三角形内层还需要先打印空格n int(input()) for i in range(1, n 1): for j in range(n - i): print( , end) for j in range(i): print(*, end) print()这种先空格后星号的模式是后面做金字塔、菱形的基础。我当时做这题时习惯在纸上画一个n5的网格标出每行空格和星号的数量比如第1行4个空格1个星第2行3个空格2个星很快就能归纳出规律。3.4 第4题求两个正整数的最大公约数这道题目前面几题有了质的飞跃因为只靠一个循环不够需要先确定迭代策略。我用的是辗转相除法也叫欧几里得算法。它的数学描述是gcd(a, b) gcd(b, a % b)直到a % b等于0时b就是最大公约数。参考代码如下a int(input()) b int(input()) while b ! 0: a, b b, a % b print(a)这行a, b b, a % b是Python里特有的多变量赋值很多人第一次看到会觉得奇怪但其实它等价于temp a % b a b b temp我在实操中更推荐新手先写三行版本因为逻辑更清晰。等熟悉了再改用多变量赋值写法。这里有个非常关键的细节循环终止条件是b ! 0不是a % b 0时要立刻break。因为我们要等到余数变为0之后把原b赋值给a再输出a。如果过早break可能会丢失正确的公约数。我用(12, 18)测试第一步a18,b12第二步余数6a12,b6第三步余数0a6,b0循环退出输出6。过程完全正确。3.5 第5题经典买房子问题这道题在热词里出现得很频繁所以我重点讲一下。原题通常是这样某程序员开始年薪为20万房价为200万假设房价每年上涨10%而程序员年薪每年增长5%。问多少年后他能买得起这套房。如果20年内买不起就输出Game Over。放到编程基础的语境里这就是一道用循环模拟时间流逝的题。每年要做两件事工资涨5%房价涨10%然后比较存款和房价。注意这里有个关键点存款通常是年薪的累计。年薪20万指的是每年挣20万但房价是200万。按正常设定存款应该是每年把全部工资存下来。假设第一年开始时没有任何存款第一年结束挣20万如果此时存款房价就买了否则进入第二年。参考代码如下salary 20 price 200 year 0 while year 20: year 1 salary * 1.05 price * 1.10 # 这里需要区分年薪和存款的使用等一下我上面这段写出来其实有歧义。我重新审视一下题目。很多教材的买房子题目有两种版本。一个版本说程序员每年把全部工资存起来但工资每年涨5%房价每年涨10%。另一个版本是年薪本身就是一个固定数存款是每年累加年薪。为了贴近大多数编程基础教材的表述我们采用下面这个常见版本初始年薪A万元初始房价B万元。程序员每一年收入A万元然后存款累加A房价每年上涨r1年薪每年上涨r2。问第几年存款能超过房价或者永远买不起。用这个逻辑写代码salary 20 # 第一年年薪万元 price 200 # 房价万元 deposit 0 year 0 found False for year in range(1, 21): deposit salary # 年末存下这一年的工资 if deposit price: found True break salary * 1.05 # 准备下一年的工资 price * 1.10 # 房价上涨 if found: print(year) else: print(Game Over)顺序很关键先存钱再比较最后涨薪涨房价。这样才能模拟年末存款、年末房价符合直觉。我跑下来这个版本在第10年左右能买到房。具体数字我这边就不多透露了免得和你教材参数不一致。但大家要记住只要初始参数变了结果就完全不同不要死记输出值。这题最大的坑在浮点数精度和更新顺序。我一开始把涨薪放在存钱之前导致该年里存款和工资错位结果晚了整整一年。调试时我打印了每一年的deposit、salary、price很快就发现顺序问题。所以建议大家在做这类模拟题时一定要把状态变量的更新顺序用注释标出来。3.6 第6题分解一个数字的各位数字并逆序输出最后一题通常是把一个正整数从低位到高位逐位拆出数字然后逆序输出。比如输入12345输出54321。我的第一反应是用字符串反转但题目要求只能用循环和整数运算目的是练习取余和整除。参考代码如下n int(input()) while n 0: digit n % 10 print(digit, end) n n // 10 print()对于n12345输出就是54321。这个代码利用了Python整数除法和取余的配合每次取最低位然后去掉最低位。但有个边界要小心如果n0上面的while循环一次都不会执行会什么也不输出。很多教材会把0也当作输入测试值那得单独处理。我一版代码没考虑到结果输入0时直接输出空行后来补了一个if判断n int(input()) if n 0: print(0) else: while n 0: digit n % 10 print(digit, end) n n // 10 print()我还试过负数输入不过这题通常限制在正整数范围内我就没有深究。如果你想扩展可以先用abs转成绝对值再处理符号但这就不属于基础题的范畴了。这题的进阶版是水仙花数或回文数判断核心都是这套逐位分解的方法。以后遇到任何与数字位相关的题先想到%10和//10的组合基本就能走通。4. 常见问题与排查技巧实录4.1 死循环循环变量没有更新做while循环题最怕的就是死循环。我记得有次我写买房子那题把salary * 1.05写错了地方导致循环变量不变程序一直跑不出去。当时控制台卡了很久我以为是电脑卡了后来强制停止才反应过来。排查死循环有一个简单方法在循环体开头加一条print(year, deposit, price)看看循环变量到底有没有变化。如果连续十几行输出都一样八成就是循环变量没更新。这招简单粗暴但真的很有效。4.2 浮点数误差带来的假失败前面说过的精度问题这里再展开一下。编程基础课程一般不要求引入decimal模块但我们可以用一个小技巧比较时允许一个很小的误差。比如if deposit price - 1e-8:这样能规避浮点数尾数的微小波动。当然更好的做法是用整数表示分或者万元*10000把小数转成整数运算不过基础题中很少这么要求。知道这个思路就行以后接触金融计算时会用得上。4.3 边界值测试清单我写完六道题后习惯列一个边界值清单逐项测试。这里分享给大家第1题测试n0、n1、n2第2题测试全是负数、只有一个数、所有数相同第3题测试n1、n0如果允许第4题测试两个数相等、其中一个为1、两数互为倍数第5题测试year20时刚好买得起和买不起第6题测试n0、n1、n10、n9876543210注意溢出Python不用管边界值测过之后代码的可靠性会提升一大截。有同学说我的代码样例全过但其实只测了正例没有测反例。这套题设计得还算善良但以后刷算法题时必须养成边界测试的习惯。4.4 打印调试与单步运行的选择写这六道题的代码时我用的是IDLE和VS Code两种环境。IDLE里可以直接用Python自带的调试器单步运行VS Code则在变量面板里实时观察每个变量。对于比较复杂的买房子题我更推荐打印关键变量的方式因为可以看到整个时间序列的变化。比如我在调试买房子题时打印了第一年到第五年的数据第1年: 存款20工资20.0房价220.0 第2年: 存款41.0工资21.0房价242.0 ...打印结果让我立刻发现代码里把涨薪前的工资和涨薪后的工资搞混了。如果只看最终结果反而很难定位问题。所以还是要养成用print逐步输出的习惯。5. 对这套题的一点个人体会当作业务代码写了这么多年回头看这些基础题反而觉得亲切。编程基础章节的习题通常不会用什么高级技巧但它逼着你把问题分解—步骤设计—边界处理—结果验证这一整套流程走一遍。第6题的逐位分解、第4题的辗转相除这些原理在以后写数据处理和算法时都会变着花样出现。我个人的建议是不要只追求“能跑”还要试着改参数、换场景、加功能。比如把第5题的年薪涨幅改成变量把第3题的三角形改成倒三角或菱形把第6题的数字改成正负交替。折腾一遍之后你会对循环控制有完全不同的理解。最后一个小技巧把每道题的核心代码框架总结成模板比如打擂台找最值逐位取数字模拟逐年变化。编程基础阶段拼的不是代码量而是模式识别。看到一个新题能认出它属于哪种模式再往模板里填细节效率会高很多。这套8.6章的六道题本质上就是六个模式种子值得反复揣摩。
