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;
}