C++基本语法与标准库 - 模运算
这是习惯了数学语言后使用 C 语言的一个大坑!!!切记切记!!!
最好对程序使用的每一处模运算,都对参与运算的两个数进行详细讨论,以验证程序在模运算上的正确性。
取整风格
在计算机实现上,有多种不同的取整风格。
-
向零取整。即向坐标轴 \(0\) 的方向,取最近的整数。
-
向上取整。也就是向 \(+\infty\) 取整,即取不小于结果的最小整数。
-
向下取整。也就是向 \(-\infty\) 取整,即取不大于结果的最大整数。
C 语言除法取整与模运算
C 语言整型的除法的结果需要取整,例如 5 / 3 = 1
。C 语言中的取整方式是向零取整。所以,C 语言中计算 \(-5\) 除以 \(3\) ,因为实数结果约为 \(-1.67\),再将结果向零取整,得到结果为 \(-1\)。
数学上的模运算
接下来介绍 C 语言的模运算,在展开介绍以前,看看数学上的模运算的定义。
定理 整数环中的带余除法
任给 \(a,b \in \mathbb{Z}\),\(b \neq 0\),则存在唯一的一对整数 \(q,r\),使得
\[a = b q + r, \quad 0 \leqslant r < |b| \]可知,\(r\) 存在唯一,将 \(r\) 称为余数。
我们在此文将上式表示的过程不严谨的写成 \(a \bmod b = r\)。
C语言中的模运算
取模运算也叫取余运算,在 C 中用 \(\%\) 来表示。在 C 中计算为
注意此处的 \(x/y\) 是指程序上的 x/y
,即 C 语言的整型除法,而又知其取整方式是向零取整。
例子
数学上
C 语言中:
进一步讨论
我们从上文已经知道了两者之间的差别。为了以后使用数学中余数相关定理更加方便,我们进一步讨论这两者之间定量的差异。
考虑参与运算的两个数 \(x\) 和 \(y\),先讨论平凡的情况,即当它们有一个为 \(0\) 时,容易验证 \(\bmod\) 与 \(\%\) 是一致的(小心 \(y=0\),此时对于两个运算是无意义)。
-
当 \(x\) 为正,\(y\) 为正,可以验证两者是一致的。
-
当 \(x\) 为正,\(y\) 为负,可以验证两者是一致的。
-
当 \(x\) 为负,\(y\) 为正,两者不一致。原因如下
- 当 \(x\) 为负,\(y\) 为负,两者不一致。原因如下
在程序中利用上述讨论的各种情况可保证程序在使用模运算时的正确性。
我们一般遇到的情况是 \(x\) 可能为正负,\(y\) 为正,在算法题中常用下述代码保证使用 \(\%\) 来达到 \(\bmod\) 的运算效果。
(x % y + y) % y