无法理解数字序列的按位AND,代码提供

时间:2015-07-16 20:17:33

标签: java bit-manipulation bitwise-operators

如果给出两个整数A和B,其中0 <= A <= B <= 2 ^ 32。找到范围(A,B)中的整数的按位AND。例如,A = 12且B = 15,

12&amp; 13&amp; 14&amp; 15 = 12

我的TA没有做太多解释如何解决问题,而是在办公时间结束时给每个人留下了解决方案,现在,我无法解决问题,但我也不明白解决方案。在下面的代码中,我注意到我理解并且不理解的行。

我尝试过用小As和B做自己的例子的笔和纸方法,而我可以看到代码工作,我无法掌握代码工作原理背后的理论。

private void run() {
        Scanner in = new Scanner(System.in);

        long a = in.nextLong();
        long b = in.nextLong();
        long diff = Math.max(((long) (Math.log(b - a) / Math.log(2)) - 1), 0);
        //what does diff represent?

        long shiftA = a >> diff;    //right shift by diff okay
        long shiftB = b >> diff;    // " "
        long result = shiftA;
        for (long j = shiftA; j <= shiftB; j++) {
            result = result & j;    //don't understand the loop
        }
        result = result << diff;    //left shift, but why?
        System.out.println(result);
    }
}

3 个答案:

答案 0 :(得分:3)

  

差异代表什么?

diff使用change of base formula for logarithms来计算差异b-a的表示位数。如果差异需要K位来表示,那么K-1范围中某个数字的结果的最后[a..b]位将全部设置为零,这意味着您可以清除他们出了结果。因此,最后左移diff:它会将diff 0转换为结果。

  

我不理解循环

循环通过位表示减少2 diff 次,即仅使用ab的高位。由于低diff位无论如何都将设置为零,因此此解决方案计数为2 diff 而不是按1计数,从而减少了到达结果所需的时间。

考虑a=23b=39的示例。 diff等于3。以下是表示,用逗号分隔最后3位:

d       b
-- -------
23 010,111
24 011,000
25 011,001
26 011,010
27 011,011
28 011,100
29 011,101
30 011,110
31 011,111
32 100,000 <<-- The last diff bits will be set to zero somewhere in the range
33 100,001
34 100,010
35 100,011
36 100,100
37 100,101
38 100,110
39 100,111

由于最后三位保证全部为零,因此循环可以计数8,而不是按1计数。这将执行速度降低了八倍。通过将数字首先向右移动diff,然后按1计数,然后向左移动diff来完成8的计数。

答案 1 :(得分:2)

diff部分只是一个优化,使得代码更复杂和棘手(在某些边缘情况下可能是错误的)以节省一些运行时间。 (一般来说,只有当性能很重要并且只有良好的测试时,这才是一个好主意。在这里,我们可以轻松地花更多时间来理解优化并使其正确,而不是运行未经优化的程序几次。)

让我们首先讨论基本循环,设置diff = 0。所以a >> diff == aresult << diff == result,所以我们可以忽略所有这些。

主循环直接实现解决方案:

long result = a;
for (long j = a; j <= b; j++) {
    result = result & j;
}

即,bitand(&)每个值一起来自范围[a ... b]。给定a = 12且b = 15,即12 & 13 & 14 & 15。 (实际上,如果你仔细地手工模拟它,你会注意到它会计算12 & 12 & 13 & 14 & 15,这会得到相同的结果。)这是理解,基本循环和按位数学的重要部分。

diff部分通过删除低阶位使代码更快。例如。如果某些输入值计算diff == 4,则a >> 4会移出a的低4位,基本上为a / 16,因为2 4 == 16然后循环将多次运行1/16。然后,代码将结果重新移位result << 4,移位为0位,基本上为a * 16

diff的值上,请注意Math.log(b - a) / Math.log(2) == log 2 (b - a),这是b - a中的位数。它正在努力计算最终为零的低阶位的数量,以便它可以将它们移出,循环次数减少,然后在末尾以零移位。

答案 2 :(得分:0)

更简单,也更容易理解:

 long result = firstValue;
 for ( long i=result+1; i <= lastValue; i++ ) {
   result &= i;
 }