参考:
1)https://zhuanlan.zhihu.com/p/84900782
2)https://blog.csdn.net/weixin_37747104/article/details/82949194
汉明距离——两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数。两个向量之间做异或得到一个值(值越大距离越远),计算该值的二进制1的个数,比如1101,二进制1的个数是3。
这里介绍两种方法:
方法1:
int bitcount(uint32_t v) {
int c = 0;
while (v) {
v &= v - 1;
++c;
}
return c;
}
下面话为引用参考1)第一个例子,“核心代码是v &= v – 1,它的作用是将最低位的那个1清0,如何做到呢?
用二进制的方式来思考,假设v不为0,我们只关心v中最低位的那个1,其他位不管它,那么v的二进制可以这样表示:xx..x 1 0..0
,1前面的x表示任意值(0或1),1后面肯定是0(因为都说了关注最低位的1);v-1就可以表示成:xx..x 0 1..1
,现在v & (v – 1) :xx..x 1 0..0
与xx..x 0 1..1
得到xx..x 0 0..0
,这样就把这个1给消除掉了,如此循环,直到v为0,循环多少次,就表明v有多少个二进制1。
这个算法的消耗与1的个数有关,如果v的二进制全是1,那就要循环32次,如果是64位的数字,就要循环64次。”
方法2
unsigned int v = *pa ^ *pb;
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
dist += (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
ORBSLAM2算描述子之间的距离时用的方法。下面这段话引用参考2),与参考1)中第三个例子异曲同工。
“pa和pb分别是两个描述子,进行异或操作得到一个32位int值,里面包含多个1,1的个数和即为两个描述子之间的距离。
0x55555555=01010101 01010101 01010101 01010101,我们把32位分成16个2位来看,即XX(X未知,可0可1),我们会发现int a=XX-(XX>>1)&01的结果即为XX中1的数量。XX>>1=0X,然后会发现int a=XX-(XX>>1)&01后得到的int值即为XX中1的个数。即XX=00时,a=0,XX=01时,a=1,XX=10时,a=1,XX=11时,a=2。
那么同理,v=v-((v>>1)&0x55555555)
得到了32位int值v,将此时的v每两位表示的int值相加即为原v中1的个数。
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
,0x33333333=00110011 00110011 00110011 00110011,很明显,这一步操作是v拆成8个4位,每个4位代表的int值之和即为原v中1的个数,每个4位是由上式中的结果v的相邻2位拼接得到的。
同理可得(v + (v >> 4)) & 0xF0F0F0F
是把之前的相邻4位拼接成8位,则此时32位int值四个八位代表的int值之和即为1的个数。
之后乘0x1010101的目的是把每个8位的数加到25-32位上,(注意由于声明的是uint型为4个字节,所以最高32位;另外,可以手动算一下,确实是累加每个8位数)然后右移24位移到末8位,即可得到四个八位的int值之和,即1的个数。“
例子:
#include<stdio.h>
int main(int argc, char** argv)
{
//int v = 0x12311231; // 0001 0010 0011 0001 0001 0010 0011 0001
int v = 0xffffffff; // 0001 0010 0011 0001 0001 0010 0011 0001
printf("%x\n", v); // 不要写成&v
v = v - ((v >> 1) & 0x55555555); // 0x55555555 = 0101 0101 0101 0101 0101 0101 0101 0101
printf("%x\n", v); // 0001 0010 0011 0001 0001 0010 0011 0001
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
printf("%x\n", v);
//v = (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
v = (v + (v >> 4)) & 0xF0F0F0F;
printf("x: %x\n", v);
v = v * 0x1010101; //变相累加
int dist = ((v)) >> 24; // int(0x20) = 32
printf("y: %d\n", dist);
return 0;
}
注意:方法1的程序效率与1的个数相关,方法2与1的个数无关: