你是一个给定整数i > 1
的C程序员。你能编写一个函数msb2
来返回它的第二个最重要的位,换句话说:
给出一个数字,最左边1位右边的位是什么?
我在1011111
中它是0,在1100000
中它是1。
不允许:
允许:
是否存在一个常数时间(位数的常数,假设所有C整数运算符占用恒定时间,并假设位数不是常数)msb2
的函数?
我的一个想法:鉴于i
,我们知道i & (i >> 1) >= (1<<((int)log2(i)-1)) <=> msb2(i)=1
,但log2(i)
无法轻松计算。也许这仍然可以以某种方式使用?
答案 0 :(得分:1)
如果你可以到达最左边的1位(见msb64()
),其余的很容易:
#include <stdio.h>
#include <inttypes.h>
uint64_t msb64(uint64_t x);
int main(int argc, char **argv)
{
uint64_t x, msb;
if (argv[1] && sscanf (argv[1], "%lu", &x) == 1) {
msb = msb64(x);
printf ("msb2(%lu) = %d\n", x, x & (msb >> 1) ? 1 : 0);
}
return 0;
}
uint64_t msb64(uint64_t x)
{
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
x |= (x >> 32);
return x & ~(x >> 1);
}
$ ./a.out 11
msb2(11) = 0
$ ./a.out 15
msb2(15) = 1
答案 1 :(得分:0)
我相信这个技术上符合标准:写63 if
语句(假设你的整数是64位),每个可能的解决方案一个。是的,这基本上模仿了一个循环会做什么,但它仍然是恒定的时间(相应的循环实际上是,但有点难以争辩)。
答案 2 :(得分:0)
Naive log(nbits)事物:(没有循环!)
#include <stdio.h>
int main(int argc, char **argv)
{
unsigned long org , val;
sscanf(argv[1] , "%lx", &org);
val = org ;
if (val >= (2<<16)) val >>= 16;
if (val >= (2<<8)) val >>= 8;
if (val >= (2<<4)) val >>= 4;
if (val >= (2<<2)) val >>= 2;
if (val >= (2<<1)) val >>= 1;
if (val <= 2) val <<= 1; // This is needed for org = 1 ;-)
printf("%lx -> %lx %c\n"
, org, val, val&1 ? '+' : '-');
return 0;
}
答案 3 :(得分:0)
clz
会使这个变得微不足道。如果没有,您可以按如下方式计算恒定时间。从这个帖子:
http://community.arm.com/thread/6400
static __INLINE uint32_t __CLZ(uint32_t x) {
extern uint8_t const log2Lkup[256];
if (x >= 0x00010000U) {
if (x >= 0x01000000U) {
return 8U - log2Lkup[x >> 24];
}
else {
return 16U - log2Lkup[x >> 16];
}
}
else {
if (x >= 0x00000100U) {
return 24U - log2Lkup[x >> 8];
}
else {
return 32U - log2Lkup[x];
}
}
}
该函数需要在.c文件中定义的log2(二进制对数)查找表:
uint8_t const log2Lkup[256] = {
0U, 1U, 2U, 2U, 3U, 3U, 3U, 3U, 4U, 4U, 4U, 4U, 4U, 4U, 4U, 4U,
5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U, 5U,
6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U,
6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U, 6U,
7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,
7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,
7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,
7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U, 7U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U,
8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U, 8U
};
我将代码完整地保留在网站上作为示例,因为修改表格可以使这更容易。
要解决您的情况,请执行(32 - clz(x)) - 1
并特别处理x
为1或0的情况,因为它未定义。注意:对于64位值,只需添加一些查找...
int wantedValue(int x)
{
if(x <= 1) return -1;
return (int)(32 - __CLZ(x)) - 1;
}