CC++ & Algorithm

同余的概念与基本性质

困难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++类似。


五、总结与相关指引

记住这三点

  1. 同余就是“余数相同”:a ≡ b (mod m) ⇔ a 和 b 除以 m 余数相同 ⇔ m 整除 a-b。
  2. 同余是等价关系:满足自反、对称、传递,可以把整数分成 m 个剩余类。
  3. 编程时小心负数:在C++里用 (a % m + m) % m 把余数规范化,或者直接用 (a-b) % m == 0 判断整除。

学了同余,下一步学什么?

  • 模运算规则:同余的加减乘、幂运算(比如快速幂)。
  • 大数取模:如何处理超大数的余数(如求 2^1000 mod 7)。
  • 欧几里得算法与贝祖定理:求最大公约数,解一次同余方程。
  • 中国剩余定理:同时满足多个同余条件的数(比如“一个数除以3余2,除以5余3,除以7余2”)。

同余是打开数论大门的钥匙,以后学密码学(RSA)、循环队列、哈希函数都会用到它。希望你能通过生活中的周期现象,彻底理解这个有趣的概念!

例题精讲

1单选题

对于整数a,b和正整数m,a≡b(mod m)的定义是?

Am整除a-b
Ba除以m的余数等于b
Ca与b相等
Dm是a和b的最大公约数
2判断题

若a≡b(mod m)且c≡d(mod m),则a-c≡b-d(mod m)一定成立。

3填空题
完成以下函数,判断整数a和b是否模m同余(m>0)。
bool congruent(int a, int b, int m) {
    return ___;
}
4单选题

下列同余性质中,哪一个不一定成立?

A若a≡b(mod m),则a+c≡b+c(mod m)
B若a≡b(mod m),则ac≡bc(mod m)
C若a≡b(mod m)且c≡d(mod m),则ac≡bd(mod m)
D若a≡b(mod m),则a/c≡b/c(mod m)(c≠0)
5判断题

若a≡b(mod m),则对任意正整数n,有a^n≡b^n(mod m)。