When is the difference between quotRem and divMod useful?
从Haskell报告:
The quot, rem, div, and mod class
methods satisfy these laws if y is
non-zero:
1 2
| (x ` quot` y )*y + (x ` rem` y ) == x
(x ` div` y )*y + (x ` mod` y ) == x |
quot is integer division truncated
toward zero, while the result of div
is truncated toward negative infinity.
号
例如:
1 2 3 4
| Prelude > (-12) ` quot` 5
-2
Prelude > (-12) ` div` 5
-3 |
有什么例子可以说明截断结果的不同之处?
许多语言都有一个"mod"或"%"运算符,它用截断后的0来给出余数,例如C、C++和Java,很可能是C。
1 2 3
| (-11)/5 = -2
(-11)%5 = -1
5*((-11)/5) + (-11)%5 = 5*(-2) + (-1) = -11. |
Haskell的quot和rem意在模仿这种行为。我可以想象,在某些人为的情况下,与某些C程序的输出兼容可能是可取的。
haskell的div和mod,随后是python的/和%,遵循数学家(至少是数理论家)的惯例,总是截断除法(不是朝0——朝负无穷大),使余数总是非负的。因此在python中,
1 2 3
| (-11)/5 = -3
(-11)%5 = 4
5*((-11)/5) + (-11)%5 = 5*(-3) + 4 = -11. |
。
haskell的div和mod遵循这种行为。
- "这样余数总是非负的"从技术上讲,EDOCX1的符号(0)跟在第二个操作数的符号后面。
- 嗯,你说得对。我不明白这个设计决定…
- 只有当且仅当x = q*y + r时,才能维护(q,r) = divMod x y的财产。举个例子,它的工作原理很聪明。
- @卢基:不,这不能解释。你可以一直用x=q*y+r表示r非负;例如,如果divMod 11 (-5) = (-2, 1)(而不是(-3,-4)),你仍然可以用"11=(-2)*(-5)+1"。因此,您的条件并不强制mod的符号跟随第二个操作数。顺便说一句,对于Quoterm,x=q*y+r总是正确的,并且总是有无穷多对(q,r),这样x=q*y+r(并且这些对中正好有两对有r_q,除非r=0给出一个解,只有一对)。
- 嗯,是的。也许mod正在补偿div中的一些相关设计决策?不确定。。。
- 实际上,虽然C99和C++ 11确实指定了截断到-0的定义,但早期版本让它实现了定义,因此"与C兼容"是其他语言或程序与截断到0一起使用的一个不太可能(或至少是坏的)原因。
这不完全是您问题的答案,但是在x86上的ghc中,int上的Quoterm将编译成一条机器指令,而divmod则要做更多的工作。所以如果你在一个速度临界区,只研究正数,Quoterm就是你要做的。
- 对于解决spoj primes,使用rem而不是mod使我的测试文件在4.758s而不是5.533s中运行。这意味着在32位Ubuntu(haskell平台2011)下,更快的版本要快16%。
- @Timperry,我不认为是这样。如果你在整个项目中做了一个mod,并且看到了同样的改进,那该怎么办?
- 我说,当我将素数代码中的调用从mod改为rem时,我看到了20%的加速。这不是理论上的评论。这是一个事件的描述。我只改变了一件事(尽管是多个地方),我看到20%的速度加快了。似乎有20%的速度加快了。
- @Timperry啊,我想"更快的版本"指的是rem,而不是你修改过的程序。(不知道为什么我认为如果你的意思是…,你就不会说rem)
- 啊,我知道你是怎么读出来的。
- 在许多体系结构中,包括x86,当除以非常量时,使用truncate to zero division比floor to negative infinity略快,但当除以许多常量值时,尤其是2的幂次时,truncate to zero快得多(例如一条指令对3)。我假设速度敏感的代码比速度慢的代码更容易有"快速"的划分。
一个简单的例子是测试一个整数是偶数还是奇数。
1 2 3 4 5 6 7
| let buggyOdd x = x ` rem` 2 == 1
buggyOdd 1 // True
buggyOdd (-1) // False (wrong !)
let odd x = x ` mod` 2 == 1
odd 1 // True
odd (-1) // True |
当然,请注意,您可以通过以下方式定义奇数来避免思考这些问题:
号
一般来说,只要记住,对于y > 0,x mod y总是返回>= 0的某个值,而x rem y返回0或与x符号相同的某个值。