为什么Python的取模操作符 (%) 不符合欧几里得定义?
欧几里得的定义是这样的:
给定两个整数 a 和 b,且 b 不等于 0,存在唯一的整数 q 和 r,使得 a = bq + r,并且 0 ≤ r < |b|,这里 |b| 表示 b 的绝对值。
根据下面的观察:
>>> -3 % -2 # Ideally it should be (-2 * 2) + 1
-1
>>> -3 % 2 # this looks fine, (-2 * 2) + 1
1
>>> 2 % -3 # Ideally it should be (-3 * 0) + 2
-1
看起来 %
这个运算符的规则有些不同。
我的问题是:
我该如何理解 Python 中取模运算符的行为?我对其他语言中 %
运算符的工作方式也不太了解。
1 个回答
整数除法和取模运算的行为在一篇关于Python历史的文章中有解释,具体内容可以参考这篇文章:为什么Python的整数除法向下取整。我来引用相关的部分:
如果其中一个操作数是负数,结果会向下取整,也就是朝着负无穷的方向取整:
>>> -5//2 -3 >>> 5//-2 -3
这让一些人感到困扰,但其实有很好的数学原因。整数除法运算(
//
)和取模运算(%
)是相互关联的,并且满足一个很好的数学关系(所有变量都是整数):
a/b = q
,余数为r
满足以下条件:
b*q + r = a
且0 <= r < b
(假设
a
和b
都是大于等于0的)。如果你想让这个关系适用于负的
a
(保持b
为正),你有两个选择:如果你把q
向零截断,r
会变成负数,这样不等式就变成了0 <= abs(r)
;否则,你可以把q
向负无穷取整,这样不等式保持为0 <= r < b
。在数学数论中,数学家们总是更倾向于后者的选择(可以参考维基百科)。对于Python,我也做了同样的选择,因为在某些有趣的取模运算中,
a
的符号并不重要。顺便提一下,对于负的
b
,一切都会翻转,不等式变为:0 >= r > b.
换句话说,Python在某些情况下选择了“打破”欧几里得定义,以获得在“有趣的情况下”更好的表现。特别是,负的a
被认为是有趣的,而负的b
则被认为不那么重要。这是一个完全任意的选择,不同编程语言之间并不一致。
需要注意的是,许多常见的编程语言(如C、C++、Java等)并不满足欧几里得不等式,往往在比Python更多的情况下(例如,即使b
是正数时)。其中一些语言甚至对余数的符号没有任何保证,留给实现者自行定义。
附带说明一下:Haskell提供了两种类型的取模和除法。标准的欧几里得取模和除法分别称为rem
和quot
,而向下取整的除法和“Python风格”的取模则称为mod
和div
。