find两个大于或等于给定值的最小幂的algorithm

我需要find两个大于或等于给定值的最小幂。 到目前为止,我有这样的:

int value = 3221; // 3221 is just an example, could be any number int result = 1; while (result < value) result <<= 1; 

它工作正常,但感觉有点天真。 这个问题有更好的algorithm吗?

编辑。 有一些不错的汇编build议,所以我将这些标签添加到问题。

这是我最喜欢的 除了初始检查是否无效(<0,如果你知道只有> = 0的数字,你可以跳过),它没有循环或条件,因此将胜过大多数其他方法。 这与埃里克森的答案类似,但我认为我在开始时递减1,最后加1会比他的回答less一些尴尬(也避免了最后的条件)。

 /// Round up to next higher power of 2 (return x if it's already a power /// of 2). inline int pow2roundup (int x) { if (x < 0) return 0; --x; x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; return x+1; } 
 ceil(log2(value)) 

ilog2()可以用3个asm指令计算,例如http://www.asterisk.org/doxygen/1.4/log2comp_8h-source.html

在Quake II的0x5f3759df和Bit Twiddling Hacks的IEEE版本的精神下,这个解决scheme达到了双倍提取指数作为计算floor(lg2(n))的方法。 它比接受的解决scheme快一点,比Bit Twiddling IEEE版本要快得多,因为它避免了浮点math。 作为编码,它假设一个double是一个真正的* 8 IEEE小浮动机器上的浮点数。

 int nextPow2(int n) { if ( n <= 1 ) return n; double d = n-1; return 1 << ((((int*)&d)[1]>>20)-1022); } 

编辑:在同事的帮助下添加优化的x86汇编版本。 速度增加4%,但仍比bsr版本慢了约50%(n = 1..2 ^ 31-2时,我的笔记本电脑上的速度为6秒vs4。

 int nextPow2(int n) { if ( n <= 1 ) return n; double d; n--; __asm { fild n mov eax,4 fstp d mov ecx, dword ptr d[eax] sar ecx,14h rol eax,cl } } 

在Intel硬件上,BSR指令接近你想要的 – find最重要的设置位。 如果你需要更精确的话,你可以想知道其余的位是否精确到零。 我倾向于认为其他CPU将有类似BSR – 这是一个问题,你想要回答正常化一个数字。 如果您的号码超过32位,那么您将从最重要的DWORD执行扫描,find设置了任何位的第一个DWORD。 Edsger Dijkstra可能会说,上面的“algorithm”假设你的计算机使用二进制数字,而从他那种崇高的“algorithm”的angular度来看,你应该考虑一下图灵机器或者什么 – 显然我更加务实。

你的实现并不幼稚,实际上它是合乎逻辑的,除了它是错误的 – 它会返回大于最大整数大小的1/2的数字。

假设你可以将数字限制在0到2 ^ 30的范围内(对于32位整数),它可以工作得很好,比任何涉及对数的math函数都快。

无符号整数可以更好地工作,但是最终会出现一个无限循环(对于大于2 ^ 31的数字),因为用<<运算符决不能达到2 ^ 32。

这是一个移位技术的模板版本。

 template<typename T> T next_power2(T value) { --value; for(size_t i = 1; i < sizeof(T) * CHAR_BIT; i*=2) value |= value >> i; return value+1; } 

由于循环只使用常量,因此编译器会将其展平。 (我查过)function也是未来的certificate。

这是一个使用__builtin_clz。 (也是未来的certificate)

 template<typename T> T next_power2(T value) { return 1 << ((sizeof(T) * CHAR_BIT) - __builtin_clz(value-1)); } 

pow(2,ceil(log2(value));

log2(value)= log(value)/ log(2);

在Bit Twiddling Hacks页面上可以find关于密切相关的问题(也就是四舍五入而不是up)的可能解决scheme,这些解决scheme可以在Bit Twiddling Hacks页面上find,这是一个很好的资源来进行各种优化你正在寻找。 最快的解决scheme是使用带有256个条目的查找表,从而使总操作次数从平均62次(通过类似的操作计数方法)减less到7次左右。 使这些解决scheme适应您的问题是一个单一的比较和增量的问题。

如何recursion模板版本来生成一个编译常量:

 template<uint32_t A, uint8_t B = 16> struct Pow2RoundDown { enum{ value = Pow2RoundDown<(A | (A >> B)), B/2>::value }; }; template<uint32_t A> struct Pow2RoundDown<A, 1> { enum{ value = (A | (A >> 1)) - ((A | (A >> 1)) >> 1) }; }; template<uint32_t A, uint8_t B = 16> struct Pow2RoundUp { enum{ value = Pow2RoundUp<((B == 16 ? (A-1) : A) | ((B == 16 ? (A-1) : A) >> B)), B/2>::value }; }; template<uint32_t A > struct Pow2RoundUp<A, 1> { enum{ value = ((A | (A >> 1)) + 1) }; }; 

可以这样使用:

 Pow2RoundDown<3221>::value, Pow2RoundUp<3221>::value 

你并没有真正地说出“更好的algorithm”的含义,但是你所提出的algorithm是完全清楚的(如果有些瑕疵的话),我会认为你是在更高效的algorithm之后。

拉里·格里茨(Larry Gritz)给出了可能是最有效的c / c ++algorithm,没有查找表的开销,在大多数情况下它就足够了(参见http://www.hackersdelight.org的类似algorithm)。;

正如其他地方所提到的,目前大多数CPU都有机器指令来计算前导零的数量(或者等同地返回ms设置位),但是它们的使用是不可移植的,并且在大多数情况下是不值得的。

然而,大多数编译器具有“内在”function,允许使用机器指令,但是以更便携的方式。

微软C ++有_BitScanReverse()和gcc提供__builtin_clz()来高效地完成大部分的工作。

下面的代码反复剥去最低位,直到数字是2的幂,然后双倍结果,除非数字是2的幂开始。 它具有与所设置的比特数成比例的时间运行的优点。 不幸的是,它在几乎所有的情况下比在问题中的代码或汇编build议中都要求更多的指示。 我只是为了完整而包含它。

 int nextPow(int x) { int y = x while (x &= (x^(~x+1))) y = x << 1; return y } 

我知道这是downvote诱饵,但如果数量足够小(如8或16位)直接查找可能是最快的。

 // fill in the table unsigned short tab[65536]; unsigned short bit = tab[i]; 

通过首先处理高位字和低位字,可能将其扩展到32位。

 // unsigned long bitHigh = ((unsigned long)tab[(unsigned short)(i >> 16)]) << 16; unsigned long bitLow = 0; if (bitHigh == 0){ bitLow = tab[(unsigned short)(i & 0xffff)]; } unsigned long answer = bitHigh | bitLow; 

这可能不是更好的方法,但也许可以扩展到更大的单词大小。

(实际上,这是最高的1位,你必须把它左移1来得到下一个更高的2的幂)

我的版本一样:

 int pwr2Test(size_t x) { return (x & (x - 1))? 0 : 1; } size_t pwr2Floor(size_t x) { // A lookup table for rounding up 4 bit numbers to // the nearest power of 2. static const unsigned char pwr2lut[] = { 0x00, 0x01, 0x02, 0x02, // 0, 1, 2, 3 0x04, 0x04, 0x04, 0x04, // 4, 5, 6, 7 0x08, 0x08, 0x08, 0x08, // 8, 9, 10, 11 0x08, 0x08, 0x08, 0x08 // 12, 13, 14, 15 }; size_t pwr2 = 0; // The return value unsigned int i = 0; // The nybble interator for( i = 0; x != 0; ++i ) { // Iterate through nybbles pwr2 = pwr2lut[x & 0x0f]; // rounding up to powers of 2. x >>= 4; // (i - 1) will contain the } // highest non-zero nybble index. i = i? (i - 1) : i; pwr2 <<= (i * 4); return pwr2; } size_t pwr2Size(size_t x) { if( pwr2Test(x) ) { return x; } return pwr2Floor(x) * 2; } 

我喜欢这个转变。

我会解决的

  int bufferPow = 1; while ( bufferPow<bufferSize && bufferPow>0) bufferPow <<= 1; 

这样循环总是终止,&&之后的部分几乎从不被评估。 我不认为两行是值得的函数调用。 根据你的判断,你也可以做出很长的或短的,这是非常可读的。 (如果bufferPow变成负数,希望你的主代码快速退出。)

通常你只在algorithm开始时计算2次方,所以无论如何优化都是愚蠢的。 然而,如果有人感到厌倦,会感到兴趣,会关心速度比赛…使用上述的例子和255 256 257 .. 4195 4196 4197

一个任意的日志函数可以通过除以2的日志被转换为日志基2:

 $ /usr/local/pypy-1.9/bin/pypy Python 2.7.2 (341e1e3821ff, Jun 07 2012, 15:38:48) [PyPy 1.9.0 with GCC 4.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. And now for something completely different: ``<arigato> yes but there is not much sense if I explain all about today's greatest idea if tomorrow it's completely outdated'' >>>> import math >>>> print math.log(65535)/math.log(2) 15.9999779861 >>>> print math.log(65536)/math.log(2) 16.0 >>>> 

由于涉及浮点运算,当然不会100%精确。

这工作,是非常快速的(在我的2.66 GHz的英特尔酷睿2双核64位处理器)。

 #include <iostream> int main(void) { int testinput,counter; std::cin >> testinput; while (testinput > 1) { testinput = testinput >> 1; counter++; } int finalnum = testinput << counter+1; printf("Is %i\n",finalnum); return 0; } 

我在3,6和65496上进行了testing,给出了正确的答案(4,8和65536)。

对不起,如果这看起来有点神秘,在写作之前我受到了Doom几个小时的影响。 🙂