资讯动态

Python循环结构入门:for/while、嵌套循环与经典习题详解

发布时间:2026/10/14 1:03:49 来源:尧图企业网站定制
1. 第4章到底在考什么循环不是语法是思维习惯作为一门Python入门课的助教我批改第4章循环练习题时最常看到的画面是同学代码写得像模像样语法也没报错运行结果却完全不对。真正深挖下去发现大部分人不是不会写 for 或 while而是没想明白“这段代码到底在替我们重复做什么”。《Python语言程序设计基础》第4章正好卡在大学程序设计课程的第一个分水岭。前几章讲变量、数据类型、表达式都是“一次算一个结果”思维上是直线型的到了循环题目开始要求你“重复做某件事”而且往往是两层重复嵌套在一起。很多同学就是在这个转弯处摔跤的。这篇文章我把第4章程序部分的经典练习题重新做了一遍题目本身的参考答案、运行逻辑、易错点都整理在下面。适合刚学完循环、准备交作业或期末复习的人看。1.1 为什么练习题全都围着 for 和 while 转第4章练习无非三种打印图形、数列求和、整数性质判断。表面上看题目五花八门本质只有一个——把“人脑里重复做的事”翻译成循环结构。我让学生做练习时会让他们先回答三个问题再做哪一部分动作是重复的重复多少次才算结束每次重复什么东西在变拿“打印直角三角形”来说重复动作是“打印若干个星号”结束条件是“打印完n行”每次变化的是“当前行数 i”。只要这三个问题想清楚代码框架就出来了。反过来如果一上来就敲 for i in range(...)很容易把行和列的边界搞混。另一个常见误区是觉得“循环题靠背模板就行”。其实教材里的练习题几乎每题都在考察同一个能力把生活中的规律描述转化为循环条件。比如“完数”题你真正要做的是把“一个数的所有真因子相加”翻译成“遍历1到n-1逐个判断是否能整除”。翻译能力到位语法只是顺手的事。1.2 for / while 的选择不是靠感觉而是看“边界”很多初学者纠结什么时候用 for、什么时候用 while。我的判断方法特别简单如果写代码之前已经明确知道“要循环多少次”用 for如果只知道“什么时候停但不知道会循环多少次”用 while。我把两者做了一个对比方便理解对比维度for 循环while 循环适用场景遍历 range、字符串、列表次数明确猜数字、直到满足某条件才停止循环变量由 range 自动赋值通常需要手动初始化、手动更新结束条件遍历完所有元素条件表达式为 False最典型的坑没有注意 range 的起始与步长忘记更新循环变量导致死循环理解方式“对每个元素处理一遍”“只要条件为真就一直做”练习题里凡是题目说“求1到100”“统计100以内的素数”基本都是 for题目说“直到用户猜中”“直到误差小于某个值”就优先用 while。这个判断不需要背规则只要翻译题意时注意到“次数”和“条件”的区别就够了。2. 打印图形与数列求和的“题意翻译”基本功第4章练习的入门题十有八九是打印图形和数列求和。这类题看着简单却是最容易暴露问题的地方。因为它们的代码量不大错一行输出就全乱套调试起来反而比后面的大题更花时间。2.1 直角三角形与九九乘法表把行列关系画出来经典题目输入行数 n打印一个直角三角形第一行1个星号第二行2个星号第 n 行 n 个星号。我第一次教这个题时要求学生先在草稿纸上写出“要打印的内容”第1行1个 * 第2行2个 * 第3行3个 *写完之后会发现第 i 行应该打印 i 个星号。于是代码就很直白n int(input(请输入行数: )) for i in range(1, n 1): for j in range(i): print(*, end) print()外层 i 控制当前在第几行内层 range(i) 让这一行恰好打印 i 个星号。这里最容易被忽略的是内层循环的 range(i) 依赖外层变量 i如果你写成了 range(n)就会每行都打印 n 个星号输出变成一个矩形。如果题目升级成“等腰三角形”就要在每行星号前面补空格n int(input(请输入行数: )) for i in range(1, n 1): print( * (n - i), end) print(* * (2 * i - 1))第 i 行左侧空格数量是 n-i星号数量是 2i-1。这里的关键是理解字符串乘法 * 3会得到三个连续空格。很多同学不敢用这个技巧宁愿写一个内层循环打空格其实在 Python 里直接用字符串乘法更安全、更直观。九九乘法表是另一个经典嵌套循环题。题目要求输出下三角形式的乘法表参考答案如下for i in range(1, 10): for j in range(1, i 1): print(f{j}×{i}{i * j}, end\t) print()运行之后第三行输出的是1×33 2×36 3×39。注意内层循环的 range(1, i1) 保证第 i 行只出现到 i不会出现4×3这种不对称的式子。print 的 end\t 用制表符把式子对齐每行结束时再单独 print() 换行。我批作业时发现很多同学会把两个循环的边界写成一样的 range(1,10)结果输出一个完整的 9×9 矩形。从做题习惯上说这是典型的“没有先用手推一遍第 i 行长什么样”就直接写代码。2.2 累加器与斐波那契初始化位置决定成败数列求和是第4章最简单的循环应用比如求 1 到 100 的整数和。多数人会写total 0 for i in range(1, 101): total i print(total)这里唯一的考点是total 0必须放在循环外面。如果把初始化写进循环体每次循环都会把 total 重置成 0最后结果就等于 100。这个问题在课堂演示时经常出现因为初学阶段很容易把“每次累加”和“每轮重置”搞混。斐波那契数列是求和问题的进阶版。题目一般要求输出前 n 项比如 1、1、2、3、5、8……核心逻辑是“后一项等于前两项之和”。我的参考答案n int(input(需要打印前几项: )) a, b 1, 1 for i in range(n): print(a, end ) a, b b, a b最容易被绕晕的是a, b b, a b这一行。Python 的平行赋值会在同一时刻计算右边两个表达式的旧值再同时赋值给左边。也就是说右边的a b用的是更新前的 a不是更新后的 a。理解不了这一点可以拆开写成临时变量temp a a b b temp b两种写法等价。考试时如果紧张拆开写反而更稳。教材第4章还没正式讲元组解包所以平行赋值算是这个章节里一个需要单独解释的语法点不少作业答案就是因为这行写成了“如果 ab; bab”这种逐行赋值导致整串数字全部变成相同的值。3. 整数性质类习题素数、最大公约数、水仙花与完数第4章有一类题专门围绕整数性质展开比如判断素数、求最大公约数、找水仙花数、找完数。这类题目的共同特点是需要你遍历某个范围的整数对每个数执行一次“性质判断”。3.1 素数判断为什么要用 sqrt 收窄循环素数题是几乎所有教材都会出现的标准练习题。初版答案通常长这样import math n int(input(请输入一个大于1的整数: )) is_prime True if n 1: is_prime False else: for i in range(2, int(math.sqrt(n)) 1): if n % i 0: is_prime False break if is_prime: print(f{n} 是素数) else: print(f{n} 不是素数)讲解时我会先让学生写最原始的版本从 2 遍历到 n-1能整除就不是素数。然后问一个问题如果一个数 n 存在一个大于 sqrt(n) 的因子 d那么它必然同时存在一个小于 sqrt(n) 的因子 n/d。所以只要检查 2 到 sqrt(n) 之间有没有因子就够了。举个例子判断 37 是否是素数只需要检查 2、3、4、5、6。因为 37 如果能被某个大于6的数整除比如 37 11 × 3.36这不可能11 大于 6对应的另一个因子 37/11 ≈ 3.36 小于 6那在检查 2 到 6 的时候早就该发现能整除了。这个优化不是投机取巧而是数学上可证明的结论。另一个容易被忽略的边界是int(math.sqrt(n)) 1。range 是左闭右开不加 1 的话当 n 是 4、9、16 这类完全平方数时sqrt(n) 本身就是一个可能的因子比如 sqrt(4)2不 1 就会漏掉检查 2导致 4 被错误判断为素数。如果题目要求输出 100 以内的所有素数只要在外面再套一层 for并对每个数单独初始化一次 is_primefor n in range(2, 101): is_prime True for i in range(2, int(n ** 0.5) 1): if n % i 0: is_prime False break if is_prime: print(n, end )许多同学第一次写会把这个内层判断逻辑直接搬到外面结果一个因子判断失败就立刻输出“不是素数”导致后面所有稍大的数都被误判。这里的关键在于素数判断的结论必须等所有可能的因子都检查完才能下不能在找到一个因子之前提前输出。3.2 最大公约数的辗转相除把数学定理变成循环最大公约数题很多初学者第一反应是暴力枚举从两个数中较小的那个开始逐一往下试找到第一个能同时整除两者的数就是答案。暴力方法当然正确但效率一般。教材练习里真正想让你掌握的是辗转相除法。辗转相除法用一句话描述两数相除取余余数为0时当前除数就是最大公约数否则用除数替换被除数用余数替换除数继续做。写成 Pythona int(input(请输入第一个整数: )) b int(input(请输入第二个整数: )) x, y a, b while y ! 0: x, y y, x % y print(f{a} 和 {b} 的最大公约数是 {x})整个过程不需要任何列表或取整技巧。我让学生把 x、y 的每一步变化写在纸上用 252 和 105 举例252 除以 105 余 42105 除以 42 余 2142 除以 21 余 0所以最大公约数就是 21。这个题有一个隐藏坑循环结束后输出的一定是 x不是 y。因为循环退出条件是 y 0此时最大公约数已经被保存在 x 里。另外输入变量 a、b 在循环里被改写成 x、y 之后如果最后还想输出原始输入值必须提前备份。上面代码用x, y a, b完成了这个备份这是批改作业时很多同学会漏掉的一步。3.3 水仙花数和完数先枚举再优化水仙花数的题目描述很固定一个三位数各位数字的立方之和等于它本身。范围是 100 到 999。参考答案for num in range(100, 1000): hundreds num // 100 tens num // 10 % 10 ones num % 10 if hundreds ** 3 tens ** 3 ones ** 3 num: print(num, end )运行结果是153 370 371 407。这题的核心考点是整数分解用整除//和取余%把每一位数字拆出来。num // 10 % 10是先丢掉个位再对10取余拿到原来的十位。很多同学会写错成num % 100 // 10其实也正确关键要理解两种写法都依赖运算优先级。完数完美数题稍微复杂如果一个数等于它的所有真因子之和就称为完数。6 1 2 3所以6是完数。for num in range(2, 1001): factor_sum 0 for i in range(1, num): if num % i 0: factor_sum i if factor_sum num: print(num, end )输出结果是6 28 496。这题虽然用了双重循环复杂度是 O(n²)但 n 只有 1000完全能接受。真正要强调的是factor_sum 0的位置它必须放在内层循环之外、每个新 num 的判断开始之前。如果放在内层循环里每个因子都会被覆盖一次最后的 factor_sum 只等于最后一个因子。我在批改时发现这是完数题里出现频率最高的错误。另外一个容易漏掉的条件是“真因子”的判定范围。对完数来说因子是除去自己本身的数所以内层 range 到 num-1 而不是 num。如果写到 num每个数都会把自己加进去那 6 会变成 123612永远不可能等于自己整道题就全错了。4. 综合应用随机数参与的程序逻辑练习题做到后面会出现一类带有“不确定性”的程序猜数字、模拟掷骰子、蒙特卡洛方法。它们和第2、3节的确定性循环最大的区别在于循环次数不再是固定的而是依赖于程序运行过程中产生的结果。这个时候 while 和 random 库就要一起上。4.1 猜数字不确定循环次数时的 while 设计猜数字题目通常描述为程序随机生成一个 1 到 100 的整数用户不断输入猜测值程序提示“猜大了”或“猜小了”直到猜中为止。代码核心结构如下import random target random.randint(1, 100) count 0 while True: guess int(input(请输入你猜的数字(1~100): )) count 1 if guess target: print(猜小了再试一次) elif guess target: print(猜大了再试一次) else: print(f恭喜你猜中了一共猜了{count}次) break这里用while True是因为我们不知道用户到底会猜几次。每次输入后通过三个分支判断从“猜小、猜大、猜中”三个结果中选一个执行。猜中后立刻 break 跳出循环否则会变成死循环。这个题值得扩展的一点是1 到 100 的整数二分查找策略最多猜 7 次就能保证命中。因为每次猜中间值都能排除一半的范围。我让学生把代码改造一下限制最多猜 7 次import random target random.randint(1, 100) for times in range(1, 8): guess int(input(请输入你猜的数字(1~100): )) if guess target: print(猜小了继续) elif guess target: print(猜大了继续) else: print(f你在第{times}次猜中了) break else: print(f7次都没猜中正确答案是{target})这里的 for-else 语法是隐藏考点Python 的 for 循环如果正常执行完没有被 break 中断就会执行 else 块。很多教材正文可能没有展开讲这个语法但练习题的参考实现会用到值得单独记一下。猜数字还有一个实战细节如果用户输入的不是数字比如按到了字母键int(input(...))会直接抛异常。教材第4章还没教异常处理作业阶段可以不用处理但如果想让程序更健壮可以用 try-except 包一层输入这是学有余力时的加分项。4.2 蒙特卡洛模拟求 π把概率统计变成循环计数蒙特卡洛方法是随机模拟题的经典代表。题目描述通常这样在边长为1的正方形中随机投放大量点统计落在以原点为圆心、半径为1的四分之一圆内的点数比例用这个比例估算 π。原理一句话能说清正方形的面积是 1×11四分之一圆的面积是 π/4。随机投点落在圆内的概率等于面积之比也就是 π/4所以 4 乘以这个比例就得到 π 的近似值。import random n 100000 inside 0 for i in range(n): x random.random() y random.random() if x * x y * y 1: inside 1 pi 4 * inside / n print(f模拟{n}次后π ≈ {pi})random.random() 会生成 0 到 1 之间不包含1的浮点数用来模拟点坐标。x*x y*y 1判断点是否落在四分之一圆内。最后pi 4 * inside / n就是估算值。我跑过一次n 取 10000 时结果可能在 3.13 到 3.15 之间浮动取 100000 时能稳定到 3.14 左右。这个题目很容易出现的一个理解误区是认为“n 越大结果就越精确”其实蒙特卡洛是随机模拟每次运行结果都不同只是误差范围会随着 n 增大而收窄不是绝对变的精确。有些教材的练习会追问如果改用n int(input(请输入模拟次数:))来让用户决定精度该怎么改。其实只要把 n 的那行换成 input 即可核心逻辑不变。这类题的价值不是让你真的去算 π而是让你体会一件事很多工程上的“算不出精确解”的问题都可以通过大量随机试验来逼近答案。循环在这个场景里变成了一个“重复试验计数器”。5. 我批改作业时反复强调的五个隐形丢分点这部分不在任何题目的参考答案里但如果你自己对照检查能少被扣很多分。下面是我在批改循环章节作业时几乎每轮都会遇到的五类问题。5.1 缩进决定了循环体的边界Python 用缩进区分代码块这在循环章节体现得最明显。下面这段代码光看逻辑想打印“每行一个星号”但因为 print 的缩进位置不同结果完全不同for i in range(3): print(i) print(*)这个会打印三组每一组都包含一个数字和一个星号。如果把第二行 print 的缩进删掉让它和 for 对齐for i in range(3): print(i) print(*)那就变成循环结束后才打印一个星号。很多同学的代码乍一看“差不多”实际差的就是这一个缩进。我的建议是写完代码后把所有缩进相同的行画成一组问自己这一组是循环内还是循环外每次执行都要经过它还是只执行一次5.2 死循环不是“电脑卡了”是循环变量没更新初学者遇到死循环第一反应是“电脑死机了”。其实大多数死循环只是代码逻辑问题。最典型的例子是i 1 while i 10: print(i)循环里没有更新 ii 永远是 1条件永远为真于是程序无限打印 1。这在第4章练习里非常常见尤其是 while 循环的题目。解决办法是在循环体内增加i i 1或者考虑使用 for 循环。我上课时会让学生养成一个调试习惯遇到疑似死循环时先按 CtrlC 终止运行然后在循环内加一行 print 打印循环变量的值看看它有没有变化。加上 print 之后问题通常一眼就能看出来。这叫“给调试加个临时观察窗”比对着代码发呆有效得多。5.3 浮点数比较与输出格式的小坑循环章节已经有浮点数参与计算的题目了比如蒙特卡洛和斐波那契斐波那契恰好都是整数但某些求和题会涉及浮点数。浮点数直接比较是否相等是一件危险的事因为 0.1 0.2 在计算机里并不精确等于 0.3print(0.1 0.2 0.3) # 输出 False如果要比较浮点数是否相等应该计算差的绝对值是否小于某个很小的数或者干脆在条件里改用整数运算。整数题里也有一类坑比如判断完全平方数时用int(n ** 0.5) ** 2 n可能会因为浮点误差出问题。虽然大多数考试环境下不会触发但我建议养成“能用整数运算就别用浮点数”的习惯。输出格式方面很多题目要求“结果之间用空格分隔”直接 print 默认会在末尾换行导致每行只有一个结果。解决方案是给 print 设置 end 参数或者在循环结束后一次性 join 成一个字符串。这个细节不是算法问题但对最终得分影响很大。5.4 变量作用域循环变量会在循环结束后依然存在Python 的循环变量在循环结束后并不会被销毁。比如这段代码for i in range(5): result i * 2 print(i)循环结束后i 仍然等于 4。如果后面还有一段代码也用了 i就可能悄悄影响结果。第4章练习往往只在一个程序里完成一个任务这个问题不明显但到了后面多个循环串行执行时循环变量互相覆盖就容易出 bug。我的建议是循环变量尽量使用有语义的名字比如 row、col、num、idx而不是都用 i、j、k。三个嵌套循环都用 i、j、k 时代码很难检查每层循环边界是不是写错。5.5 只给答案不给验证是作业里最大的“隐性扣分点”最后一个问题不在代码本身而在做题步骤。很多同学提交的循环题代码能跑通但没有任何验证痕迹。比如求完数代码输出6 28 496这是正确答案可如果不写一句“验证6 123”老师根本不知道你是真的理解了还是从网上背来的代码。我一直建议学生在每道题后面用注释写一行“为什么这个结果是对的”。比如# 验证6 1 2 328 1 2 4 7 14这行注释花不了10秒但能强迫自己把思路理一遍。写不出来的时候往往就意味着这道题还没真正学会。6. 几个常见“换个数字就不会”的场景题目做多了之后我发现第4章的失分点往往不是题目的难度而是很多同学只记住了一类题的标准写法题目稍微变形就懵了。这里补充几个常考变形也相当于把之前的知识点再串一遍。6.1 从“求1到100和”变成“求1到100内奇数和”基础版是 total 从0开始循环里把 i 加上变形版只要在循环体里加一个判断total 0 for i in range(1, 101): if i % 2 1: total i print(total)也可以用 range(1, 101, 2)一步到位。关键在于看到“奇数和”要立刻反应过来要么在循环里筛选要么修改 range 步长。这两种做法没有优劣但考试时先用 if 筛选的思路更通用因为换成“3的倍数”“能被5整除”也能套用。6.2 从“打印星号”变成“打印数字或字母”打印题的正统套路是“外层控制行、内层控制列”数字字母题只是把星号替换成变量值。常见题目打印如下图形。1 1 2 1 2 3参考答案n int(input(请输入行数: )) for i in range(1, n 1): for j in range(1, i 1): print(j, end ) print()核心还是第2节那个“第 i 行打印 i 个数”的逻辑只是把print(*, end)换成了print(j, end )。如果题目要求每行打印都是 1 到 n那就是内层 range 写错的问题了。6.3 从“判断一个数是否素数”变成“输出区间内所有素数”这个我在第3节已经给过代码。很多同学第一次写会犯一个错误把所有素数判断的结果放在一个大循环里共用一个 is_prime 变量导致第一个非素数被判断后后面所有数都跟着受影响。正确做法是让 is_prime 在内层循环之前、每个新数开始判断时重新初始化成 True。这个逻辑值得多写一遍for n in range(2, 1001): is_prime True for i in range(2, int(n ** 0.5) 1): if n % i 0: is_prime False break if is_prime: print(n, end )如果把is_prime True放到外层循环之外程序只会检查一个数后面的数全都沿用第一次的判断结果输出直接乱套。这个错误我已经看过无数遍。6.4 从“完数”变成“输出完数的所有因子”完数题的变体是除了输出完数本身还要求输出例如6 1 2 3这样的因子分解式。思路是在原来的判断代码里用字符串累计因子for num in range(2, 1001): factor_sum 0 factors [] for i in range(1, num): if num % i 0: factor_sum i factors.append(str(i)) if factor_sum num: print(f{num} .join(factors))这里用列表收集因子最后用 join 拼接。如果没学过列表也没关系可以改成边判断边拼字符串但列表方案更清晰。这个变体说明一个道理循环题很少孤立出现它经常和列表、字符串这些后续章节的知识配合考查。把第4章的循环结构练扎实后面学列表时会舒服很多。7. 做完这些练习题之后我建议你做的三件事刷完第4章的练习题先别急着翻下一章。我个人带学生复习时会额外要求做三件看似“不增加代码量”但非常有效的事。第一把每一道自己写过的循环题用两种不同方式各写一遍。比如猜数字分别用 for 和 while 实现素数判断分别用遍历到 n-1 和遍历到 sqrt(n) 实现。用一个题目比较两种写法比做十道新题更能理解“为什么教科书推荐某种写法”。第二手动模拟运行一遍自己写的代码。拿斐波那契举例手写前五项逐行对照代码里 a、b 的变化。这个过程看起来“慢”但它能帮你把抽象的执行过程变成具体的画面。我见过太多同学代码能跑对但问他“a、b 交换后现在的值是多少”就答不上来。这就是对循环执行机制的理解还不够深。第三给自己出两道变形题。把“直角三角形”改成“倒直角三角形”把“1到100求和”改成“求1到100的平方和”把“判断素数”改成“统计1000以内素数的个数”。自己改题自己解是最快暴露知识盲区的方式。如果你能独立把一道原题改成自己的版本并做出来那这道题的暂时算真正掌握了。说到底第4章的练习题答案本身并不重要重要的是你写答案的过程中经历的那几次调试、那几处报错、那几个想不通的瞬间。这些才是循环结构真正教给你的东西。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑