给定两个字节,如何在两个字节的开头找到公共位的长度。
例如:
9 == 00001001
6 == 00000110
Common prefix is 0000, length 4
我正在使用C#,所以请坚持使用C#操作。
附录:这段特殊的代码将运行数千次,并且需要非常快。
答案 0 :(得分:6)
byte x = 9;
byte y = 6;
while ( x != y )
{
x >>= 1;
y >>= 1;
}
基本上,从每个数字的右边删除一个位,直到两个相等。当它们变得相等时,它们的位也是相等的。
您可以通过引入另一个变量轻松跟踪前缀的长度。我会留给你的。
如果您希望它快速,并且考虑到您正在处理字节,为什么不预先计算值并在单个操作中返回答案?对两个字节的所有可能组合运行此算法,并将结果存储在表中。
您实际只有2^8 * 2^8 = 2^16
种可能性2^15
,因为x = 6
和y = 9
与x = 9
和y = 6
相同。如果你能负担得起初始时间和内存,那么预计算最终应该是最快的。
修改强>
你得到的解决方案至少更适合预计算,一般来说可能更快:找到x ^ y
中最左边的1位。使用此方法,构建一个表Pre
,其中Pre[i] = position of leftmost 1 bit in i
。此表只需要2 ^ 8个字节。
答案 1 :(得分:4)
使用查找表:
readonly static int[] bytePrefix = new int[] {
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
使用XORing两个字节:
bytePrefix[9 ^ 6]
我相信它的速度和它一样快,它只是一个XOR操作和一个数组查找(你也可以将它改为2个数组查找,但它会使用256倍的内存,可能会慢一点,它真的是按位的快)。
答案 2 :(得分:3)
首先使用xor运算符获取字节之间的二进制差异。然后你只需向右移位,直到差值为零:
byte b1 = 6;
byte b2 = 9;
int length = 8;
for (int diff = b1 ^ b2; diff != 0; length--) diff >>= 1;
这将在循环中给出最少的计算,因此它会相当快。
答案 3 :(得分:2)
如果你是在一个空间有限的环境中(显然你不是在使用C#,但只是一般)并且无法负担查找表:
byte test = byte1 ^ byte2;
int length = 0;
if ((test & 0x80) == 0)
{
if ((test & 0x40) == 0)
{
if ((test & 0x20) == 0)
{
if ((test & 0x10) == 0)
{
// I think you get the idea by now.
// Repeat for the lower nibble.
}
else
length = 3;
}
else
length = 2;
}
else
length = 1;
}
这基本上是一个解开的循环,用于查找XOR数字中的前1位。如果没有查找表,我认为它不会比这更快。
答案 4 :(得分:2)
这可以作为已知快速解决方案的一个更简单的问题重申:
X ^ Y
中最左侧的真实位。一些代码(显然代码不能立即跟随项目符号列表?!?)
int findCommonPrefix(long x, long y, out long common)
{
int prefixPlace = 0;
int testPlace = 32;
long w, mismatch = x ^ y;
do {
w = mismatch >> testPlace;
if (w != 0) { prefixPlace |= testPlace; mismatch = w; }
testPlace >>= 1;
} while (testPlace != 0);
common = x >> prefixPlace;
return 64 - prefixPlace;
}
这只需要6次迭代才能找到64位长的公共前缀,字节版本只需要3次迭代。展开循环以获得更快的速度。
答案 5 :(得分:1)
这是一种程序方式:
int r = 8;
while (a != b)
{
a >>= 1;
b >>= 1;
r -= 1;
}
这是一种使用只有256个条目的查找表的方法:
int[] lookupTable;
void createLookupTable()
{
lookupTable = new int[256];
for (int a = 0; a <= 255; ++a)
{
int n = 8;
byte b = (byte)a;
while (b > 0) {
b >>= 1;
n -= 1;
}
lookupTable[a] = n;
}
}
int commonPrefix(byte a, byte b)
{
return lookupTable[a ^ b];
}
只是为了好玩,这是用LINQ做的一种方式:
int r = 8 - Enumerable.Range(0, 9).Where(n => a >> n == b >> n).First();
答案 6 :(得分:1)
使用exclusive或(xor)的另一种方法:
public int GetCommonPrefixLength(byte a, byte b)
{
int c = a ^ b;
int len = -1;
while ((++len < 8) && ((c & 0x80) == 0))
c = c << 1;
return len;
}
答案 7 :(得分:1)
这是一个没有桌子或循环的人:
len = (a^b) ? (7 - (int)Math.Log( a^b, 2)) : 8;
log 2 X是必须提高数字2以获得值X的功率。由于二进制数中的每个位表示下一个2的幂,您可以使用此事实来找到最高位集(从0开始计数):
2**0 = 1 = 0b0001; log2(1) = 0
2**1 = 2 = 0b0010; log2(2) = 1
2**1.6 =~3 = 0b0011; log2(3) =~1.6; (int)log2(3) = 1
2**2 = 4 = 0b0100; log2(4) = 2
...
2**3 = 8 = 0b1000; log2(8) = 3
因此代码的工作原理是a XOR b
,它只设置不同的位。如果结果不为零,我们使用log2来查找最高位集。少了7,结果给出了前导零的数量=公共位的数量。有一种特殊情况,a XOR b == 0
:log2(0)是-Infinity,因此不起作用,但我们知道所有位必须匹配,所以答案是8。
答案 8 :(得分:0)
int i;
for (i=0;i<sizeof(byte);i++)
if (a >> sizeof(byte)-i != b >> sizeof(byte)-i) break;
答案 9 :(得分:0)