例如,如果我有数字64,则其二进制表示将为0000 0000 0000 0000 0000 0000 0100 0000,因此前导零的数量为25。
请告诉我正确的做法。即使您的复杂度> O(1),也请发布您的答案。谢谢
我刚刚在搜索结果的顶部发现了这个问题和这段代码:
int pop(unsigned x) {
unsigned n;
n = (x >> 1) & 033333333333;
x = x - n;
n = (n >> 1) & 033333333333;
x = x - n;
x = (x + (x >> 3)) & 030707070707;
return x % 63;
}
int nlz(unsigned x) {
x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >>16);
return pop(~x);
}
针对1位二进制数计数的pop函数,比第一个(得到赞同的)答案快几倍。
我没有注意到问题是关于64位数字的,所以在这里提供:
int nlz(unsigned long x) {
unsigned long y;
long n, c;
n = 64;
c = 32;
do {
y = x >> c;
if (y != 0) {
n = n - c;
x = y;
}
c = c >> 1;
} while (c != 0);
return n - x;
}
这是一个64位算法,比上述提到的算法快了几倍。
请参见此处获取32位版本和其他优秀的位操作技巧。
// this is like doing a sign-extension
// if original value was 0x00.01yyy..y
// then afterwards will be 0x00.01111111
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
x |= (x >> 32);
然后你只需要返回64 - numOnes(x)。一个简单的方法是numOnes32(x) + numOnes32(x >> 32),其中numOnes32被定义为:
int numOnes32(unsigned int x) {
x -= ((x >> 1) & 0x55555555);
x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
x = (((x >> 4) + x) & 0x0f0f0f0f);
x += (x >> 8);
x += (x >> 16);
return(x & 0x0000003f);
}
我还没有尝试过这段代码,但这应该可以直接使用numOnes64(在更短的时间内):
int numOnes64(unsigned long int x) {
x = ((x >> 1) & 0x5555555555555555L) + (x & 0x5555555555555555L);
x = ((x >> 2) & 0x3333333333333333L) + (x & 0x3333333333333333L);
// collapse:
unsigned int v = (unsigned int) ((x >>> 32) + x);
v = ((v >> 4) + v) & 0x0f0f0f0f) + (v & 0x0f0f0f0f);
v = ((v >> 8) & 0x00ff00ff) + (v & 0x00ff00ff);
return ((v >> 16) & 0x0000ffff) + (v & 0x0000ffff);
}
右移是你的好朋友。
int input = 64;
int sample = ( input < 0 ) ? 0 : input;
int leadingZeros = ( input < 0 ) ? 0 : 32;
while(sample) {
sample >>= 1;
--leadingZeros;
}
printf("Input = %d, leading zeroes = %d\n",input, leadingZeros);
unsigned long clz(unsigned long n) {
unsigned long result = 0;
unsigned long mask = 0;
mask = ~mask;
auto size = sizeof(n) * 8;
auto shift = size / 2;
mask >>= shift;
while (shift >= 1) {
if (n <= mask) {
result += shift;
n <<= shift;
}
shift /= 2;
mask <<= shift;
}
return result;
}
由于以2为底数的对数大致代表了表示一个数字所需的比特数,因此在答案中可能会有用:
irb(main):012:0> 31 - (Math::log(64) / Math::log(2)).floor()
=> 25
irb(main):013:0> 31 - (Math::log(65) / Math::log(2)).floor()
=> 25
irb(main):014:0> 31 - (Math::log(127) / Math::log(2)).floor()
=> 25
irb(main):015:0> 31 - (Math::log(128) / Math::log(2)).floor()
=> 24
log(3)
的一个缺点是它是一个浮点数例程;可能有一些极其聪明的位操作技巧来查找整数中前导零位的数量,但我想不出一个。使用浮点数并不是正确的答案...
这里有一个我用来计算末尾0的算法...想要计算首位0可以进行修改... 这个算法的时间复杂度为O(1)(在某些CPU上总是以大约相同的时间执行甚至完全相同)。
int clz(unsigned int i)
{
int zeros;
if ((i&0xffff)==0) zeros= 16, i>>= 16; else zeroes= 0;
if ((i&0xff)==0) zeros+= 8, i>>= 8;
if ((i&0xf)==0) zeros+= 4, i>>= 4;
if ((i&0x3)==0) zeros+= 2, i>>= 2;
if ((i&0x1)==0) zeros+= 1, i>>= 1;
return zeroes+i;
}
char
/short
/int
/long
的大小仅指定为最小值,并相对于彼此。 - Karl Knechtel