模运算的基本性质
问题引入
今天在刷题的时候遇到了一个问题,为什么((x + y) % p + y) % p
等价于(x + y + y) % p
?
这就要了解模运算的性质,之前其实已经学过模运算的分配律、结合律等等了,但是太懒了就没有总结,看来还是得总结一下。。。
模运算的基本性质
首先了解预备知识:
- 模可以加、减、乘、幂,但不能直接除。
- 在模运算下,你可以“先模后加、减、乘、幂”,也可以“合起来运算完完再模”,因为加、减、乘、幂和模运算是兼容的,换句话说:模可以出现在运算的任何位置。
加法性质
(a % m + b % m) % m ≡ (a + b) % m
模可以分配到加法里,也可以从加法里提取出来,这就是分配律和结合律
减法性质
错误写法: (a % m - b % m) % m ≡ (a - b) % m
注意这样有可能得到负数,比如(3 % 5 - 7 % 5) % 5 的结果本来是 (3 - 2) % 5 = 1
但是如果根据公式来计算
(3 % 5 - 7 % 5) % 5 = (3 - 7) % 5 = -4 % 5 = 1
乘法性质
(a % m * b % m) % m ≡ (a * b) % m
幂运算性质(可递推)
(a^k) % m ≡ ((a % m)^k) % m
📌这是快速幂的基础
传递性(等价替换)
a ≡ b (mod m),则 f(a) ≡ f(b) (mod m)
只要 f 是由加减乘幂构成的表达式就可以替换
同余传递性
如果 a ≡ b (mod m),且 c ≡ d (mod m)
则 a + c ≡ b + d (mod m)
a - c ≡ b - d (mod m)
a * c ≡ b * d (mod m)
取模分配律(可以提前或延后取模)
(a + b) % m ≡ ((a % m) + (b % m)) % m
(a - b) % m ≡ ((a % m) - (b % m) + m) % m
(a * b) % m ≡ ((a % m) * (b % m)) % m
一个防负数的技巧(模加模)
((a % m) + m) % m
负数取模的结果
1. 商向零取整 (Truncated Division)
- 适用语言: C/C++/Java/JavaScript
- 规则: 结果的符号与被除数(第一个数)相同
- 公式:
a % b = a - b × trunc(a / b)
(trunc
表示向零取整,即直接截断小数部分)
-7 % 3 = -1 // 因为 -7 ÷ 3 = -2.333... → trunc(-2.333) = -2 → -7 - (3 × -2) = -1
7 % -3 = 1 // 7 ÷ -3 = -2.333... → trunc(-2.333) = -2 → 7 - (-3 × -2) = 1
-7 % -3 = -1 // -7 ÷ -3 = 2.333... → trunc(2.333) = 2 → -7 - (-3 × 2) = -1
2. 商向负无穷取整 (Floored Division)
-
适用语言: Python/Ruby
-
规则: 结果的符号与除数(第二个数)相同,且结果非负
-
公式:
a % b = a - b × floor(a / b)
(floor
表示向下取整)
-7 % 3 = 2 # 计算过程:# -7 ÷ 3 = -2.333... → floor(-2.333) = -3# -7 - (3 × -3) = 27 % -3 = -2 # 7 ÷ -3 = -2.333... → floor(-2.333) = -3# 7 - (-3 × -3) = -2-7 % -3 = -1 # -7 ÷ -3 = 2.333... → floor(2.333) = 2# -7 - (-3 × 2) = -1
3. 数学上的模运算(同余)
数学中,模运算的结果通常是非负的(与Python规则一致),例如:
-7 ≡ 2 (mod 3)
因为 -7 + 3 × 3 = 2
floor
和 trunc
函数对比
1. 定义对比
函数 | 行为描述 | 数学表示 |
---|---|---|
floor | 向负无穷方向取整(返回 ≤ 原数的最大整数) | floor(x) = ⌊x⌋ |
trunc | 向零方向取整(直接截断小数部分) | trunc(x) = { ⌊x⌋ (x≥0), ⌈x⌉ (x<0) } |
2. 具体行为
(1) 正数的情况
输入:x = 3.7
floor(3.7) = 3
trunc(3.7) = 3
(两者结果相同)
(2) 负数的情况
输入:x = -2.3
floor(-2.3) = -3
(向更小的整数方向取整)trunc(-2.3) = -2
(直接截断小数部分,向零靠近)
3. 关键区别
- 对于正数:两者行为一致
- 对于负数:
floor
:向更小的整数取整(-2.3 → -3)trunc
:向零取整(-2.3 → -2)
4. 应用场景
floor
:用于需要保证结果≤原数的场景(如分页计算)trunc
:用于需要快速截断小数部分的场景(如金融取整)
注:⌊ ⌋表示向下取整,⌈ ⌉表示向上取整
回到问题
为什么((x + y) % p + y) % p
等价于(x + y + y) % p
?
原式((x + y) % p + y) % p
相当于
- 先对 (x + y) 取了模
- 再加一个 y
- 再取模
这就等价于把所有加数凑一起 (x + y + y) 后整体取模,因为(x + y) 取模之后的结果再取模结果是不变的,(x + y)不管取几次模结果都是一样的,只要有一次取模就行了,内层的取模运算我们就可以去掉,所以可以转换成(x + y + y) % p
。
这样以来了解本质后,就可以在原来的加法性质
进一步扩展,不仅仅只有 (a % m + b % m) % m
的时候才等价于 (a + b) % m
。同样地 (a % m + b ) % m
、(a + b % m) % m
都是等价于 (a + b) % m
的。