令a,b和c为非大正整数。使用C#整数算法,a / b / c是否总是等于a /(b * c)?对我来说,在C#中看起来像:
int a = 5126, b = 76, c = 14;
int x1 = a / b / c;
int x2 = a / (b * c);
所以我的问题是:x1 == x2
a,b和c都适用吗?
令a,b和c为非大正整数。使用C#整数算法,a / b / c是否总是等于a /(b * c)?对我来说,在C#中看起来像:
int a = 5126, b = 76, c = 14;
int x1 = a / b / c;
int x2 = a / (b * c);
所以我的问题是:x1 == x2
a,b和c都适用吗?
Answers:
让\
分别表示整数除法(该C#/
2个之间操作者int
或多个),并让/
分别表示通常的数学除法。然后,如果x,y,z
是正整数,而我们忽略了溢出,
(x \ y) \ z
= floor(floor(x / y) / z) [1]
= floor((x / y) / z) [2]
= floor(x / (y * z))
= x \ (y * z)
哪里
a \ b = floor(a / b)
从线跳转[1]
到线[2]
上面解释如下。假设您有两个整数a
和b
一个f
范围内的小数[0, 1)
。显而易见
floor(a / b) = floor((a + f) / b) [3]
如果符合条件,则[1]
标识a = floor(x / y)
,f = (x / y) - floor(x / y)
和b = z
,则[3]
表示[1]
和[2]
相等。
您可以将该证明推广为负整数(仍然忽略overflow),但是我将把它留给读者以保持重点。
关于溢出的问题-请参阅Eric Lippert的答案以获得良好的解释!他还在博客文章和答案中采取了更为严格的方法,如果您觉得我太过手摇,就应该研究一下。
floor(x / y) - (x / y)
很小,z >= 1
因此将其floor
取为0,我们可以忽略它。这实际上并没有发生,因为它实际上是一个加数floor()
(例如,考虑floor(1/2)
vs floor(1/2 + 1/2)
)。
我非常喜欢这个问题,因此我将其作为主题 在2013年6月4日博客。感谢您提出的好问题!
大案件很容易得到。例如:
a = 1073741823;
b = 134217727;
c = 134217727;
因为b * c
溢出到负数。
我想补充到,在事实核对算术,之间的差异a / (b * c)
,并(a / b) / c
可以是程序之间的区别在于作品和程序崩溃。如果是b
和c
溢出的整数的范围那么前者将在检查范围内崩溃。
对于小的正整数(例如,足够小以适合短整数),应保持身份。
蒂莫西·希尔兹(Timothy Shields)刚刚发布了证明;我在这里提出另一种证明。假定这里的所有数字都是非负整数,并且所有操作都不会溢出。
的整数除法x / y
找到值,q
如q * y + r == x
,0 <= r < y
。
所以师a / (b * c)
认定的价值q1
,使得
q1 * b * c + r1 == a
哪里 0 <= r1 < b * c
该部门( a / b ) / c
首先找到的价值qt
是
qt * b + r3 == a
然后发现值q2
,使得
q2 * c + r2 == qt
因此,用它代替,qt
我们得到:
q2 * b * c + b * r2 + r3 == a
在哪里0 <= r2 < c
和0 <= r3 < b
。
两个相等的事物彼此相等,所以我们有
q1 * b * c + r1 == q2 * b * c + b * r2 + r3
假设q1 == q2 + x
有一个整数x
。代入并解决x
:
q2 * b * c + x * b * c + r1 = q2 * b * c + b * r2 + r3
x = (b * r2 + r3 - r1) / (b * c)
哪里
0 <= r1 < b * c
0 <= r2 < c
0 <= r3 < b
可以x
大于零吗?不,我们有不平等:
b * r2 + r3 - r1 <= b * r2 + r3 <= b * (c - 1) + r3 < b * (c - 1) + b == b * c
因此,该分数的分子始终小于b * c
,因此x
不能大于零。
可以x
小于零吗?不,通过类似的论点,留给读者。
因此,整数x
为零,因此q1 == q2
。
x1
与该x2
操作将在这种情况下,相同的崩溃
x1
,x2
都会崩溃。为其他值时,表达式是更好的,因为将避免的可能整数溢出该了。b
c
x1
( b * c)
x2
我将提供自己的有趣证明。这也忽略了溢出,仅不幸地处理了正数,但我认为证明是干净明确的。
目的是表明
floor(floor(x/y)/z) = floor(x/y/z)
/
正常除法在哪里(在整个证明中)。
我们将a/b
唯一的商和余数表示为a = kb + r
(这意味着我们k,r
是唯一的,也要注意|r| < |b|
)。然后我们有:
(1) floor(x/y) = k => x = ky + r
(2) floor(floor(x/y)/r) = k1 => floor(x/y) = k1*z + r1
(3) floor(x/y/z) = k2 => x/y = k2*z + r2
因此,我们的目标只是表明这一点k1 == k2
。好吧,我们有:
k1*z + r1 = floor(x/y) = k = (x-r)/y (from lines 1 and 2)
=> x/y - r/y = k1*z + r1 => x/y = k1*z + r1 + r/y
因此:
(4) x/y = k1*z + r1 + r/y (from above)
x/y = k2*z + r2 (from line 3)
现在从(2)观察它r1
是一个整数(fork1*z
定义为整数)和r1 < z
(也定义为整数)。此外,从(1)我们知道r < y => r/y < 1
。现在考虑r1 + r/y
来自(4)的和。该声明是,r1 + r/y < z
并且从先前的声明中可以很明显地看出来(因为0 <= r1 < z
和r1
是一个整数,所以我们有0 <= r1 <= z-1
。因此0 <= r1 + r/y < z
)。因此,r1 + r/y = r2
根据定义r2
(否则将有两个余数与余数x/y
的定义相矛盾)。因此,我们有:
x/y = k1*z + r2
x/y = k2*z + r2
我们有我们期望的结论k1 = k2
。
上面的证明应该与否定一起工作,除了需要检查额外情况的几个步骤之外……但我没有检查。