提取整数的最右边N位

时间:2010-05-09 15:50:37

标签: java algorithm bit-manipulation

在yester Code Jam Qualification round http://code.google.com/codejam/contest/dashboard?c=433101#s=a&a=0中,出现了一个名为Snapper Chain的问题。从比赛分析我开始知道这个问题需要一些小问题,例如提取一个整数的最右边的N位并检查它们是否都是1.我看到一个参赛者的(Eireksten)代码执行了如下所述的操作:

(((K&(1<<N)-1))==(1<<N)-1)

我无法理解这是如何运作的。在比较中有什么用于-1?如果有人可以解释这一点,那对我们这些新手来说非常有用。此外,非常感谢任何有关识别此类问题的提示。我使用了一个简单的算法来解决这个问题,最终只解决了较小的数据集。(编译需要在8分钟内提交的较大数据集需要花费一些时间。)。提前谢谢。

4 个答案:

答案 0 :(得分:22)

我们以N = 3为例。二进制,1<<3 == 0b1000。所以1<<3 - 1 == 0b111

通常,1<<N - 1以二进制形式创建一个带有N个的数字。

R = 1<<N-1。然后表达式变为(K&R) == RK&R将提取最后N位,例如:

     101001010
  &        111
  ———————————— 
     000000010

(回想一下,当且仅当两个操作数在该数字中都有1时,按位与AND将返回一位数。)

当且仅当最后N位全部为1时,等式成立。因此表达式检查K是否以N结尾。

答案 1 :(得分:4)

例如:N = 3,K = 101010

1. (1<<N) = 001000 (shift function)
2. (1<<N)-1 = 000111 (http://en.wikipedia.org/wiki/Two's_complement)
3. K&(1<<N)-1 = 0000010 (Bitmask)
4. K&(1<<N)-1 == (1<<N)-1) = (0000010 == 000111) = FALSE

答案 2 :(得分:1)

我正在解决Snapper Chain问题并来到这里寻找关于我在解决方案中遇到的比特错误算法的解释。我发现了一些不错的信息,但我还是花了好一些时间来为自己弄明白,这是一个有点小道的菜鸟。

这是我试图解释算法以及如何提出它的尝试。如果我们列举链中每个snapper的所有可能的功率和ON / OFF状态,我们会看到一个模式。鉴于测试用例N = 3,K = 7(3个笛鲷,7个按扣),我们显示每个第k个按扣的每个鲷鱼的功率和开/关状态:

            1    2    3
  0b:1 1   1.1  1.0  0.0 -> ON for n=1
 0b:10 2   1.0  0.1  0.0
 0b:11 3   1.1  1.1  1.0 -> ON for n=1, n=2
0b:100 4   1.0  0.0  1.0
0b:101 5   1.1  1.0  1.0 -> ON for n=1
0b:110 6   1.0  0.1  0.1
0b:111 7   1.1  1.1  1.1 -> ON for n=2, n=3

当所有笛鲷打开并接通电源时,或当我们有第k个按扣导致n 1s时,灯泡亮起。更简单地说,当所有笛子都打开时,灯泡打开,因为它们都必须接通电源(因此灯泡)。这意味着对于每个k个快照,我们需要n 1个。

此外,您可以注意到k是所有二进制1,不仅对于满足n = 3的k = 7,而对于k = 3,满足n = 2且k = 1满足n = 1。此外,对于n = 1或2,我们看到每个数量的按钮打开灯泡,k的最后n个数字总是1.我们可以尝试推广所有满足n个笛鲷的ks将是一个以二进制数结尾的n位数为1。

我们可以使用早于1&lt;&lt;&lt; n-1总是给出n个二进制数字1,或者在这种情况下,1&lt;&lt;&lt; 3 - 1 = 0b111。如果我们将n个笛鲷链视为二进制数,其中每个数字代表开/关,我们想要一个n个数字,这就是我们的表示。

现在我们想要找到1&lt;&lt; n - 1等于某个以n个二进制数字1结尾的k,我们通过执行按位 - 和:k&amp; (1&lt;&lt; n-1)得到k的最后n位数,然后将其与1&lt;&lt;&lt; n - 1。

我认为在解决这些类型的问题后,这种思维会更自然地出现,但它仍然让我感到恐惧,我怀疑自己会不会想出这样的解决方案!

这是我在perl中的解决方案:

$tests = <>;
for (1..$tests) {
    ($n, $k) = split / /, <>;
    $m = 1 << $n - 1;
    printf "Case #%d: %s\n", $_, (($k & $m) == $m) ? 'ON' : 'OFF';
}

答案 3 :(得分:0)

我认为我们可以通过手动计算答案来识别这类问题,对于一些N系列(例如1,2,3,...)。之后,我们将识别状态更改,然后编写一个函数来自动化该过程(第一个函数)。运行程序以获取某些输入,并注意模式。

当我们得到模式时,写下表示模式的函数(第二个函数),并比较第一个函数和第二个函数的输出。

对于Code Jam案例,我们可以对小数据集运行这两个函数,并验证输出。如果它是相同的,我们很有可能第二个函数可以及时解决大数据集。

相关问题