
1. 从四道题看编程入门的核心训练逻辑1.1 这组题目到底在练什么很多人看到输入一个四位数求各位数和水仙花数四叶玫瑰花数判断能否组成三角形这几个题目第一反应是这不就是几道普通的编程练习题吗没错它们确实是练习题但如果你只把它们当成做完就扔的作业那就浪费了这组题目真正的价值。这四道题放在一起其实构成了一条非常完整的入门训练链路。数位拆解练的是整数运算和循环取余水仙花数练的是枚举加条件筛选四叶玫瑰花数是水仙花数的进阶版本考察你对位数和幂运算的理解深度三角形判断则把你从纯计算拉到了逻辑分支和边界条件处理。四道题覆盖了入门阶段最核心的四种能力拆解、枚举、验证、分支。我带过不少刚入门的朋友发现一个普遍现象单独做一道题能做出来但四道题放在一起就懵了因为它们看起来没什么关联。实际上关联很大——数位拆解是水仙花数和四叶玫瑰的基础而三角形判断里的边界思维恰恰是前三道题最容易忽略的东西。把这四道题串起来做一遍你对写程序这件事的理解会上一个台阶。1.2 为什么选择从这几道题切入从教学角度看这组题目的选材非常讲究。它们不需要任何复杂的数据结构不需要面向对象不需要框架只需要最基本的变量、循环、条件判断和输入输出。这意味着你可以把全部注意力放在逻辑本身上而不是被工具和语法细节分散精力。另一个原因是它们都有明确的、可验证的正确答案。水仙花数就那几个四叶玫瑰花数也就那几个三角形判断的对错一目了然。这种可验证性对初学者极其重要——你能立刻知道自己写对了没有而不是模棱两可。还有一个容易被忽视的点这几道题都涉及边界条件。四位数有范围限制水仙花数有位数限制三角形有两边之和大于第三边的严格不等式。边界处理得好不好直接决定你的程序是能跑还是跑对。这个意识一旦建立起来后面学任何东西都会受益。1.3 适合谁来参考这篇内容如果你刚开始学编程正在刷基础题这篇内容可以直接当参考手册用。如果你已经学了一段时间但总觉得基础不牢这四道题可以帮你查漏补缺。如果你是在教别人编程这里面的思路拆解和避坑经验也可以直接拿去用。我下面会按照思路拆解—核心细节—实操实现—问题排查的顺序把每道题都掰开揉碎讲清楚。代码我会用Python来写因为它的语法最接近自然语言读起来不费劲。但思路是通用的你换成C、Java、JavaScript都一样。2. 数位拆解四位数各位数之和的完整实现2.1 核心思路与数学原理输入一个四位数求各位数和这道题看起来简单到不值一提但它其实是后面所有题目的地基。数位拆解的核心操作只有两个取余和整除。假设输入的数是n 1234。我们要拿到千位、百位、十位、个位。最直观的做法是千位n // 1000百位(n // 100) % 10十位(n // 10) % 10个位n % 10这里//是整除%是取余。为什么这样能拿到每一位因为十进制数本质上就是位权的叠加。1234 1×1000 2×100 3×10 4×1。整除1000就把低位全部丢掉只剩千位整除100再取余10就把百位挤到了个位的位置上。提示取余运算%的结果永远在0到除数-1之间。所以% 10的结果一定是 0 到 9正好对应一位数字。2.2 两种实现方式对比实现数位拆解有两种主流方式各有适用场景。方式一数学运算直接拆n int(input(请输入一个四位数)) qian n // 1000 bai (n // 100) % 10 shi (n // 10) % 10 ge n % 10 total qian bai shi ge print(f各位数之和为{total})这种方式的好处是不依赖字符串操作纯数学效率高而且思路清晰。缺点是如果位数不固定比如输入可能是三位也可能是五位代码就得改。方式二转字符串遍历n input(请输入一个四位数) total sum(int(ch) for ch in n) print(f各位数之和为{total})这种方式的好处是位数无关输入几位都能处理代码也更短。缺点是引入了字符串和整数的转换对纯初学者来说多了一层概念。我个人建议先用方式一理解原理再用方式二简化代码。如果你连方式一的原理都没搞明白就直接用方式二后面遇到需要精确控制某一位的题目就会卡住。2.3 循环拆解法通用性最强的方案如果位数不固定最通用的做法是用循环不断取余和整除n int(input(请输入一个整数)) total 0 temp n while temp 0: total temp % 10 temp // 10 print(f各位数之和为{total})这个循环的逻辑是每次拿个位temp % 10加到总和里然后把个位丢掉temp // 10直到数字变成0。比如 1234轮次temptemp % 10total1123444212337312294111050循环结束10这个方案的好处是位数完全无关而且它是后面水仙花数判断的基础。你一定要把这个循环写熟闭着眼睛都能写出来。2.4 实操中的注意事项有几个坑我踩过这里直接告诉你。第一个坑负数处理。如果输入是负数比如 -1234temp % 10在Python里会得到 6因为Python的取余结果符号跟除数一致而不是 -4。所以如果题目允许负数输入你得先取绝对值或者单独处理符号。第二个坑输入验证。题目说输入一个四位数但用户可能输入 123 或者 12345。严格来说你应该加一个范围检查n int(input(请输入一个四位数)) if n 1000 or n 9999: print(输入不是四位数请重新输入) else: # 正常处理 pass第三个坑前导零。如果用户输入 0123int()会把它变成 123位数就变了。如果你用字符串方式处理又得考虑前导零算不算一位。这个要看题目具体要求通常四位数题目默认不会有前导零。注意数位拆解是后面所有题目的基础这个循环写不熟后面水仙花数和四叶玫瑰都会卡壳。建议至少手写五遍直到不用想就能写出来。3. 水仙花数枚举与条件筛选的经典组合3.1 什么是水仙花数水仙花数Narcissistic number的定义是一个 n 位数它的每个位上的数字的 n 次幂之和等于它本身。对于三位数来说就是各位数字的立方和等于自身。举个例子153 1³ 5³ 3³ 1 125 27 153。所以153是水仙花数。三位数的水仙花数一共只有四个153、370、371、407。这个结论你可以直接记但更重要的是理解怎么用程序找出来。3.2 枚举范围怎么定求三位数水仙花数枚举范围就是 100 到 999。为什么从100开始因为三位数最小是100。为什么到999结束因为三位数最大是999。for n in range(100, 1000): # 判断 n 是不是水仙花数 pass这里range(100, 1000)包含100但不包含1000正好覆盖所有三位数。这个边界很容易写错写成range(100, 999)就会漏掉999虽然999不是水仙花数但逻辑上是错的。3.3 判断逻辑的三种写法写法一直接拆位for n in range(100, 1000): bai n // 100 shi (n // 10) % 10 ge n % 10 if bai**3 shi**3 ge**3 n: print(n)这种写法最直观适合三位数这种位数固定的情况。写法二循环拆位for n in range(100, 1000): temp n total 0 while temp 0: digit temp % 10 total digit ** 3 temp // 10 if total n: print(n)这种写法通用性更强改成四位数只需要把** 3改成** 4。写法三字符串处理for n in range(100, 1000): digits [int(ch) for ch in str(n)] if sum(d**3 for d in digits) n: print(n)这种写法最简洁但隐藏了数位拆解的细节。初学者建议先用写法一或写法二。3.4 扩展到任意位数的水仙花数水仙花数的定义其实不限于三位数。n位水仙花数就是各位数字的n次幂之和等于自身。我们可以写一个通用版本def is_narcissistic(n): digits [int(ch) for ch in str(n)] power len(digits) return sum(d**power for d in digits) n for n in range(1, 10000): if is_narcissistic(n): print(n)这个版本能找出所有位数小于等于4的水仙花数。实测下来1到9999之间的水仙花数有1、2、3、4、5、6、7、8、9、153、370、371、407、1634、8208、9474。提示一位数全都是水仙花数因为任何一位数的1次幂都等于它自己。这个边界情况很多人会忽略。3.5 性能与优化思考三位数水仙花数只有900个候选随便怎么写都是瞬间出结果。但如果你要找出所有七位水仙花数候选就有900万个这时候性能就开始有感觉了。一个常见的优化思路是预计算幂次。因为0到9的n次幂是固定的可以提前算好存起来def find_narcissistic(n): powers [i**n for i in range(10)] start 10**(n-1) end 10**n result [] for num in range(start, end): temp num total 0 while temp 0: total powers[temp % 10] temp // 10 if total num: result.append(num) return result这样每次拆位后不需要重新算幂直接查表。对于位数较多的场景这个优化能省不少时间。不过说实话对于入门练习来说性能优化不是重点。重点是先把逻辑写对再考虑快不快。我见过太多人一上来就纠结优化结果连基本逻辑都没搞明白。4. 四叶玫瑰花数水仙花数的进阶变体4.1 四叶玫瑰花数的定义四叶玫瑰花数是水仙花数概念的自然延伸一个四位数它的每个位上的数字的四次幂之和等于它本身。数学表达式对于四位数abcd满足a⁴ b⁴ c⁴ d⁴ abcd。四叶玫瑰花数一共只有三个1634、8208、9474。你可以验证一下1634 1⁴ 6⁴ 3⁴ 4⁴ 1 1296 81 256 1634。对的。4.2 为什么叫四叶玫瑰这个名字的由来其实挺有意思。水仙花数在英文里叫 Narcissistic number自恋数因为数字爱自己。而四位数版本被中文社区赋予了四叶玫瑰这个浪漫的名字四叶草象征幸运玫瑰象征美丽合在一起就是幸运又美丽的四位数。名字归名字逻辑上它和水仙花数完全一样只是位数从3变成了4幂次从3变成了4。4.3 实现代码与逐行解析for n in range(1000, 10000): qian n // 1000 bai (n // 100) % 10 shi (n // 10) % 10 ge n % 10 if qian**4 bai**4 shi**4 ge**4 n: print(n)逐行看range(1000, 10000)覆盖所有四位数从1000到9999。qian n // 1000拿到千位。比如 1634 // 1000 1。bai (n // 100) % 10拿到百位。1634 // 100 1616 % 10 6。shi (n // 10) % 10拿到十位。1634 // 10 163163 % 10 3。ge n % 10拿到个位。1634 % 10 4。判断四次幂之和是否等于原数。4.4 通用版本一次找出所有位数的自幂数既然水仙花数和四叶玫瑰都是自幂数的特例我们可以写一个通用函数输入位数输出该位数的所有自幂数def find_armstrong(n): powers [i**n for i in range(10)] start 10**(n-1) if n 1 else 0 end 10**n result [] for num in range(start, end): temp num total 0 while temp 0: total powers[temp % 10] temp // 10 if total num: result.append(num) return result for digits in range(1, 8): nums find_armstrong(digits) print(f{digits}位自幂数{nums})跑一下你会发现位数自幂数10-92无3153, 370, 371, 40741634, 8208, 9474554748, 92727, 93084654883471741725, 4210818, 9800817, 9926315这个表你可以直接收藏以后遇到类似题目直接对答案。4.5 实操心得与常见误区误区一把四次幂写成四倍。有人会把qian**4写成qian * 4这是完全不同的东西。1⁴ 1但 1×4 4。这个错误很低级但很常见尤其是赶时间的时候。误区二枚举范围写错。四位数是1000到9999写成range(1000, 9999)会漏掉9999。虽然9999不是四叶玫瑰但逻辑上不对。误区三忽略0的处理。如果某一位是00⁴ 0这没问题。但如果你用循环拆位当数字中间有0时循环依然能正确处理因为temp % 10会得到0temp // 10会继续。这个不用担心。心得先验证再优化。我建议你先用最笨的方法把三个四叶玫瑰找出来确认结果对得上再去想怎么优化。很多人一上来就写通用函数结果通用函数里有个边界bug找半天找不到。注意四叶玫瑰只有三个数如果你跑出来四个或者两个一定是代码有问题。用这个来快速自检。5. 三角形判断逻辑分支与边界条件的实战5.1 三角形成立的数学条件判断三条边能否组成三角形核心条件只有一个任意两边之和大于第三边。设三边为 a、b、c则需要同时满足a b ca c bb c a为什么是大于而不是大于等于因为如果 a b c三条边会退化成一条直线构不成三角形。这个边界极其重要是这道题最容易出错的地方。5.2 基础实现与代码解析a float(input(请输入第一条边)) b float(input(请输入第二条边)) c float(input(请输入第三条边)) if a b c and a c b and b c a: print(可以组成三角形) else: print(不能组成三角形)这段代码逻辑很直白但有几个细节值得说。为什么用 float 而不是 int因为边长不一定是整数可能是 3.5、4.2 这种。用 float 更通用。当然如果题目明确说输入整数用 int 也行。为什么三个条件都要写有人会想我只要判断较小的两边之和大于最大边不就行了吗理论上是的但你需要先找出最大边多一步排序。直接写三个条件更直观也不容易出错。5.3 进阶判断三角形类型如果题目进一步要求判断是锐角、直角还是钝角三角形就需要用到勾股定理的推广直角a² b² c²c为最长边锐角a² b² c²钝角a² b² c²a float(input(请输入第一条边)) b float(input(请输入第二条边)) c float(input(请输入第三条边)) if a b c and a c b and b c a: sides sorted([a, b, c]) x, y, z sides if x**2 y**2 z**2: print(直角三角形) elif x**2 y**2 z**2: print(锐角三角形) else: print(钝角三角形) else: print(不能组成三角形)这里用sorted把三边排序z就是最长边然后只需要比较一次。5.4 浮点数比较的坑如果你用浮点数做相等判断比如x**2 y**2 z**2可能会遇到精度问题。比如 3、4、5 没问题但某些浮点数运算结果会有微小误差。更稳妥的做法是判断差值是否小于一个极小值eps 1e-9 if abs(x**2 y**2 - z**2) eps: print(直角三角形)这个技巧在处理浮点数相等时非常常用值得记住。5.5 输入验证与异常处理实际使用中用户可能输入负数或者0。边长必须是正数所以应该加验证a float(input(请输入第一条边)) b float(input(请输入第二条边)) c float(input(请输入第三条边)) if a 0 or b 0 or c 0: print(边长必须为正数) elif a b c and a c b and b c a: print(可以组成三角形) else: print(不能组成三角形)这个验证看起来简单但很多人会忘。忘了的后果是输入 -1、-1、-1 时程序会告诉你可以组成三角形因为 -1 -1 -2 不大于 -1等等-2 -1 是假的所以其实会输出不能组成。但输入 0、0、0 时000 是假的也会输出不能组成。所以负数0的情况碰巧不会误判但逻辑上还是应该显式验证。5.6 三角形判断的常见错误清单错误类型错误写法正确写法后果用大于等于ab cab c退化三角形被误判只判断一次只写 ab c三个条件都写漏判忘记正数验证直接判断先验证 0边界情况异常浮点相等 直接比较abs(diff) eps精度误差排序后只比一次没排序就比先sorted再比比错边这张表建议截图保存写代码的时候对照检查。6. 四道题的串联思路与综合训练建议6.1 它们之间的内在联系把这四道题放在一起看你会发现一条清晰的能力递进线数位和→ 练的是拆解把一个整体拆成部分。这是所有数字处理的基础。水仙花数→ 练的是枚举验证遍历所有可能逐个检查条件。这是暴力法的雏形。四叶玫瑰→ 练的是泛化把三位数的逻辑推广到四位数理解参数化的重要性。三角形判断→ 练的是分支边界从纯计算转向逻辑判断学会处理边界条件。这四种能力合在一起就是编程入门阶段最核心的基本功。你后面学的排序、查找、递归、动态规划全都是在这四个能力上叠加的。6.2 建议的练习顺序如果你正在刷题我建议按这个顺序来先做数位和用三种方式各写一遍数学拆、字符串、循环确保彻底理解。再做水仙花数先用固定三位数写再改成通用n位数。然后做四叶玫瑰直接套用水仙花数的通用版本改个参数就行。最后做三角形判断重点练边界条件和输入验证。每道题做完后不要急着下一道先问自己三个问题能不能用另一种方法实现边界条件都考虑到了吗如果输入变了代码还能跑吗6.3 从这四道题延伸出去的方向这四道题只是起点你可以沿着几个方向继续深入方向一自幂数的完整探索。写一个程序找出所有位数小于等于10的自幂数。这个过程中你会遇到性能问题需要学习优化技巧。方向二三角形判断的扩展。加上面积计算海伦公式、周长计算、判断是否为等腰或等边三角形。方向三数位处理的通用化。写一个函数输入任意整数返回它的各位数字列表、位数、逆序数、是否为回文数等。方向四从暴力枚举到数学优化。水仙花数的枚举范围可以缩小因为n位数的n次幂之和有上限。研究这个上限能让你理解算法优化的基本思路。6.4 给初学者的几句实在话我见过太多人在这几道题上卡住不是因为题目难而是因为想太多。有人一上来就想用最优雅的写法结果连最基本的循环都没写熟。有人纠结用Python还是C结果两种都没学好。我的建议很简单先用最笨的方法把题做出来再想怎么改进。笨方法能跑通说明你理解了逻辑改进方法能跑通说明你理解了优化。顺序不能反。另外一定要自己动手敲代码。看别人写和自己写是两回事。看的时候觉得我懂了一上手就发现各种小错误。这些错误才是真正让你进步的东西。提示这四道题的标准答案网上到处都是但抄答案没有意义。你要的是自己推导出答案的过程那个过程才是真正学到的东西。7. 常见问题排查与避坑速查7.1 数位拆解类问题排查问题拆出来的位数不对。检查整除和取余的顺序。记住口诀先整除到目标位再取余10。比如要百位先//100把百位变个位再%10取出来。问题循环拆位时死循环。检查有没有写temp // 10。如果忘了这一步temp永远不变循环永远不会结束。问题负数拆位结果异常。Python中-123 % 10 7不是 -3。如果题目允许负数先取绝对值。7.2 自幂数类问题排查问题结果多了或少了。先检查枚举范围。三位数是range(100, 1000)四位数是range(1000, 10000)。范围写错是最常见的原因。问题幂次写错。三位数用**3四位数用**4。写反了结果全错。问题通用函数在1位数时出错。1位数的范围是0到910**(1-1) 1会漏掉0。需要特殊处理。7.3 三角形判断类问题排查问题退化三角形被误判。检查是不是用了。必须是严格大于。问题浮点数精度导致直角判断失败。用abs(diff) 1e-9代替。问题输入0或负数没被拦截。加一个if a 0 or b 0 or c 0的前置检查。7.4 通用调试技巧打印中间变量。在关键步骤加print看看每一步的值对不对。这是最原始但最有效的调试方法。用小数据测试。不要一上来就测9999先用153、1634这种已知答案的数测试。对照已知结果。水仙花数就四个四叶玫瑰就三个跑出来对不上就是代码有问题。分步验证。先验证拆位对不对再验证幂次对不对最后验证比较对不对。不要一次性写完再调。7.5 一张表总结所有坑题目常见坑排查方法数位和循环忘记整除检查temp // 10数位和负数取余异常先取绝对值水仙花范围写成range(100,999)改成range(100,1000)水仙花幂次写成乘法检查**3不是*3四叶玫瑰范围写成range(1000,9999)改成range(1000,10000)四叶玫瑰幂次写成**3改成**4三角形用了改成三角形浮点相等直接比用abs(diff) eps三角形没验证正数加前置检查这张表我建议你打印出来贴在显示器旁边写代码的时候随时对照。踩过的坑不用再踩第二遍。7.6 最后分享几个实操小技巧技巧一用断言自检。在代码里加assert比如assert 153 in results如果结果不对会立刻报错比肉眼检查快得多。技巧二把常用逻辑封装成函数。数位拆解、幂次求和这些逻辑封装成函数后可以反复调用代码更干净也不容易出错。技巧三保留一份笨版本。优化后的代码跑出结果后用笨版本再跑一遍对比。两个版本结果一致才能确认优化没引入bug。技巧四注释写为什么而不是是什么。不要写# 取个位要写# 取个位用于累加幂次。前者是废话后者帮你记住意图。这几道题看起来简单但真正写对、写全、写通用还是需要花点功夫的。我自己当年做这几道题的时候水仙花数一次就过了但四叶玫瑰因为范围写错卡了半小时三角形判断因为用了导致退化情况误判。这些坑现在说起来都是笑话但当时确实让人抓狂。希望这篇内容能帮你少走点弯路把时间花在真正重要的逻辑理解上而不是跟边界条件死磕。