数学问题 - %运算符
%运算符称为求模运算符,通俗的讲即求一个数被另一个数除后剩余的余数。
%运算符的用法非常简单,用形如 a % b 的语句来调用该运算符。其中变量 a,b 必须为整型变量,例如 int、short 等,而不能为浮点数。且 b 变量必须为非零值,若出现模零错误,程序会因为该异常意外终止。在评判系统中表现为 评判系统给出了运行时错误,程序未运行完成就异常终止。所以若出现评判系统返回了运行时错误,可以试着检查是否可能出现模零错误。
以 a % b 语句为例,先不加说明的指出该运算的特点。其运算在行为上 好像是按如下步骤进行的,首先计算出 a 的绝对值被 b 的绝对值除所得的余数, 再使该余数的符号与 a 保持一致。
即若 a 为正数,则该表达式结果必为非负数(可 能为 0);若 a 为负数,则表达式结果必为非正数(可能为 0)。
而表达式结果与 b 的符号没有直接关系,即 a % -b 与 a % b 的结果相同。
通过求模运算符求得的余数存在着负数的可能。而这与数论中关于余数的定义是不相符的。数论指出,余数的取值范围为从 0 到除数减 1,即在 a % b 表达式中,其符合数论规定的的结果取值范围应是 0 到 b - 1。
%运算符 的运算特性仅保证余数的绝对值在如上所述的范围内,而不保证不会出现负数, 出现负余数也为下一步操作带来诸多不便。
如利用求模运算来计算数组下标,而负数组下标是不能使用的。那么必须保证表达式求得的余数在数论定义的区间范围内,只需在该负的余数上再加上除数再对除数求一次余即可。
r = a % b
a = k * b + r
r 即为要求的余数,它是由第一式求得的。同时它应符合第二式中关于余数的原始定义,即 a 将等于某个整数k与 b 的积再加上余数 r,由于 C/C++的% 算符特点,当 a 为负数时 r 很可能出现负数(或为 0),为了得到正确范围内 的余数,可以对该式作如下变形(这里假设 b 大于 0,否则取绝对值):
a = ( k - 1) * b + r + b
若 r 非零,该式同样符合关于余数的相关定义,但是它的余数部分(r + b) 将不再为负数,而是落在之前讨论的、数论规定的范围[0,b-1]内,即由 0 至 b-1。而这个符合要求的新余数即为原负余数加上 b,正是利用该方法来使余数落入所需的区间内。但是读者还需特别注意,即使被除数为负数,余数也是有可能为 0 的(刚好整除),那么假如与对待其他负余数一样为其加上余 数以期其能够落入我们需要的区间内,这将会适得其反(将会使余数等于 b)。 所以可以统一的对取得的余数加上除数后再对该和求模,即:
r' = (r + b) % b
这样做,不仅能对可能出现的负余数做适当的修正,同时对出现的零和正余数也不会改变它们的值。 另外,顺便来看一下为什么在%运算中余数的值看起来好像与除数的符号无关。假设 b 为正数,利用 r = a % b求得的余数 r 将会满足 下式:
a = k * b + r
其中 r 绝对值的取值范围为从 0 到 b-1,其符号保持与 a 一致(除非 r 为 0)。 若此时,利用 r = a % -b来求得的余数 r’又会满足下式
a = k' * (-b) + r'
同样,其中 r 的绝对值取值范围为从 0 到 b-1,其符号保持与 a 一致。比较两式即能发现,当 k’ 等于-k 时两式成立,且 r’与 r 相同。所以用%运算符求得的余数看起来与除数的符号无关。
最后总结%运算的科学解释,以式r=a%b为例,其所求得的r满足a=k*b+r,其中k为某整数,r的绝对值范围为[0,|b|-1],其符号与a保持一致,除非其为0。
(a*b)%c=(a%c*b%c)%c
(a+b)%c=(a%c+b%c)%c
利用如上规律可以有效避免大数求模中的溢出问题。
本文介绍了C/C++中的%运算符,即求模运算,用于计算一个整数被另一个整数除后的余数。文章详细阐述了%运算符的使用规则,包括模零错误、负数余数的情况,以及如何处理负余数以确保其落在0到除数-1的范围内。此外,还探讨了除数正负对结果的影响,并提供了科学解释和避免溢出的方法。
1904

被折叠的 条评论
为什么被折叠?



