Python 中关于负数的模除

我在 Python 中发现了一些关于负数的奇怪行为:

>>> -5 % 4
3

有人能解释一下这是怎么回事吗?

113263 次浏览

与 C 或 C + + 不同,Python 的模运算符(%)总是返回与分母(除数)具有相同符号的数字。你的表达式得出3因为

(-5)/4 = -1.25—— > 楼层(-1.25) = -2

(-5)% 4 = (-2 × 4 + 3)% 4 = 3.

它之所以选择 C 行为,是因为非负结果通常更有用。一个例子是计算工作日。如果今天是星期二(第二天) ,那么前几天是星期几?在 Python 中,我们可以使用

return (2 - N) % 7

但是在 C 中,如果 N≥3,我们得到一个负数,这是一个无效的数,我们需要手动修正它,加上7:

int result = (2 - N) % 7;
return result < 0 ? result + 7 : result;

(如何确定不同语言的结果符号,请参阅 http://en.wikipedia.org/wiki/Modulo_operator。)

下面是吉多·范罗苏姆的一个解释:

Http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html

本质上,a/b = q 余数 r 保持 b * q + r = a 和0 < = r < b 的关系。

模数,4的等价类:

  • 0:0,4,8,12... 和 -4,-8,-12..。
  • 1:1,5,9,13... 和 -3,-7,-11..。
  • 2:2,6,10... 和 -2,-6,-10..。
  • 3:3,7,11... 和 -1,-5,-9..。

这里有一个到 负数模的行为的链接。(是的,我谷歌了一下)

没有一种最好的方法来处理整数除法和带有负数的 mod。如果 a/b(-a)/b的大小相同,符号相反,那就太好了。如果 a % b确实是模 b 就好了。因为我们真正想要的是 a == (a/b)*b + a%b,所以前两个是不兼容的。

保留哪一个是一个困难的问题,双方都有争议。C 和 C + + 将整数除法向零(所以是 a/b == -((-a)/b)) ,很明显 Python 没有这样做。

正如前面所指出的,Python 模块对其他语言的约定造成了 理由充分异常。

这使得负数具有无缝的行为,特别是当与 //整数除法运算符结合使用时,就像 %模通常所做的那样(如在 math.迪莫德中) :

for n in range(-8,8):
print n, n//4, n%4

制作:

 -8 -2 0
-7 -2 1
-6 -2 2
-5 -2 3


-4 -1 0
-3 -1 1
-2 -1 2
-1 -1 3


0  0 0
1  0 1
2  0 2
3  0 3


4  1 0
5  1 1
6  1 2
7  1 3
  • Python %总是输出零或正值 *
  • Python//总是向负无穷大转变

* ... 只要正操作数是正的,另一方面 11 % -10 == -9

我还认为这是巨蟒的一种奇怪行为。事实证明,我没有很好地解决除法问题(在纸上) ; 我给商的值是0,给余数的值是 -5。糟糕... 我忘了整数的几何表示法。通过回想数字行给出的整数的几何形状,可以得到商和余数的正确值,并检查 Python 的行为是否正常。(尽管我认为你很久以前就已经解决了你的问题)。

同样值得一提的是,python 中的除法也不同于 C: 考虑一下

>>> x = -10
>>> y = 37

在 C 中,你期望得到结果

0

Python 中的 x/y 是多少?

>>> print x/y
-1

而% 是模的-不是余数! 而 x% y 在 C 中的收益

-10

蟒蛇的产量。

>>> print x%y
27

你可以得到 C 中的两个

分工:

>>> from math import trunc
>>> d = trunc(float(x)/y)
>>> print d
0

其余部分(使用上面的除法) :

>>> r = x - d*y
>>> print r
-10

这个计算可能不是最快的,但是对于任何 x 和 y 的符号组合来说,都可以得到与 C 相同的结果,而且避免了条件语句。

巨蟒中,模运算符是这样工作的。

>>> mod = n - math.floor(n/base) * base

所以结果是(就你而言) :

mod = -5 - floor(-1.25) * 4
mod = -5 - (-2*4)
mod = 3

而其他语言,如 C,JAVA,JavaScript,则使用截断代替地板。

>>> mod = n - int(n/base) * base

结果是:

mod = -5 - int(-1.25) * 4
mod = -5 - (-1*4)
mod = -1

如果需要更多关于 python 舍入的信息,请阅读 这个

其他的答案,特别是被选中的那个,已经很清楚地回答了这个问题。但是我想介绍一种图形化的方法,这种方法也可能更容易理解,同时还有在 python 中执行常规数学模块的 python 代码。

傻瓜用的 Python 模块

模函数是一个方向函数,它描述了在数学跳跃之后,我们需要向前或向后移动多少,这些跳跃是在 X 轴上的无限数字除法过程中产生的。 假设你在做 7%3

enter image description here

所以在向前的方向上,你的答案是 + 1,但是在向后的方向上-

enter image description here

你的答案是 -2,两个都是正确的 数学上来说

类似地,对于负数也有2个模。对于例如: -7%3,可以导致 -1或 + 2,如图所示-

enter image description here

前进方向


enter image description here

后退


在数学中,我们选择向内跳跃,即正数向前跳跃,负数向后跳跃。

但是在 Python 中,我们对所有的正模操作都有一个前进的方向

>>> -5 % 4
3


>>> 5 % 4
1

下面是 python 中向内跳转类型 module 的 python 代码:

def newMod(a,b):
res = a%b
return res if not res else res-b if a<0 else res

这会给

>>> newMod(-5,4)
-1


>>> newMod(5,4)
1

很多人会反对向内跳的方法,但我个人的意见是,这个更好! !

你可使用:

result = numpy.fmod(x,y)

它将保持标志,见 Numpy fmod ()文档

@ Deekshant 用可视化很好地解释了它。另一种理解% (模)的方法是问一个简单的问题。

在 X 轴上可以被除数整除的距离被除数最近的小数是多少?

让我们来看几个例子。

5 % 3

5是红利3是除数。如果你问上面的问题3是最小的可被除数整除的数。也就是5-3 = 2。对于正红利,最小数总是在红利的右边。

-5 % 3

可被3整除的最小数是 -6,因此 ans 是 -5-(-6) = 1

-5 %4

可被4整除的最小数是 -8,所以 ans 是 -5-(- 8) = 3

Python 用这种方法回答每一个模表达式。希望您能够理解接下来表达式将如何执行。

这就是模数的用途。如果你通过一系列数字做一个模,它会给出一个值的循环,比如:

ans = num % 3
Num 答案是肯定的
3 0
2 2
1 1
0 0
-1 2
-2 1
-3 0