热门问题
时间线
聊天
视角

模除

来自维基百科,自由的百科全书

模除
Remove ads
Remove ads

模除(英语:modulo 有时也称作 modulus),又称模数取模取模运算等,它得出一个数除以另一个数的余数。给定两个正数:被除数除数(通常缩写为),得到的是使用欧几里得除法时的余数所得之数被称为小数部份。

Thumb
  商数 () 和   馀数 () 作为被除数 () 的函数时的图像。左侧是除数为正的情况,右侧除数为负。从上至下分别使用了:向零取整、向下取整和欧几里得除法

举个例子:计算表达式得到,因为的商为而余数为;而得到,因为的商为而余数为;做除法不能整除时得到是有理数,平常使用的计算器采用了有限数位十进制表示法,它以小数点分隔整数部份和小数部份,比如

虽然通常情况下模除的被除数和除数都是整数,但许多计算系统允许其他类型的数值运算,比如对浮点数取模。在所有计算系统中当被除数和除数都是正数时,结果的余数的范围为;而经常是未定义的,在编程语言里可能会导致除以零错误。当被除数或除数为负数时,不同的编程语言对结果有不同的处理。

Remove ads

各种定义

在数学中,取模运算的结果就是欧几里德除法余数。当然也有许多其他的定义方式。计算机和计算器有许多种表示和储存数字的方法,因此在不同的硬件环境下、不同的编程语言中,取模运算有着不同的定义。

几乎所有的计算系统中,除以得到商和余数均满足以下式子:

1

即使如此,当余数时,余数的符号仍然是有歧义的:余数非时,它的符号有两种选择,一个是正号而另一个是负号[a]。通常情况下,在数论中总是选择正余数。但在编程中,选择余数的符号依赖于编程语言和被除数或除数的符号。在编程语言所定义的整数模除中,ISO/IEC标准Pascal[1]ALGOL 68[2],在计算出的余数r为负数时,返回正数作为结果[b];另一些编程语言如ISO/IEC C90,当被除数或除数是负数时,C90标准并没有做具体的规定,而是留给编译器去定义并实现[3]。在大多数系统中是未定义的,虽然有些系统定义它就等于。更多详情参见后续章节表格。

  • 很多取模的实现都使用了“截断除法”(英语:truncated division),商经由截尾函数来定义,商向零取整,结果等于普通除法所得的小数靠近方向的第一个整数:

    余数和被除数符号一致:

  • 高德纳定义的“下取整除法”(英语:floored division[4],商经由下取整函数来定义,商总是向负无穷取整,即使商已经是负数:

    余数和除数符号一致:

  • Raymond T. Boute使用的欧几里得除法定义中[5],要求满足,在这种情况下:

    这里的符号函数,余数总是非负数:

  • Common Lisp的round函数和IEEE 754英语IEEE 754-1985使用“修约除法”(英语:rounded division),商经由修约函数约半成偶)来定义为:

    当商为偶数时,余数范围是;当商为奇数时,余数范围是

  • Common Lisp的ceiling函数使用“上取整除法”(英语:ceiling division),商经由上取整函数定义为:

    余数与除数有相反的符号:

Remove ads

记号

一些计算器有取模mod()按钮,很多编程语言里也有类似的函数,通常像mod(a, n)这样。有些语言也支持在表达式内使用%modMod作为取模或取余操作符,比如a % na mod n

在一些没有mod()函数的环境中或许可使用等价的:a - (n * int(a/n))。这里的int()函数事实上等价于截断函数。

性质及恒等式

一些取模操作,经过分解和展开可以等同于其他数学运算。这在密码学的证明中十分有用,例如:迪菲-赫尔曼密钥交换

  • 恒等式:
    • 对所有的正数 有:
    • 如果 是一个质数,且不为 因数,此时由费马小定理有:
  • 逆运算:
    • .
    • 表示模反元素。当且仅当 互质时,等式左侧有定义:
  • 分配律:
  • 除法定义:仅当式子右侧有定义时,即 互质时有:,其他情况为未定义的。
  • 乘法逆元:.
Remove ads

编程语言实现

更多信息 语言, 算符 ...

此外,很多计算机系统提供divmod功能,它同时产生商和余数。例子x86架构IDIV指令,C编程语言的div()函数,和Pythondivmod()函数。

Remove ads

常见错误

当取模的结果与被除数符号相同时,可能会导致意想不到的错误。

举个例子:如果需要判断一个整数是否为奇数,有人可能会测试这个数除 2 的余数是否为 1:

bool is_odd(int n) {
    return n % 2 == 1;
}

但在一个取模结果与被除数符号相同的编程语言里,这样做是错的。因为当被除数 n 是奇数且为负数时, 得到 −1,此时函数返回“假”。

一种正确的实现是测试取模结果是否为 0,因为余数为 0 时没有符号的问题:

bool is_odd(int n) {
    return n % 2 != 0;
}

或者考虑余数的符号,有两种情况:余数可能为 1 或 -1。

bool is_odd(int n) {
    return n % 2 == 1 || n % 2 == -1;
}
Remove ads

性能问题

可以通过依次计算带余数的除法实现取模操作。特殊情况下,如某些硬件上,存在更快的实现。 例如:2 的 n 次幂的模,可以通过逐位与运算实现:

x % 2n == x & (2n - 1)

例子,假定 x 为正数:

x % 2 == x & 1
x % 4 == x & 3
x % 8 == x & 7

在进行位操作比取模操作效率更高的设备或软件环境中,以上形式的取模运算速度更快。[56]

编译器可以自动识别出对 2 的 n 次幂取模的表达式,自动将其优化为 expression & (constant-1)。这样可以在兼顾效率的情况下写出更整洁的代码。这个优化在取模结果与被除数符号一致的语言中(包括 C 语言)不能使用,除非被除数是无符号整数。这是因为如果被除数是负数,则结果也是负数,但 expression & (constant-1) 总是正数,进行这样的优化就会导致错误,无符号整数则没有这个问题。

Remove ads

用途

  • 取模运算可用于判断一个数是否能被另一个数整除。对 2 取模即可判断整数的奇偶性;从 2 到 取模则可判断一个数是否为质数。
  • 进制之间的转换。
  • 用于求取最大公约数的辗转相除法使用取模运算。
  • 密码学中的应用:从古老的凯撒密码到现代常用的RSA椭圆曲线密码,它们的实现过程均使用了取模运算。

参见

脚注

参考文献

Loading content...
Loading related searches...

Wikiwand - on

Seamless Wikipedia browsing. On steroids.

Remove ads