使用整数除法时,用“a / b / c”替换“a /(b * c)”是否安全?

时间:2017-07-14 23:30:30

标签: math language-agnostic integer-division

在正整数a/(b*c)上使用整数除法时,将a/b/c替换为a,b,c是否安全,或者我是否有丢失信息的风险?

我做了一些随机测试但找不到a/(b*c) != a/b/c的例子,所以我很确定它是安全的,但不太确定如何证明它。

谢谢。

3 个答案:

答案 0 :(得分:11)

数学

作为数学表达式,⌊a/(bc)⌋⌊⌊a/b⌋/c⌋只要b非零且c为正整数(特别是正整数a) ,bc)。这些东西的标准参考是由Graham,Knuth和Patashnik撰写的令人愉快的书 Concrete Mathematics:计算机科学基础。其中,第3章主要是关于地板和天花板,这在第71页证明是一个更为一般的结果的一部分:

screenshot from book

在上面的3.10中,您可以定义x = a/b(数学,即实际划分)和f(x) = x/c(再次精确划分),并将其插入左侧⌊f(x)⌋ = ⌊f(⌊x⌋)⌋的结果中(在验证f上的条件在此处保留时)以使LHS上的⌊a/(bc)⌋等于RHS上的⌊⌊a/b⌋/c⌋

如果我们不想依赖书中的参考,我们可以直接使用他们的方法证明⌊a/(bc)⌋ = ⌊⌊a/b⌋/c⌋。请注意,对于x = a/b(实数),我们要尝试证明的是⌊x/c⌋ = ⌊⌊x⌋/c⌋。所以:

  • 如果x是一个整数,则无需证明,x = ⌊x⌋
  • 否则为⌊x⌋ < x,所以⌊x⌋/c < x/c表示⌊⌊x⌋/c⌋ ≤ ⌊x/c⌋。 (我们希望表明它是平等的。)假设为了相互矛盾,⌊⌊x⌋/c⌋ < ⌊x/c⌋那么⌊x⌋ < y ≤ xy/c = ⌊x/c⌋必须有一个数字y。 (当我们将数字从⌊x⌋增加到x并考虑按c划分时,我们必须在某个地方达到确切的值⌊x/c⌋。)但这意味着{{1} }是y = c*⌊x/c⌋⌊x⌋之间的整数,这是一个矛盾!

这证明了结果。

编程

x

打印(使用32位整数),

#include <stdio.h>

int main() {
  unsigned int a = 142857;
  unsigned int b = 65537;
  unsigned int c = 65537;

  printf("a/(b*c) = %d\n", a/(b*c));
  printf("a/b/c = %d\n", a/b/c);
}

(我使用无符号整数作为它们的溢出行为是well-defined,所以上面的输出是有保证的。对于有符号整数,溢出是未定义的行为,所以程序实际上可以打印(或做)任何,只会强调结果可能不同的点。)

但是如果你没有溢出,那么你在程序中得到的值等于它们的数学值(也就是说,代码中的a/(b*c) = 1 a/b/c = 0 等于数学值{{1} },并且代码中的a/(b*c)等于数学值⌊a/(bc)⌋),我们已经证明它们是相等的。因此,当a/b/c足够小而不会溢出时,⌊⌊a/b⌋/c⌋可以安全地替换代码中的a/(b*c)

答案 1 :(得分:2)

只要你跟踪你的除数和红利,就可以重新排序嵌套的地板分割。

#python3.x
x // m // n = x // (m * n)

#python2.x
x / m / n = x / (m * n)

证明(在python3.x中没有LaTeX :()很糟糕:

Let k = x // m
then k - 1 < x / m <= k
and (k - 1) / n < x / (m * n) <= k / n

In addition, (x // m) // n = k // n
and because x // m <= x / m and (x // m) // n <= (x / m) // n
k // n <= x // (m * n)

Now, if k // n < x // (m * n)
then k / n < x / (m * n)
and this contradicts the above statement that x / (m * n) <= k / n

so if k // n <= x // (m * n) and k // n !< x // (m * n)
then k // n = x // (m * n)

and (x // m) // n = x // (m * n)

https://en.wikipedia.org/wiki/Floor_and_ceiling_functions#Nested_divisions

答案 2 :(得分:2)

虽然b*c可能会溢出(在C中)原始计算,但a/b/c无法溢出,因此我们不需要担心前置替换的溢出{{ 1}}。不过,我们需要另外担心。

a/(b*c) -> a/b/c。对于某些x = a/b/c,我会a/b == x*c + y,而y < c则为a == (x*c + y)*b + z

因此,z < ba == x*b*c + y*b + z最多为y*b + z,因此b*c-1x*b*c <= a <= (x+1)*b*c

因此,a/(b*c) == xa/b/c == a/(b*c)替换a/(b*c)是安全的。