同余的概念与基本性质
困难7同余是什么?从钟表周期到数学规律
你有没有注意过,钟表上的时间总在一个圈里循环:3点过后12小时还是3点,15点其实就是下午3点。这是因为钟表每12小时重复一次。这种“看起来一样”的关系,在数学里就叫同余(congruence)。简单说,同余就是描述“两个数除以同一个数,余数相同”的数学工具。它不仅在数学中非常重要,在编程里也随处可见——比如判断星期几、处理循环队列、加密算法等,都离不开它。
我们从一个生活中最常见的问题开始:今天是周三,那么100天后是星期几? 只要知道100除以7的余数(100 mod 7 = 2),周三往后数2天就是周五。这里的关键就是100与2模7同余:100 ≡ 2 (mod 7)。有了同余,很多周期问题都能轻松搞定。
一、同余是怎么定义的?
给定一个正整数 m(叫做模数),如果两个整数 a 和 b 满足 a - b 能被 m 整除,我们就说 a 和 b 模 m 同余,记作:
a ≡ b (mod m)
如果 a - b 不能被 m 整除,就记作 a ≢ b (mod m)。
另一种等价理解:a 和 b 除以 m 得到的余数相同。因为如果 a = m × q₁ + r,b = m × q₂ + r,那么 a - b = m × (q₁ - q₂) 一定是 m 的倍数。
举个生活中的例子
- 零花钱:小明每周拿10元零花钱。他攒了47元,小红攒了17元。47和17除以10的余数都是7,所以 47 ≡ 17 (mod 10)。两个人虽然钱数不同,但如果按“10元一组”装箱,多余的钱一样多。
- 考试分数:某次考试满分100分,但老师只按“是否及格”(60分以上)来看。76分和136分模100同余吗?76 - 136 = -60,不能被100整除,所以不同余。但 76 和 176 模100同余(76 ≡ 176 (mod 100)),因为 176 - 76 = 100 能被100整除。这就像“考试分数只看最后两位”一样——超过100分就相当于加了一个满分周期。
- 排队:同学们按学号排队,学号除以班级人数(比如40人一班)的余数,就决定了他在班里的序号。学号 123 和 3 模40同余,因为 123 ÷ 40 余3,3 ÷ 40 余3,所以 123 ≡ 3 (mod 40)。这告诉我们,不同年级的两个同学如果学号相差40的整数倍,他们在班级里的“位置”是一样的。
再深入一点:为什么用“模”(mod)这个字?
“模”就像是一个“周期标尺”。把整数想象成一根无限长的尺子,每 m 个单位做一个标记,所有标记相同的位置就属于同一个“类别”。这个类别叫做模 m 的剩余类。比如模3,整数被分成三类:
- 余0类:... -6, -3, 0, 3, 6, ...
- 余1类:... -5, -2, 1, 4, 7, ...
- 余2类:... -4, -1, 2, 5, 8, ...
所有同一类里的数都模3同余。
二、同余的三大基本性质
同余关系非常像我们熟悉的“相等”关系,它满足三个性质:自反性、对称性、传递性。这三个性质合起来说明同余是一种等价关系,可以把全体整数划分成互不相交的等价类(就是上面的剩余类)。
1. 自反性:任何数和自己同余
对于任意整数 a,有 a ≡ a (mod m)。
因为 a - a = 0,0 能被任何非零整数整除(0 = m × 0),所以成立。
生活比喻:今天就是今天,不会变成别的星期几。周三 ≡ 周三 (mod 7) 永远成立。
2. 对称性:如果a和b同余,那么b和a也同余
如果 a ≡ b (mod m),那么 b ≡ a (mod m)。
因为如果 m | (a - b),那么 m 也整除 (b - a) = -(a - b)。整除性质:如果 m 整除一个数,那它也整除它的相反数。
生活比喻:如果今天是周三,100天后是周五,那么周五往前100天也是周三。即 100 ≡ 2 (mod 7) ⇒ 2 ≡ 100 (mod 7)。
3. 传递性:如果a和b同余,b和c同余,那么a和c也同余
如果 a ≡ b (mod m) 且 b ≡ c (mod m),那么 a ≡ c (mod m)。
因为 m | (a - b) 且 m | (b - c),所以 m | [(a - b) + (b - c)] = a - c。
生活比喻:小明和小红零花钱(模10)余数相同,小红和小刚余数相同,那么小明和小刚余数也相同。这是显而易见的,因为余数就只有0~9十种情况。
这三个性质说明:同余关系把整数分成了 m 个“抽屉”,每个抽屉里的数都彼此同余。你一旦知道了某数属于哪个抽屉(余数),就能推断出很多信息。
三、新手容易犯的错误
❌ 错误1:忽略模数不能为0
同余定义中,m 必须是一个正整数。模数为0没有意义,因为任何数减去自身都得0,但“除以0”是禁止的。代码中一定要检查 m == 0 的情况。
❌ 错误2:处理负数时直接用 % 比较余数
在很多编程语言(如C++)中,负数取模的结果可能是负数。比如 -5 % 3 在C++里结果是 -2,而不是数学上期望的 1(因为 -5 = (-2)×3 + 1)。如果直接用 a % m == b % m 判断负数,会出错。正确的做法是:
- 用
(a - b) % m == 0判断整除性(但需要注意C++中%对负数的行为,实际上(a-b)%m的结果符号与a-b相同,但只要a-b是 m 的整数倍,结果就是0,所以判断整除没问题)。 - 或者统一将余数转换为非负:
(a % m + m) % m得到[0, m-1]的余数。
Python 的 % 自动返回非负余数,所以更安全。
❌ 错误3:混淆“同余”和“等于”
a ≡ b (mod m) 并不意味着 a 等于 b,只是说它们相差 m 的整数倍。比如 15 ≡ 3 (mod 12),15不等于3,但15点与3点在钟表上相同。这一点初学容易搞混。
❌ 错误4:认为模数可以不是整数
模数 m 必须是整数(通常为正整数),不能是小数或负数。虽然数学上可以推广,但中小学阶段只考虑正整数模数。
四、完整代码示例(C++ + Python)
下面给出一个完整的交互程序,用户输入两个数和一个模数,程序判断它们是否同余,并演示三个性质。代码中变量使用简短英文单词,每行带中文注释。
C++ 版本
#include <iostream>
using namespace std;
int main() {
// 定义变量并提示输入
int a, b, m; // a和b是要比较的数,m是模数
cout << "请输入三个整数a, b, m(用空格分隔):";
cin >> a >> b >> m;
// 检查模数是否合法
if (m <= 0) {
cout << "模数必须为正整数!" << endl;
return 1; // 返回错误代码
}
// 方法1:用 (a-b) % m == 0 判断同余
// 注意:C++中%对负数返回负余数,但整除性判断依然正确
if ((a - b) % m == 0) {
cout << a << " ≡ " << b << " (mod " << m << ") 成立" << endl;
} else {
cout << a << " ≡ " << b << " (mod " << m << ") 不成立" << endl;
}
// 方法2:通过比较规范化后的余数
// 将余数转为 0 ~ m-1 之间的非负数
int ra = (a % m + m) % m; // 规范化a的余数
int rb = (b % m + m) % m; // 规范化b的余数
cout << "a的余数:" << ra << ",b的余数:" << rb << endl;
if (ra == rb) {
cout << "余数相同,所以同余。" << endl;
} else {
cout << "余数不同,所以不同余。" << endl;
}
// 演示同余的三个性质
cout << "\n=== 演示同余性质 ===" << endl;
// 1. 自反性
if ((a - a) % m == 0) {
cout << "自反性:a ≡ a (mod m) 成立" << endl;
}
// 2. 对称性
if ((a - b) % m == 0 && (b - a) % m == 0) {
cout << "对称性:由a≡b可推出b≡a,成立" << endl;
}
// 3. 传递性:需要第三个整数c
int c; // 第三个用于传递的数
cout << "请输入另一个整数c,用于演示传递性:";
cin >> c;
if ((a - b) % m == 0 && (b - c) % m == 0) {
if ((a - c) % m == 0) {
cout << "传递性成立:" << a << " ≡ " << c << " (mod " << m << ")" << endl;
}
} else {
cout << "不满足a≡b且b≡c,无法演示传递性。" << endl;
}
return 0;
}
运行示例
输入:19 7 12
输出:
19 ≡ 7 (mod 12) 成立
a的余数:7,b的余数:7
余数相同,所以同余。
=== 演示同余性质 ===
自反性:a ≡ a (mod m) 成立
对称性:由a≡b可推出b≡a,成立
请输入另一个整数c,用于演示传递性:31
传递性成立:19 ≡ 31 (mod 12)
(检查:19-31 = -12,被12整除,确实同余)
Python 版本
# Python 的 % 运算符对负数返回非负余数,更符合数学定义
# 因此代码更简洁
a, b, m = map(int, input("请输入三个整数a, b, m(用空格分隔):").split())
if m <= 0:
print("模数必须为正整数!")
else:
# 判断同余:检查 (a-b) % m 是否为0
if (a - b) % m == 0:
print(f"{a} ≡ {b} (mod {m}) 成立")
else:
print(f"{a} ≡ {b} (mod {m}) 不成立")
# 比较余数(Python的%直接得到非负余数,无需额外处理)
ra = a % m
rb = b % m
print(f"a的余数:{ra},b的余数:{rb}")
if ra == rb:
print("余数相同,所以同余。")
else:
print("余数不同,所以不同余。")
# 演示三个性质
print("\n=== 演示同余性质 ===")
# 自反性
if (a - a) % m == 0:
print("自反性:a ≡ a (mod m) 成立")
# 对称性
if (a - b) % m == 0:
print("对称性:由a≡b可推出b≡a,成立")
# 传递性:需要第三个整数c
c = int(input("请输入另一个整数c,用于演示传递性:"))
if (a - b) % m == 0 and (b - c) % m == 0:
print(f"传递性成立:{a} ≡ {c} (mod {m})")
else:
print("不满足a≡b且b≡c,无法演示传递性。")
运行示例与C++类似。
五、总结与相关指引
记住这三点
- 同余就是“余数相同”:a ≡ b (mod m) ⇔ a 和 b 除以 m 余数相同 ⇔ m 整除 a-b。
- 同余是等价关系:满足自反、对称、传递,可以把整数分成 m 个剩余类。
- 编程时小心负数:在C++里用
(a % m + m) % m把余数规范化,或者直接用(a-b) % m == 0判断整除。
学了同余,下一步学什么?
- 模运算规则:同余的加减乘、幂运算(比如快速幂)。
- 大数取模:如何处理超大数的余数(如求 2^1000 mod 7)。
- 欧几里得算法与贝祖定理:求最大公约数,解一次同余方程。
- 中国剩余定理:同时满足多个同余条件的数(比如“一个数除以3余2,除以5余3,除以7余2”)。
同余是打开数论大门的钥匙,以后学密码学(RSA)、循环队列、哈希函数都会用到它。希望你能通过生活中的周期现象,彻底理解这个有趣的概念!
例题精讲
对于整数a,b和正整数m,a≡b(mod m)的定义是?
若a≡b(mod m)且c≡d(mod m),则a-c≡b-d(mod m)一定成立。
完成以下函数,判断整数a和b是否模m同余(m>0)。
bool congruent(int a, int b, int m) {
return ___;
}下列同余性质中,哪一个不一定成立?
若a≡b(mod m),则对任意正整数n,有a^n≡b^n(mod m)。