定理
定理一: k, g
为任意整数, d
为任意 不为g
的倍数, 则: k*g + d
一定不是 g
的倍数.
… 即一个数a
, (他的倍数) + (不是他的倍数) = (不是他的倍数)
… (但, k + d
可能是g
的倍数. 比如: 2 + 3 = 5
)
定理二:
a % b = a − ⌊ a b ⌋ ∗ b a \% b = a - {\lfloor \frac{a}{b} \rfloor} * b a%b=a−⌊ba⌋∗b, 该公式非常常用!
a % b = ( k 1 % k 2 ) ∗ g = ( k 1 − ⌊ k 1 k 2 ⌋ ∗ k 2 ) ∗ g a \% b = (k1 \% k2) * g = ( k1 - {\lfloor \frac{k1}{k2} \rfloor} * k2) * g a%b=(k1%k2)∗g=(k1−⌊k2k1⌋∗k2)∗g
且g = gcd(a,b), a=k1*g, b=k2*g
注意, 这里的下取整, 是严格的下取整, 不是 (计算机的向0取整), 因为 我们定义%
为: 非负取模
… 比如, -5 % 2 = 1 = -5 - (-5/2) * 2
, 显然这个-5 / 2
应该等于-3
, 不是向零取整.
… 除非你把%
定义为 (向0取模: 正数正模, 负数负模),
计算机在进行a%b
时, 就是执行的 a - (a/b)*b
, 因为(计算机的/
是: 向0取整的除法), 因此, 计算机的取模, 是(向0取模)
取模
计算机中的取模%
, 本质是:
Ll_ Mod( Ll_ _a, Ll_ _b){
return _a - Divide( _a, _b) * _b;
}
这也是根据的定理二.
你可以当做是: a % b
, 就调用了这个函数.
因此, 研究%
的行为 (是非负取模, 还是非正取模, 还是向0取模), 就必须研究 整除/
的行为
Divide
整数的取整, 分类有: (向0取整 cpp默认
) (向下取整) (向上取整)
-
一般, 模数都会是正数.
因此, 我们这里只考虑: b > 0 b > 0 b>0的情况. (b
如果是0, 是非法的, 除0错误)
… 这里多说一句, 如果Divide
是 (向0取整), 则有:Divide( a, -b) == -1 * Divide( a, b)
… 也就是意味着, 此时Mod( a, b) 恒等于 Mod( a, -b)
… 因为:Divide( a, -b) = -1 * Divide( a, b)
, 但这只发生在 Divide是 (向0取整)的情况
… 比如:5 / 2 = 2
5 / -2 = -2
(这个性质很重要, 可能计算机使用(向0取整), 是因为他)
… 但是, 当Divide是 (向上/ 向下取整)时,Divide( a, -b) = -Divide( a, b)
并不成立.
… 比如: (下取整:5 / 2 = 2
5 / -2 = -3
) (上取整:5 / 2 = 3
5 / -2 = -2
总之, 以下分析中, 都假设b > 0
-
当
Divide
是 (向0取整)时, 此时,Mod
呈现的是: (向0取模)
m = a % b
: (如果a >= 0
, 则m = [0, b)
) (否则a < 0
, 则m = (-b, 0]
) 从那个公式可推出
比如:5 % 2 = 1
-5 % 2 = -1
, 有正有负 -
当
Divide
是 (向下取整)时, 此时,Mod
呈现的是: (正取模)
m = a % b
: (不管a
是正负, 都会有m = [0, b)
) 从那个公式可推出
比如:5 % 2 = 1
-5 % 2 = 1
, 全部为正 -
当
Divide
是 (向上取整)时, 此时,Mod
呈现的是: (负取模)
m = a % b
: (不管a
是正负, 都会有m = (-b, 0]
) 从那个公式可推出
比如:5 % 2 = -1
-5 % 2 = -1
, 全部为负
上, 下, 向0取整 的代码
//> 向0整除
Ll_ Divide( Ll_ _a, Ll_ _b){
return _a / _b;
}
//> 向上取整
Ll_ Divide( Ll_ _a, Ll_ _b){
return ( _a + ( _b - 1)) / _b;
}
//> 向下取整
Ll_ Divide( Ll_ _a, Ll_ _b){
//* 下取整, 暂时还没想到更简洁的算法.
//* 目前的做法是: 如果结果为正 或 可以整除, 则使用(向0取整); 否则, 向0取整 - 1
//* 这里的`结果`, 是指的`a/b`这个结果 是个小数 精确的指. 不是整除.
//* 但肯定不是: 类似上取整的`return ( _a - ( _b - 1)) / _b`, 不要理想当然
if( ( _a >= 0 && _b >= 0)
|| ( _a <= 0 && _b <= 0)){
return _a / _b;
}
if( _a