☰
整除、流程图与C语言取余:从年份题到避坑指南
2026/10/13 9:00:28 网站建设 项目流程

“整除”这两个字,单独看很正常,连着念三遍“整除整除整除”,莫名有点施法前摇的味道。但这三个字凑在一起,其实牵出了三件不一样的事:数学里的整除是一个严格定义的关系;流程图里的整除是一个分支判断;C语言里跟整除对应的是一对符号/和%,稍不留神就能给你挖个坑。我之所以想写这篇,是因为这两天正好刷到一道年份题:求整数 n,满足 n+20250412 能被 20240413 整除,并且 n+20240413 能被 20250412 整除。乍看是一串年份在倒腾,本质就是整除和同余的经典考法。

这篇文章就把解这道题、画“判断一个数能否同时被3和5整除”的流程图、整理 C 整除取余的坑放在一起聊一遍,对正在学 C 语言、准备算法入门、或者被老师要求交流程图作业的同学,应该都直接用得上。

1. 三个“整除”,其实说的是三件不同的事

1.1 数学里的“整除”:比“除得尽”严格得多

先对齐最基础的定义。设 a、b 是整数,b 不等于 0,如果存在某个整数 q,使得 a = bq,我们就说 b 整除 a,记作 b | a。这里最容易闹笑话的是读法:b | a 读作“b 整除 a”或“a 被 b 整除”,反过来说“b 被 a 整除”就完全反了。a 是被分的那堆东西,b 是除数,q 是每个人分到的数量。

我习惯用一个分东西的例子。15 块巧克力分给 3 个人,每人 5 块,一块不剩,3 | 15 成立。16 块分给 3 个人,每人 5 块还剩 1 块,3 ∤ 16。注意整除是不允许出现“每人 5.33 块”这种说法的,它要求商必须是整数,而且余数为 0。这跟口语里“除得尽”最大的区别就在这里:口语不管对象是不是整数,数学里的整除只讨论整数。

整除还有几个会在解题时突然派上用场的性质。如果 m | a 且 m | b,那么 m | (a+b)、m | (a-b),进一步地,对任意整数 k、l,都有 m | (ka+lb),也就是能整除任意线性组合。这个性质在后面的年份题里是最关键的一把钥匙。它还可以用来快速判断一个数能否被另一个数整除时,将大数拆成若干好判断的部分分别考察。另外,整除和同余是同一件事的两面:n 被 d 整除,等价于 n ≡ 0 (mod d)。很多同学觉得同余抽象,其实它就是把“余数相同”的数拉到一个集合里说事。

1.2 编程里的“整除”:符号背后的规则

到了程序里,“整除”从数学上的关系命题,变成了一对运算符。/和%总是成对出现,它们的关系用一句话能记住:a = (a / d) * d + a % d。对正数来说,7 / 2 得 3,7 % 2 得 1,很好理解。但对负数,C 语言用的是向零取整,也就是截断小数部分:-7 / 2 结果是 -3 而不是 -4,-7 % 2 结果是 -1。很多从 Python 转到 C 的人在这里第一次翻车,因为 Python 里 -7 // 2 是 -4,-7 % 2 是 1。这个问题我在第 4 节会专门展开。

程序里“判断一个数能不能被另一个数整除”,对应的写法永远是n % d == 0。注意这里只用了%,没有用/。因为除法会先把商截断成整数,再反过来乘,碰到边界值很容易因为溢出或者截断误差得出错误结论。这一点属于“看着简单,一写就错”的典型。而%本身也不只是用来判断整除的,它还是分页、哈希、循环队列里最常用的下标计算工具,属于每个程序员肌肉记忆的一部分。

1.3 流程图里的“整除”:把它画成分支条件

流程图是另一种描述整除的方式,它不关心具体的运算符号,只关心逻辑走向。判断一个数 n 能否同时被 3 和 5 整除时,流程图上真正要画的是两个判断框:第一个问 n 被 3 除是否余 0,第二个问 n 被 5 除是否余 0。这里的“整除”本质上是一个布尔表达式的结果,决定了流程应该走“是”的分支还是“否”的分支。

画流程图有个基本功,先把五个基本图形认全:椭圆或圆角矩形表示开始和结束,平行四边形表示输入输出,矩形表示处理,菱形表示判断,带箭头的线表示流程方向。判断整除要用菱形,而不是用矩形,这是新手交作业时最常见的扣分点之一。另一个常见问题是不给菱形出口标“是/否”,光画两条线出去,看的人根本不知道哪条对应哪种情况。我把标准画法和容易踩的细节放在第 3 节一起说。

这三个层面先拉通:数学里你研究的是关系,程序里你操作的是符号,流程图里你布置的是分支。接下来拿那道年份题练手,看整除怎么真正“算”起来。

2. 年份整除题:n 满足两个条件时怎么解

2.1 把题目翻译成同余方程

题目原话是:整数 n 满足下面两个条件:

  • n + 20250412 能被 20240413 整除;
  • n + 20240413 能被 20250412 整除。

第一步不要急着代数字,先把“能被整除”翻译成同余。翻译完就是:

n + 20250412 ≡ 0 (mod 20240413)
n + 20240413 ≡ 0 (mod 20250412)

观察一下四个数:20250412 与 20240413 相差正好 9999。这个差几乎就是出题人故意留给你的一条路。同余式两侧可以同时加减模数的整数倍,于是第一式可以整理成:

n ≡ -20250412 ≡ -9999 ≡ 20240413 - 9999 = 20230414 (mod 20240413)

第二式同理:

n ≡ -20240413 ≡ 20250412 - 20240413 = 9999 (mod 20250412)

到这一步,就有了一个标准的同余方程组:

n ≡ 20230414 (mod 20240413)
n ≡ 9999 (mod 20250412)

如果你熟悉中国剩余定理,理论上可以直接套公式。但这两个模数相差只有 9999,硬套公式会碰上求 9999 的逆元,乘来乘去容易算错。更顺的路线是回到整除定义本身,用“设商”的办法把条件变成两个等式。

2.2 作差消元:一条比想象中更顺的路

既然两个条件都说了“被整除”,那就直接写出商。设两个整数 a、b 满足:

n + 20240413 = 20250412 a
n + 20250412 = 20240413 b

把两式相减,注意用第二式减第一式,左边正好是:

(n + 20250412) - (n + 20240413) = 9999

所以有:

20240413 b - 20250412 a = 9999

因为 20250412 = 20240413 + 9999,代入后整理:

20240413 (b - a) = 9999 (a + 1)

这是一个很漂亮的形态:左边是 20240413 的倍数,右边是 9999 的倍数。现在关键就看这两个数是否互质。9999 = 3² × 11 × 101,逐个检查:

  • 20240413 的各位数字和是 2+0+2+4+0+4+1+3 = 16,不是 3 的倍数,所以不含因子 3;
  • 判断 11 的倍数有个小技巧,奇数位和减偶数位和应为 11 的倍数:奇数位和 3+1+4+0+2=10,偶数位和 1+4+0+2+0=7,差是 3,不等于 11 的倍数;
  • 20240413 除以 101:101 × 200400 = 20240400,余 13,也不是 101 的倍数。

于是 gcd(20240413, 9999) = 1。既然右边有因子 9999,左边却和 9999 互质,就逼出结论:20240413 必须整除 a+1。于是存在整数 t,使:

a + 1 = 20240413 t

接下来把 a 代回原来的等式:

n + 20240413 = 20250412 a
n = 20250412 (20240413 t - 1) - 20240413
n = 20250412 × 20240413 t - (20250412 + 20240413)

算出乘法:20250412 × 20240413 = 409876702300156;最后得到:

n = 409876702300156 t - 40490825,t ∈ Z

这就是这道题最完整的答案。整个过程比直接解同余方程组要少算很多中间量,主要用到的就是“被整除就设商”和“两式相减”两个动作。

2.3 回代验算与标准答案写法

解数学题最怕解完不验算。这里验证很简单,直接用 a+1 = 20240413 t 往回代。把 20240413 记作 A,20250412 记作 B,那么 a = A t - 1,n = B a - A = A B t - (A + B)。

检查第一个条件:n + A = A B t - B = B(A t - 1),能被 B 整除,也就是能被 20250412 整除,符合条件 2。
检查第二个条件:n + B = A B t - A = A(B t - 1),能被 A 整除,也就是能被 20240413 整除,符合条件 1。

两个条件都成立,说明整个通解正确。写成标准同余形式就是:

n ≡ 409876661809331 (mod 409876702300156)

如果题目问的是最小正整数解,取 t = 1,得到 409876702300156 - 40490825 = 409876661809331。但如果题目只问“满足条件的 n”,一定要说明 t 取任意整数,只写一个最小正整数会丢分。

这类“年份题”在竞赛里出现时,通常不指望你真去把八位数乘起来。看到模数接近,先做差,再试因子,解题速度会快很多。这也是为什么我一直建议,学整除的时候不要只背性质,要把“设商”“作差”“验证互质”这三个动作练熟。

3. 判断一个数能否同时被3和5整除:流程图这样画

3.1 “同时”翻译成逻辑与

题目要求里的关键词是“同时”。翻译成逻辑就是:n 被 3 整除 且 n 被 5 整除,两个条件都要成立。这里有个数学等价关系值得先点出来:因为 3 和 5 互质,n 能被 3 整除并且能被 5 整除,等价于 n 能被 15 整除。可是如果流程图里只画一个“n % 15 == 0”的判断框,虽然结果完全正确,但严格来说并没有体现“同时”这个题眼,老师有时会扣分。所以教学场景下,标准做法永远是画两个判断框,分别检查 3 和 5,让读图的人一眼看到两个条件是“并且”的关系。

两个判断框可以串行摆放,也可以并行再合并。教材上最常见的是串行:先问 n 能不能被 3 整除,能再继续问 5,不能就直接输出“不能”,流程图不需要再往下走。这样画的优点是符合人检查时的自然顺序,也不会因为并行分支太多导致连线交叉。

3.2 标准流程图:从开始到结束

按上面思路,完整步骤如下:

  1. 椭圆框写“开始”。
  2. 平行四边形框写“输入 n”。
  3. 菱形框写“n % 3 == 0”,出口分两支:
    • 否:直接进输出框“输出‘不能’”,再到结束;
    • 是:继续进入下一个菱形判断。
  4. 第二个菱形框写“n % 5 == 0”,出口同样分两支:
    • 否:进同一个“输出‘不能’”,再到结束;
    • 是:进“输出‘能’”,再到结束。

用文本示意大概长这样:

开始 ↓ 输入 n ↓ [n % 3 == 0 ?] ──否──→ 输出“不能” ──→ 结束 ↓是 [n % 5 == 0 ?] ──否──→ 输出“不能” ──→ 结束 ↓是 输出“能” ──→ 结束

手工画图时有几个细节值得注意。第一,菱形框里只写一个简短条件,不要堆两行字,否则图会变得很挤。第二,每个出口都必须标“是”或“否”,这是硬性要求,不标别人只能靠猜。第三,两个“否”出口如果都汇到同一个输出框,连线尽量绕开中间区域,避免交叉,保持图面整洁。第四,流程线必须带箭头,起始位置贴着框边缘,不能悬空。

如果还想再严谨一点,判断框里的条件建议写“n mod 3 = 0”而不是“n % 3 = 0”,因为流程图不绑定任何一门具体语言,mod 写法更通用。不过在实际作业里,写 % 一般也不会被判定为错误,关键是逻辑要完整。

3.3 伪代码与 C 实现

流程图画完,写程序就顺理成章了。伪代码很简单:

输入 n 如果 n % 3 == 0 且 n % 5 == 0: 输出“能同时被3和5整除” 否则: 输出“不能同时被3和5整除”

C 语言实现:

#include <stdio.h> int main(void) { int 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 % 3 == 0 已经为假,后面的 n % 5 == 0 根本不会执行。对于本题来说短路不影响结果,但如果你在第二个条件里写了 n 作为除数、或者做了有副作用的函数调用,短路就会改变行为。调试阶段建议先把两个余数分别算出来:

int r3 = n % 3; int r5 = n % 5; printf("r3=%d, r5=%d\n", r3, r5); if (r3 == 0 && r5 == 0) { ... }

这样能直观看到每一步的余数,排查问题比直接看“能/不能”高效得多。还有一个边界情况:n 为 0 时,0 % 3 == 0 且 0 % 5 == 0,程序会输出“能”,这在数学上是正确的,因为 0 能被任何非零整数整除。但如果题目限定 n 为正整数,就在输入后加一个 n > 0 的判断。

4. C 语言整除与取余符号:这些坑我全踩过

4.1 / 和 % 是怎么分工的

C 语言里,整数除法跟取余总是一对出现。假设 a 是被除数,d 是除数,那么会有两个结果:商 q = a / d,余数 r = a % d,而且始终满足:

a = q * d + r

对正数来说,一切都很安分。7 / 2 = 3,7 % 2 = 1,完美符合“7 除以 2 商 3 余 1”的数学直觉。麻烦出现在负数上。C99 标准规定整数除法采用向零取整,也就是说商的小数部分直接丢掉,向 0 靠拢。于是 -7 / 2 不再等于 -4,而是等于 -3;余数呢?-7 % 2 等于 -1。检查恒等式:(-3) × 2 + (-1) = -7,没毛病,但数学里带余除法一般要求余数 0 ≤ r < |d|,C 语言这条规则显然不保证。

很多人第一次接触这个知识点时会有一种“编程数学怎么跟书上不一样”的错觉。其实不是数学变了,是语言把“怎么取整”的选择权交给了标准。C 选择截断,Python 选择向下取整,Java 跟 C 一样也是截断。于是同一个算式在不同语言里可能得到不同余数,但恒等式 a = q*d + r 永远成立。

4.2 负数一出,规则就不“数学”了

看一组对比:

表达式C 结果Python 结果
7 % 211
-7 % 2-11
7 % -21-1
-7 % -2-1-1

从 Python 转 C 的同学最容易在这张表上失分。原因很简单:Python 的//是向下取整,商向负无穷方向走,余数由“被除数 = 除数 × 商 + 余数”倒推,保证余数非负;C 是向零取整,余数符号跟随被除数。

那判断整除会不会受影响?好消息是:判断整除写n % d == 0时,无论被除数是正是负,结果都一样,因为余数为 0 时符号讨论没有意义。真正会翻车的是把余数当数组下标或哈希桶索引时,比如 n % 4 在 n 为负时可能得到 -1,直接拿去做下标就会越界。工程上统一先做一次“归一化”:

int r = n % d; if (r < 0) { r += abs(d); }

这样得到的 r 一定落在 [0, |d|) 区间里,对分页、哈希、环形队列都安全。

4.3 判断整除最容易翻车的三个场景

第一个场景是用除法反推。有人为了省事写if (n / 3 * 3 == n),想判断 n 能否被 3 整除。这种做法对小数看着成立,但 n 稍大就可能因为整数除法先截断、后乘法溢出,得出错误结果。判断整除就老老实实用n % 3 == 0,一行代码的事,不要秀操作。

第二个场景是浮点数。浮点运算有精度问题,a / b == 2.0这种写法极不稳定,10.0 / 0.1 甚至都不等于 100.0。需要判断“能否除尽”时,能把问题转到整数运算就转,不能转就用fabs(fmod(a, b)) < 1e-9这种带容差的方式,别直接拿 == 比浮点结果。实际项目中,浮点配合“整除”这个说法本身就要警惕,计数类问题用整数,测量类问题用容差。

第三个场景是 int 溢出。比如年份题算出来的乘积 409876702300156,这个数早就超过 int 的约 21 亿上限。即使单项数据不超 int,两步乘出来也可能爆。写代码之前养成估算数量级的习惯:模数、除数、商、中间乘积都过一遍,发现可能超 int 就换成 long long,不要等测试用例爆了才回头改。顺手写一个安全的判断函数:

int is_divisible(long long n, long long d) { if (d == 0) return 0; return n % d == 0; }

除数不为 0 的判断看似多余,但加上之后函数才能放进公共工具库,否则哪天被脏数据喂一个 d=0,程序直接崩溃。

5. 常见问题与避坑速查表

5.1 常见问题对照表

把前面所有容易踩的坑整理成一张表,平时做题或写代码时对一眼,能省不少排查时间:

症状可能原因处理办法
数学里 a|b 和 b/a 分不清记号记忆混乱记住 a|b 表示存在整数 k 使 b=ak,读作“a 整除 b”
-7 % 2 结果不是 1 而是 -1C/C++ 向零取整先取 abs(d) 再做归一化,或直接用 n % d == 0 判断整除
用 n / k * k == n 判断整除除法截断+乘法溢出一律改写成 n % k == 0
浮点数之间用 == 判断整除精度误差转整数运算,或 fabs(fmod(a,b)) < 1e-9
年份题答案在 int 里溢出中间乘积超 21 亿用 long long,写代码前先估算数量级
流程图菱形出口没标是/否逻辑分支不明确每个判断框出口都写字
0 % 3 判断条件数学上 0 能被整除若题目要求正整数 n,先判断 n > 0
同时被 3 和 5 整除只写一个 15 判断没体现题眼教学作业画两个判断框,程序里写成n%3==0 && n%5==0

这张表里最值得记住的一条是:判断整除永远写n % d == 0,不要用任何“除法再乘回去”或者“直接用浮点数比较”的方案。这是整除在编程里的第一原则。

5.2 我的几个实操习惯

最后分享几个我自己的习惯,算不上什么高深技巧,但对减少低级错误很有帮助。

第一,做整除相关数学题时,我第一步一定先翻译。要么写成 a = bq,要么写成 a ≡ 0 (mod b),要么写成 a % b == 0。三种表达在纸上对应三种思维:前两种走代数变形,最后一种直接面向代码。一旦翻译完,题目就从语文题变成了代数题,后面就是基本功。

第二,画流程图时先画输入输出,再画判断框。我见过很多同学一上来就精心雕琢菱形,结果画到一半发现输入都没画,流程图没法闭环。流程图的阅读顺序是数据流:输入 → 处理 → 判断 → 输出,按照数据流来画,结构自然就顺了。

第三,写工具函数时一定处理除数为 0。哪怕调用场景保证 d 不为 0,也要在函数开头写一行判断。这个习惯帮我挡过好几次线上数据异常。

行,这篇说到这儿就差不多了。我自己的感受是,整除这个概念从来不缺“简单”,缺的是在不同场景下把它拆对。解数学题时把“能被整除”翻译成同余方程,画流程图时把“同时”拆成两个判断框,写 C 代码时永远记住 % 的余数符号和除数为 0 的边界。这三件事只要都做到,哪怕题目再换年份、再换数,套路也不会变。尤其是年份题里那个“先作差、再验互质、最后回代验证”的流程,建议你手抄一遍,碰到类似的题直接照着走,基本不会错。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询