提到“整除”这两个字写代码的人脑子里通常先闪过的是C语言里的%取余符。说实话这个运算符用起来简单但真要拿它去判断“一个数能不能同时被3和5整除”再画一张标准流程图很多人会卡在细节上。前几天一个读者给我转了一道稍显头的题n满足条件——n20250412 能被20240413整除n20240413 能被20250412整除。我一看这正好可以把数学上的整除同余、流程图设计和C语言代码串成一条线。这篇文章就按这个思路来聊既照顾刚要接触整除的新手也想给已经会写if的读者一点近似实战的延伸。下面的内容分成四大块先把整除和取余的概念掰开揉碎然后回到经典的“判断n能否同时被3和5整除”流程图接着写C语言实现和常见坑最后用那道带大年份数字的进阶题演示怎么把同余化简真正落地。每部分都有可以直接抄作业的代码和步骤。1. 先把“整除”这件事说透1.1 数学里的整除和C语言里的整除不是一回事在数学上说“整数a能被整数b整除”指的是存在一个整数k使得 a b × k。比如15能被3整除因为15 3 × 516不能被3整除因为16 3 × 5 1。这个定义很干净但在写程序时会遇到一个容易混淆的点C语言里的a / b是整除运算它只取商结果是一个整数。举个例子C语言里7 / 2的结果是3不是3.5。因为两个操作数都是int类型所以执行的是整数除法小数部分直接被截掉。这个行为和“能不能整除”有关系吗有关系但不完全等价。只靠/判断整除是不靠谱的你算出7 / 2 3并不能立刻知道余数是多少。真正用来判断整除的是配套的取余运算符%。a % b在C语言里表示a除以b后的余数。当a % b等于0的时候说明a能被b整除。这是判断整除的黄金法则凡是判断能否整除优先看余数是否为0而不是看商。对于“同时能被3和5整除”核心条件是n % 3 0 n % 5 0这比写成n / 3或者n / 5要直观得多也不容易出错。1.2 为什么“取余等于0”就能判断整除很多人刚学编程时不太理解为什么余数为0就等于整除我们回头看看带余除法的定义。任意整数n除以一个正整数m都会得到一个商q和一个余数r满足n q × m r且 0 ≤ r m当r等于0时n q × m所以存在整数q让等式成立n当然被m整除。当r不等于0时无论如何都凑不出一个整数倍的等价关系。所以n % m 0就是“n能被m整除”的计算机语言表达。这个逻辑还可以推广到“同时被几个数整除”。n能同时被3和5整除意思就是n同时满足n % 3 0和n % 5 0。注意这两个条件必须都用和0比较不能写成n % 3 0那是赋值语句在很多编译器里能编译过去但结果完全不对这也是新手最容易踩的坑。1.3 别忽略负数和大数的情况在数学里整除、余数的概念对负数同样成立但C语言对负数的取余规则是向着0截断的。比如-7 % 2在C99标准里结果是 -1因为 -7 (-3) × 2 (-1)。如果你以为余数一定是正数那么判断“-7能不能被2整除”时-7 % 2 ! 0确实没问题因为余数不为0说明不能整除但要是反过来想用这个余数去推导商就会掉坑。更关键的是当n是负数时n % 3 0和n % 5 0的判断依然准确因为0的符号没有正负之分。所以对于纯整除判断负数并不可怕可怕的是你在判断前又顺手做了整数除法那就可能因为截断方向不同而得到错误结论。至于大数20万以内的n用int没问题但后面那道进阶题里数字接近4×10^14就必须用long long类型了。2. 画流程图前先把判断逻辑想清楚2.1 从需求到算法先写伪代码“判断一个数n能否同时被3和5整除”是一个特别经典的入门流程题。先别急着画框把逻辑顺序想清楚。算法可以分成四步输入一个整数n。判断n是否能被3整除。再判断n是否能被5整除。两个条件都满足输出“能”否则输出“不能”。写成伪代码大概是这个感觉开始 输入 n 如果 n % 3 0 并且 n % 5 0 输出“能同时被3和5整除” 否则 输出“不能同时被3和5整除” 结束这里“并且”就是逻辑与在C语言里写作。两个条件需要同时成立才能进入“能”的分支。任何一个条件不满足整个表达式就是假走“不能”的分支。2.2 流程图的基本框怎么摆流程图的画法有成套的行业标识但这里只说最核心的几种。开始和结束用圆角矩形或椭圆形框输入输出用平行四边形处理操作用矩形判断分支用菱形。判断框内写条件框外一定要标注两个出口通常用“是/否”或者“Y/N”标明。针对这个题最常见的标准画法是椭圆形“开始”。平行四边形“输入n”。菱形判断“n % 3 0”如果为否直接跳转到“输出不能”。如果为是进入第二个菱形判断“n % 5 0”。第二个判断为是输出“能”为否输出“不能”。每个输出都汇合到椭圆形“结束”。这种画法把两个条件拆成两个判断框思路清晰特别适合考试和教学。如果你觉得框太多了也可以在菱形里直接写n % 3 0 n % 5 0一个判断框就够。但要注意一个判断框里塞进两个条件对新手来说容易忽略的短路逻辑而两个判断框则能让你直观看到“先从3开始再判断5”的过程。2.3 画流程图的三个常见低级错误第一判断框的“是/否”方向乱标。有些人画完第一个判断后两条分支都指向同一个输出导致逻辑矛盾。第二缺少结束框。考试时流程不闭合会扣分。第三把输入输出框画成矩形混淆了“动作”和“数据”。在程序里输入n是数据流动要用平行四边形而计算n % 3这一步才是处理动作用矩形。很多人把“n % 3 0”写进矩形这就错了因为这是一个判断必须放在菱形里。还有一个技巧如果你觉得画两个菱形麻烦可以先用数学生成“等价条件”。因为3和5互质n能同时被3和5整除等价于n能被3×515整除。这句话先记住后面写代码时会省很多事但流程图里我更建议保留两个判断框因为更通用换成4和6这种非互质数字时两个判断框仍然正确而直接合并成24就会出错。3. C语言实操从流程图到可运行代码3.1 第一版代码直接用两个判断条件流程图想清楚之后代码基本是照抄。下面这段是完整的C语言程序从输入到输出一步到位#include stdio.h int main(void) { int n; printf(请输入一个整数 n); scanf(%d, n); if (n % 3 0 n % 5 0) { printf(%d 能同时被 3 和 5 整除\n, n); } else { printf(%d 不能同时被 3 和 5 整除\n, n); } return 0; }写完之后用几个典型值验证一下。n 15输出“能”n 10输出“不能”n 0输出“能”。看到n0也输出“能”的时候别慌这不是bug而是数学定义上0能被任何非零整数整除。如果你希望n是正整数才判断可以在条件里再加一个n 0if (n 0 n % 3 0 n % 5 0) { ... }3.2 编译器怎么理解这条if语句很多人写完n % 3 0 n % 5 0后会担心优先级问题。C语言的运算符优先级规定%的优先级高于的优先级高于。所以整个表达式会被自动解析成((n % 3) 0) ((n % 5) 0)也就是先计算n % 3把结果和0比较再计算n % 5和0比较最后做逻辑与。这个运算顺序恰好是我们想要的。新手最容易犯的错误是写成n % 3 0 n % 5 0却在某个条件里多写一个等号变成n % 3 0编译器会认为这是赋值表达式哪怕编译能通过最终结果也不对。每次写完条件先看一眼是还是这个习惯能帮你省下大量调试时间。3.3 用n % 15一行代码搞定但要搞懂原因既然3和5互质n能同时被3和5整除等价于能被15整除代码可以简写为if (n % 15 0) { printf(%d 能同时被 3 和 5 整除\n, n); } else { printf(%d 不能同时被 3 和 5 整除\n, n); }但这里有一个很关键的前提3和5互质。为什么互质就可以相乘判断因为如果n能被15整除那么n 15k自然能被3和5整除反过来如果n能被3和5同时整除n必须同时包含3和5这两个质因子由于3和5互质n就必须包含它们的乘积因子15。这个结论可以推广n能被a和b同时整除等价于n能被lcm(a, b)整除其中lcm是最小公倍数。比如判断n能否同时被4和6整除不能写成n % 24 0因为4和6的最小公倍数是12n % 12 0才对。流程图画两个判断框的通用性在这个例子里体现得淋漓尽致。3.4 数据类型的边界int不够用怎么办在判断“n能否同时被3和5整除”这个简单题目里int完全够用。但如果你要处理大数字比如后面的进阶题n大约是4×10^14量级int根本装不下。C语言的int在多数环境下是32位最大只能到2147483647约2.1×10^9。一旦超出范围计算结果溢出判断结果就毫无意义。这时候要用long long它是64位有符号整数最大可到9223372036854775807约9.2×10^18。哪怕是409876661809331这个数字也放得下。写的时候要养成在数字末尾加LL后缀的习惯比如long long n 409876661809331LL;这样编译器才会把字面量当成long long处理而不是先按int解析再转换。很多人在大数题目里栽跟头不是算法错而是类型选错了。4. 进阶解那道“n20250412能被20240413整除”的题4.1 先审题别被大年份数字吓住前面聊的都是基础现在回到开头那道进阶题n满足以下条件n 20250412 能被 20240413 整除。n 20240413 能被 20250412 整除。看数字很唬人但本质还是整除问题。为了后面写起来方便先定义两个变量A 20240413B 20250412你会发现B比A大9999因为20250412 - 20240413 9999。这个差值不是随便来的它几乎是整个题目的核心钥匙。第一个条件写成同余式n B ≡ 0 (mod A)。因为B A 9999而A 9999在mod A的意义下等价于9999所以第一个条件等价于n 9999 ≡ 0 (mod A)也就是 n ≡ -9999 (mod A)第二个条件写成同余式n A ≡ 0 (mod B)。因为A B - 9999在mod B的意义下等价于 -9999所以第二个条件等价于n - 9999 ≡ 0 (mod B)也就是 n ≡ 9999 (mod B)这样题目就从两个带大年份数字的整除条件变成了两个相对简洁的同余条件。4.2 手动解题代入消元与互质判断有了上面两个同余条件接下来就是解同余方程组n ≡ 9999 (mod B) n ≡ -9999 (mod A)先设 n 9999 kB这里的k是任意整数。代入第一个同余条件9999 kB 9999 ≡ 0 (mod A)也就是 kB 19998 ≡ 0 (mod A)。因为B在mod A的意义下等价于9999所以把B替换成99999999k 19998 ≡ 0 (mod A)提取公因数99999999(k 2) ≡ 0 (mod A)这时候如果能确定9999和A互质就可以两边同时除以9999得到k 2 ≡ 0 (mod A)9999 9 × 1111 3^2 × 11 × 101。再看A 20240413。我一时间不好心算但可以用程序或者手算验证A的数位和是2024041316不能被3整除A的奇数位与偶数位差也不是11的倍数A除以101有余数13。所以9999和A的最大公约数是1确实互质。于是得到通解k A × t - 2代回n 9999 kBn 9999 (A × t - 2) × B当t 1时得到最小正整数解n 9999 (A - 2) × B把A和B的具体值代进去n 9999 (20240413 - 2) × 20250412n 9999 20240411 × 20250412计算20240411 × 20250412得409876659999932再加9999得到n 409876661809331这个数字看起来很大但它确实是满足条件的。因为n除以A的余数是-9999除以B的余数是9999正好对应两个同余条件。4.3 用C语言验证这个巨型答案费了半天劲解出来的答案对不对当然要验证。写一段C语言代码用取余符号直接检查#include stdio.h int main(void) { long long n 409876661809331LL; long long A 20240413LL; long long B 20250412LL; if ((n B) % A 0) { printf(条件1成立n 20250412 能被 20240413 整除\n); } else { printf(条件1不成立\n); } if ((n A) % B 0) { printf(条件2成立n 20240413 能被 20250412 整除\n); } else { printf(条件2不成立\n); } return 0; }这段代码在支持C99的编译器上运行两个printf都会走成立的分支。我建议你把n改成其他数比如0或者9999再看输出会更容易体会到同余条件的约束力。4.4 这题还能怎么扩展这道题如果只停留在“求出n”其实只发挥了它一半的威力。你还可以继续思考如果不要求正整数n的通解里t取负整数时结果是什么如果A和B不互质两边还能不能同时约掉如果把9999换成其他差值d题目的解法会怎么变这些扩展看起来是数学问题但在实际编程里很常见尤其是做时间戳、日期周期、多模数计数系统时往往会遇到“一个数同时满足几个模约束”的场景。读懂这道题的化简过程相当于掌握了一种通用的问题转化能力把多个整除条件化成同余式再用代入消元找规律最后用代码验证。5. 常见问题与排查技巧实录5.1 判断整除时最容易犯的四个错我见过太多人在整除题目上翻车归结起来就四类。第一把%和/搞混。n / 5得到商n % 5得到余数。想把“n能被5整除”写对只能靠n % 5 0。第二把写成。赋值语句会把右边的值塞给左边然后整个表达式的值等于被赋的那个值if判断的结果往往和预期完全相反。第三忽略类型溢出。当n超过2×10^9时还用int结果溢出后取余判断全部失真。第四用多个判断条件时不打括号。虽然C语言的优先级在这里能正确解析但依赖优先级不是好习惯。写代码时加上括号会更保险if ((n % 3 0) (n % 5 0)) { ... }5.2 流程图常见问题速查考试或者画设计文档时流程图有自己的一套查错方式。如果判断框有两条出口一个对应“是”一个对应“否”那么每条出口最后必须能走到结束框中间不能有路径断裂。输入输出框里要写清楚是什么数据比如“输入n”而不是只写一个n。判断框里要写可判定的条件比如n % 3 0不要写“处理数据”这种含糊描述。这些细节虽然不起眼但在批改和协作里都是硬指标。5.3 实测下来最省事的调试手段如果你不确定自己的整除逻辑对不对别急着从头推先用一组极端测试数据跑一遍0、负数、两个条件的公倍数、只满足其中一条的数。比如判断“同时被3和5整除”至少测0、-15、10、15这四组。0和-15容易暴露类型问题10暴露只满足一个条件的分支15验证正确路径。把这四个值测完代码的整除判断基本就稳了。对于进阶题那种大数我一般会把答案代回去做一次“手算模”或者直接写一段验证程序。你在看我这篇文章时完全可以把代码复制下来自己改一个n然后观察输出。与其背结论不如亲手跑一遍这个习惯比多刷十道题都管用。最后再分享一个小技巧判断一个数能否同时被多个数整除先看看这些数是否互质。如果互质直接判断能否被它们的乘积整除如果不互质务必用最小公倍数判断或者老老实实把条件分开写。这个原则在写C、Python、Java时都通用也是我跟读者讨论“整除”时最常强调的一点。