Algorithm 如何确定相似位的数量?

Algorithm 如何确定相似位的数量?,algorithm,bit-manipulation,Algorithm,Bit Manipulation,我需要比较两个数字,在更重要的部分寻找相似之处。我试图确定不同的最低有效位的数量 10111000 10111011 184和187要求偏移量为2,因为只有两个最低有效位不同 10111011 11111011 187和251要求偏移量为7,因为第七个最低有效位不同 我的第一个想法是将这些数字进行异或运算,然后向右移位,直到数字等于零。我觉得有一个更好的解决方案,它不涉及循环,但是我自己还没有做足够多的工作来解决这个问题 解决方案需要适用于任何64位,因为我的号码存储为UInt64。这是用C

我需要比较两个数字,在更重要的部分寻找相似之处。我试图确定不同的最低有效位的数量

10111000
10111011
184和187要求偏移量为2,因为只有两个最低有效位不同

10111011
11111011
187和251要求偏移量为7,因为第七个最低有效位不同

我的第一个想法是将这些数字进行异或运算,然后向右移位,直到数字等于零。我觉得有一个更好的解决方案,它不涉及循环,但是我自己还没有做足够多的工作来解决这个问题

解决方案需要适用于任何64位,因为我的号码存储为
UInt64
。这是用C#编写的,但解决方案很可能是语言不可知的


需要6位的偏移量。我正在试着找出我能从顶部去掉多少类似的部分。

类似的东西

floor( log(184 ^ 187) / log(2) ) + 1
没有循环,但可能不会更快,因为登录操作代价高昂。您应该测试它,并将其与具有位移位的简单循环进行比较

有时一个(编码良好的)循环比没有循环快,特别是如果您最多有64次迭代,而且通常更少


我的代码的更高效版本:

预计算

double Ilog2 = 1 / log(2);
然后每次你需要的时候

floor( log(184 ^ 187) * ILog2 ) + 1

听起来你已经发现了主要的窍门;r=x XOR y,然后在r中找到最高位。有很多不同的解决方法。最快的方法是在O(n)运算中,将r一分为二,然后检查上半部分是否为零。如果在固定位数(您说的是64位)上执行此操作,则展开循环以获得一系列测试:

pos = 0
r = x XOR y
if r>>32 == 0 :
   r = r & 2^32-1
else
   pos += 32
   r = r>>32
if r>>16 == 0 :
   r = r & 2^16-1
else
   pos += 16
   r = r>16
... etc
您可以编写一个O(log(n))循环,以非常容易地找到最高的集合位:

int findHighestSetBit(unsigned long long x) {
    int rv = 0;
    if (x == 0)
        return -1;  // no set bits
    for (int shift = 32; shift > 0; shift >>= 1) {
        if (x >> shift) {
            rv += shift;
            x >>= shift;
        }
    }
    return rv+1; // number least significant bit as '1' rather than '0'
}

如果速度太慢,您可以手动展开循环5次。

假设您首先必须对8位数字展开循环。
#include <stdio.h>
#include <stdlib.h>

#define TO_L(s) (strtol((s), NULL, 16))

int tsb(unsigned long xa, unsigned long xb) {
  unsigned long v = xa ^ xb;
  static const unsigned long b[] = {
    0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000L, 0xFFFFffff00000000L
  };
  static const unsigned int S[]  = { 1, 2, 4, 8, 16, 32 };
  unsigned int r = 0;

#define STEP(i)   \
  if(v & b[i]) {  \
    int t = S[i]; \
    v >>= t;      \
    r  |= t;      \
  }
  STEP(5)
  STEP(4)
  STEP(3)
  STEP(2)
  STEP(1)
  STEP(0)
  return r;
}

int main(int ac, char **av) {
  return printf("%d\n", tsb(TO_L(av[1]), TO_L(av[2]))), 0;
}
最快的方法是使用预编译值的256字节查找表:

static unsigned char highest_bit_num_LUT[256] = {0, 1, 2, 2, 3, etc }; // precomputed

unsigned diff = (unsigned)a ^ (unsigned)b; // sure you need XOR and not MINUS?
unsigned highest_bit_num = highest_bit_num_LUT[diff & 0xff];
现在扩展它以获得更高的位计数:

static unsigned char highest_bit_num_LUT[256] = {0, 1, 2, 2, 3, etc }; // precomputed
unsigned diff = (unsigned)a ^ (unsigned)b; // sure you need XOR and not MINUS?
unsigned highest_bit_num = 0;
for (int i = 7; i >= 0; i--)    
    if (diff >> ( i*8) ){ // found most significant non-zero byte
        highest_bit_num = i*8 + highest_bit_num_LUT[diff >> (i*8)];
        break;
    }
现在我们最多有8次迭代


编辑:在前3次迭代中使用DigitalRoss idea,然后使用LUT会更快

这是一个很好的问题需要解决,但对于数字11101101和11010101(即多个位置上存在差异)的情况,结果应该是什么还不太清楚。在循环中移位1时,甚至不需要对它们进行异或运算,而不是将它们与0进行比较,可以一直移位,直到它们为0为止equal@Eugene-我加上了你的例子@博士-是的,但这仍然是我试图避免的。我只知道XORing是正确的方向。有趣的是,在x86汇编程序中,使用XOR的方法编码非常有效,而且很难被接受(除非他们引入特殊命令,如“获取字节中1的位置”),而在C#中,它可能需要更多的代码。@Eugene:我不确定你所说的括号中的部分是什么意思。在x86汇编中,如果我们至少需要386,那么问题会减少到
xor
+
bsr
,不?从奔腾II开始,这应该只需要很少的恒定时钟周期。
static unsigned char highest_bit_num_LUT[256] = {0, 1, 2, 2, 3, etc }; // precomputed

unsigned diff = (unsigned)a ^ (unsigned)b; // sure you need XOR and not MINUS?
unsigned highest_bit_num = highest_bit_num_LUT[diff & 0xff];
static unsigned char highest_bit_num_LUT[256] = {0, 1, 2, 2, 3, etc }; // precomputed
unsigned diff = (unsigned)a ^ (unsigned)b; // sure you need XOR and not MINUS?
unsigned highest_bit_num = 0;
for (int i = 7; i >= 0; i--)    
    if (diff >> ( i*8) ){ // found most significant non-zero byte
        highest_bit_num = i*8 + highest_bit_num_LUT[diff >> (i*8)];
        break;
    }