给定无符号整数x
如何找到最小的n,O(1)中2 ^ n
≥x
?换句话说,我希望在O(1)中以x
的二进制格式(加上1,如果x
不是2的幂)找到更高设置位的索引(不依赖于整数的大小和字节大小。)
答案 0 :(得分:7)
如果没有内存限制,那么您可以使用查找表(x
的每个可能值的一个条目)来实现 O(1)时间。
如果你想要一个实用的解决方案,大多数处理器都会有某种“找到最高位设置”的操作码。例如,在x86上,它是BSR
。大多数编译器都有一个编写原始汇编程序的机制。
答案 1 :(得分:4)
好的,到目前为止还没有人发布编译时解决方案,这是我的。前提条件是您的输入值是编译时常量。如果你有,那么这一切都是在编译时完成的。
#include <iostream>
#include <iomanip>
// This should really come from a template meta lib, no need to reinvent it here,
// but I wanted this to compile as is.
namespace templ_meta {
// A run-of-the-mill compile-time if.
template<bool Cond, typename T, typename E> struct if_;
template< typename T, typename E> struct if_<true , T, E> {typedef T result_t;};
template< typename T, typename E> struct if_<false, T, E> {typedef E result_t;};
// This so we can use a compile-time if tailored for types, rather than integers.
template<int I>
struct int2type {
static const int result = I;
};
}
// This does the actual work.
template< int I, unsigned int Idx = 0>
struct index_of_high_bit {
static const unsigned int result =
templ_meta::if_< I==0
, templ_meta::int2type<Idx>
, index_of_high_bit<(I>>1),Idx+1>
>::result_t::result;
};
// just some testing
namespace {
template< int I >
void test()
{
const unsigned int result = index_of_high_bit<I>::result;
std::cout << std::setfill('0')
<< std::hex << std::setw(2) << std::uppercase << I << ": "
<< std::dec << std::setw(2) << result
<< '\n';
}
}
int main()
{
test<0>();
test<1>();
test<2>();
test<3>();
test<4>();
test<5>();
test<7>();
test<8>();
test<9>();
test<14>();
test<15>();
test<16>();
test<42>();
return 0;
}
这样做很有趣。
答案 2 :(得分:1)
在<cmath>
中,有对数函数可以为你执行此计算。
ceil(log(x) / log(2));
答案 3 :(得分:1)
转换表达式的一些数学运算:
int n = ceil(log(x)/log(2));
这显然是O(1)。
答案 4 :(得分:1)
这是一个关于找到最高位集的问题(正如lshtar和Oli Charlesworth指出的那样)。 Bit Twiddling Hacks提供了一个解决方案,对32位整数进行大约7次操作,对64位整数进行大约9次操作。
答案 5 :(得分:0)
您可以使用预先计算的表格。
如果您的号码在[0,255]间隔内,则可以查看简单的表格。
如果它更大,那么你可以按字节分割并从高到低检查它们。
答案 6 :(得分:0)
也许这link会有所帮助。
警告:代码不是很简单,而且看起来不太可维护。
uint64_t v; // Input value to find position with rank r.
unsigned int r; // Input: bit's desired rank [1-64].
unsigned int s; // Output: Resulting position of bit with rank r [1-64]
uint64_t a, b, c, d; // Intermediate temporaries for bit count.
unsigned int t; // Bit count temporary.
// Do a normal parallel bit count for a 64-bit integer,
// but store all intermediate steps.
// a = (v & 0x5555...) + ((v >> 1) & 0x5555...);
a = v - ((v >> 1) & ~0UL/3);
// b = (a & 0x3333...) + ((a >> 2) & 0x3333...);
b = (a & ~0UL/5) + ((a >> 2) & ~0UL/5);
// c = (b & 0x0f0f...) + ((b >> 4) & 0x0f0f...);
c = (b + (b >> 4)) & ~0UL/0x11;
// d = (c & 0x00ff...) + ((c >> 8) & 0x00ff...);
d = (c + (c >> 8)) & ~0UL/0x101;
t = (d >> 32) + (d >> 48);
// Now do branchless select!
s = 64;
// if (r > t) {s -= 32; r -= t;}
s -= ((t - r) & 256) >> 3; r -= (t & ((t - r) >> 8));
t = (d >> (s - 16)) & 0xff;
// if (r > t) {s -= 16; r -= t;}
s -= ((t - r) & 256) >> 4; r -= (t & ((t - r) >> 8));
t = (c >> (s - 8)) & 0xf;
// if (r > t) {s -= 8; r -= t;}
s -= ((t - r) & 256) >> 5; r -= (t & ((t - r) >> 8));
t = (b >> (s - 4)) & 0x7;
// if (r > t) {s -= 4; r -= t;}
s -= ((t - r) & 256) >> 6; r -= (t & ((t - r) >> 8));
t = (a >> (s - 2)) & 0x3;
// if (r > t) {s -= 2; r -= t;}
s -= ((t - r) & 256) >> 7; r -= (t & ((t - r) >> 8));
t = (v >> (s - 1)) & 0x1;
// if (r > t) s--;
s -= ((t - r) & 256) >> 8;
s = 65 - s;
答案 7 :(得分:0)
正如已经提到的,x + 1的二进制表示的长度是你正在寻找的n(除非x本身是2的幂,意味着10 ...... 0在二进制表示中) 。 我严重怀疑在O(1)中存在一个真正的解决方案,除非你认为二进制表示的翻译是O(1)。
答案 8 :(得分:0)
对于32位int,以下伪代码将为O(1)。
highestBit(x)
bit = 1
highest = 0
for i 1 to 32
if x & bit == 1
highest = i
bit = bit * 2
return highest + 1
x有多大并不重要,它总是检查所有32位。因此,时间不变。
如果输入可以是任何整数,则输入为n位数。然后读取输入的任何解决方案将读取n个数字,并且必须至少为O(n)。除非有人在没有读取输入的情况下提出解决方案,否则无法找到O(1)解决方案。
答案 9 :(得分:0)
在互联网上进行一些搜索之后,我发现了这2个版本的32位无符号整数。我测试过他们的工作。很明显,为什么第二个有效,但现在我仍在考虑第一个......
1
unsigned int RoundUpToNextPowOf2(unsigned int v)
{
unsigned int r = 1;
if (v > 1)
{
float f = (float)v;
unsigned int const t = 1U << ((*(unsigned int *)&f >> 23) - 0x7f);
r = t << (t < v);
}
return r;
}
2
unsigned int RoundUpToNextPowOf2(unsigned int v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
编辑:第一个也是明确的。
答案 10 :(得分:0)
一个有趣的问题。你的意思是不依赖于大小 int或一个字节中的位数?遇到不同的号码 在一个字节中的位,你将不得不使用另一台机器 一组不同的机器指令,可能会或可能不会影响 答案。
无论如何,基于Mihran提出的第一个解决方案,含糊不清, 我明白了:
int
topBit( unsigned x )
{
int r = 1;
if ( x > 1 ) {
if ( frexp( static_cast<double>( x ), &r ) != 0.5 ) {
++ r;
}
}
return r - 1;
}
这适用于输入值必须精确的约束
代表double
;如果输入为unsigned long long
,则为此
可能不是这样,在一些更具异国情调的平台上,它
甚至可能不是unsigned
的情况。
我可以唯一的其他常数时间(相对于位数) 想到的是:
int
topBit( unsigned x )
{
return x == 0 ? 0.0 : ceil( log2( static_cast<double>( x ) ) );
}
,它与x
完全相同
在double
中可表示,并且还可能遭受舍入错误
浮点运算中固有的(尽管如果log2
是
正确实施,我不认为应该是这种情况)。如果
您的编译器不支持log2
(C ++ 11功能,但也存在
在C90中,我希望大多数编译器已经实现了
当然,log( x ) / log( 2 )
可以使用,但我怀疑
这将增加导致舍入错误的风险
错误的结果。
FWIW,我发现O(1)的位数有点不合逻辑 我在上面指定的原因:位数只是其中之一 “常数因素”取决于您运行的机器。 无论如何,我提出了以下纯粹的整数解决方案,即 O(lg 1)表示位数,O(1)表示其他所有内容:
template< int k >
struct TopBitImpl
{
static int const k2 = k / 2;
static unsigned const m = ~0U << k2;
int operator()( unsigned x ) const
{
unsigned r = ((x & m) != 0) ? k2 : 0;
return r + TopBitImpl<k2>()(r == 0 ? x : x >> k2);
}
};
template<>
struct TopBitImpl<1>
{
int operator()( unsigned x ) const
{
return 0;
}
};
int
topBit( unsigned x )
{
return TopBitImpl<std::numeric_limits<unsigned>::digits>()(x)
+ (((x & (x - 1)) != 0) ? 1 : 0);
}
一个好的编译器应该能够内联递归调用 接近最佳代码。