(number) & (-number)
的含义是什么?我搜索了它但却找不到意思
我想在for循环中使用i & (-i)
:
for (i = 0; i <= n; i += i & (-i))
答案 0 :(得分:21)
假设2的补码(或i
未签名),-i
等于~i+1
。
i & (~i + 1)
是提取i
的最低设置位的技巧。
这是有效的,因为+1实际上做的是设置最低的清除位,并清除低于该位的所有位。因此,i
和~i+1
中设置的唯一位是来自i
的最低设置位(即~i
中的最低清除位)。低于此值的位在~i+1
中清晰显示,高于此位的位在i
和~i
之间不相等。
在循环中使用它似乎很奇怪,除非循环体修改i
,因为i = i & (-i)
是一个幂等操作:执行两次会再次给出相同的结果。
[编辑:在别处的评论中,您指出代码实际上是i += i & (-i)
。那么,非零i
的作用是清除i
的最低设置位组,并将下一个清除位设置为高于该值,例如101100 - > 110000.对于i
没有明显高于最低设置位(包括i = 0
)的位,它将i
设置为0.所以如果它不是i
的事实{ {1}}从0
开始,每个循环将至少增加i
两倍于前一循环,有时更多,直到最终超过{{1}并且打破或转到n
并永远循环。
在没有评论的情况下编写这样的代码通常是不可原谅的,但是根据问题的域可能这是一个明显的&#34;循环的值序列。]
答案 1 :(得分:3)
我想我只是花一点时间来说明这是如何运作的。此代码为您提供最低设置位的值:
int i = 0xFFFFFFFF; //Last byte is 1111(base 2), -1(base 10)
int j = -i; //-(-1) == 1
int k = i&j; // 1111(2) = -1(10)
// & 0001(2) = 1(10)
// ------------------
// 0001(2) = 1(10). So the lowest set bit here is the 1's bit
int i = 0x80; //Last 2 bytes are 1000 0000(base 2), 128(base 10)
int j = -i; //-(128) == -128
int k = i&j; // ...0000 0000 1000 0000(2) = 128(10)
// & ...1111 1111 1000 0000(2) = -128(10)
// ---------------------------
// 1000 0000(2) = 128(10). So the lowest set bit here is the 128's bit
int i = 0xFFFFFFC0; //Last 2 bytes are 1100 0000(base 2), -64(base 10)
int j = -i; //-(-64) == 64
int k = i&j; // 1100 0000(2) = -64(10)
// & 0100 0000(2) = 64(10)
// ------------------
// 0100 0000(2) = 64(10). So the lowest set bit here is the 64's bit
对于无符号值,它的作用相同,结果始终是最低设置位的值。
鉴于你的循环:
for(i=0;i<=n;i=i&(-i))
没有设置位(i=0
),因此您将在此操作的增量步骤中返回0。因此,除非n=0
或i
被修改,否则此循环将永远继续。
答案 2 :(得分:1)
假设负值使用二进制补码。然后-number
可以计算为(~number)+1
,翻转位并添加1。
例如number = 92
。那就是它在二进制文件中的样子:
number 0000 0000 0000 0000 0000 0000 0101 1100
~number 1111 1111 1111 1111 1111 1111 1010 0011
(~number) + 1 1111 1111 1111 1111 1111 1111 1010 0100
-number 1111 1111 1111 1111 1111 1111 1010 0100
(number) & (-number) 0000 0000 0000 0000 0000 0000 0000 0100
从上面的示例中可以看出(number) & (-number)
给你的位置最少。
您可以在IDE One上看到代码在线运行:http://ideone.com/WzpxSD
这是一些C代码:
#include <iostream>
#include <bitset>
#include <stdio.h>
using namespace std;
void printIntBits(int num);
void printExpression(char *text, int value);
int main() {
int number = 92;
printExpression("number", number);
printExpression("~number", ~number);
printExpression("(~number) + 1", (~number) + 1);
printExpression("-number", -number);
printExpression("(number) & (-number)", (number) & (-number));
return 0;
}
void printExpression(char *text, int value) {
printf("%-20s", text);
printIntBits(value);
printf("\n");
}
void printIntBits(int num) {
for(int i = 0; i < 8; i++) {
int mask = (0xF0000000 >> (i * 4));
int portion = (num & mask) >> ((7 - i) * 4);
cout << " " << std::bitset<4>(portion);
}
}
这里还有一个C#.NET版本:https://dotnetfiddle.net/ai7Eq6
答案 3 :(得分:1)
操作i & -i
用于隔离相应整数的最低有效非零位。
在二进制符号中,num
可以表示为 a1b
,其中 a
代表最后一位之前的二进制数字,b
代表最后一位之后的零。
-num
等于 (a1b)¯ + 1 = a¯0b¯ + 1
。 b
由全零组成,因此 b¯
由全 1 组成。
-num = (a1b)¯ + 1
=> a¯0b¯ + 1
=> a¯0(0…0)¯ + 1
=> ¯0(1…1) + 1
=> a¯1(0…0)
=> a¯1b
现在,num & -num
=> a1b & a¯1b
=> (0..0)1(0..0)
例如如果 i = 5
| iteration | i | last bit position | i & -i|
|-------- |--------|-------- |-----|
| 1 | 5 = 101 | 0 | 1 (2^0)|
| 2 | 6 = 110 | 1 | 2 (2^1)|
| 3 | 8 = 1000 | 3 | 8 (2^3)|
| 4 | 16 = 10000 | 4 | 16 (2^4)|
| 5 | 32 = 100000 | 5 | 32 (2^5)|
此操作主要用于二叉索引树在树上上下移动
PS:由于某种原因,stackoverflow 将表格视为代码 :(