如果我有一个整数n
,我怎样才能找到的下一个号码k > n
,使得k = 2^i
,其中一些i
的元件N
由按位移动或逻辑。
示例:如果我有n = 123
,我怎么能找到k = 128
,它是2的幂,而不是124
只能被2整除的值。这应该很简单,但这使我难以理解。
如果我有一个整数n
,我怎样才能找到的下一个号码k > n
,使得k = 2^i
,其中一些i
的元件N
由按位移动或逻辑。
示例:如果我有n = 123
,我怎么能找到k = 128
,它是2的幂,而不是124
只能被2整除的值。这应该很简单,但这使我难以理解。
Answers:
对于32位整数,这是一条简单明了的路由:
unsigned int n;
n--;
n |= n >> 1; // Divide by 2^k for consecutive doublings of k up to 32,
n |= n >> 2; // and then or the results.
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n++; // The result is a number of 1 bits equal to the number
// of bits in the original number, plus 1. That's the
// next highest power of 2.
这是一个更具体的例子。让我们以二进制数221(即11011101)为例:
n--; // 1101 1101 --> 1101 1100
n |= n >> 1; // 1101 1100 | 0110 1110 = 1111 1110
n |= n >> 2; // 1111 1110 | 0011 1111 = 1111 1111
n |= n >> 4; // ...
n |= n >> 8;
n |= n >> 16; // 1111 1111 | 1111 1111 = 1111 1111
n++; // 1111 1111 --> 1 0000 0000
第九位有一个位,代表2 ^ 8或256,实际上是2的第二大幂。每个移位都将数字中所有现有的1位与某些先前未触及的零重叠,最终产生等于原始数字中位数的1位数。该值加1会产生新的2的幂。
另一个例子; 我们将使用131,即二进制文件10000011:
n--; // 1000 0011 --> 1000 0010
n |= n >> 1; // 1000 0010 | 0100 0001 = 1100 0011
n |= n >> 2; // 1100 0011 | 0011 0000 = 1111 0011
n |= n >> 4; // 1111 0011 | 0000 1111 = 1111 1111
n |= n >> 8; // ... (At this point all bits are 1, so further bitwise-or
n |= n >> 16; // operations produce no effect.)
n++; // 1111 1111 --> 1 0000 0000
实际上,256是131的第二高幂数。
如果用于表示整数的位数本身是2的幂,则可以继续无限制地有效扩展此技术(例如,n >> 32
为64位整数添加一行)。
k >= n
不是k > n
实际上有一个汇编解决方案(自80386指令集起)。
您可以使用BSR(反向位扫描)指令扫描整数中的最高有效位。
bsr扫描双字操作数或第二个字中从最高有效位开始的位。如果这些位全为零,则清除ZF。否则,将设置ZF,并在反向扫描时将找到的第一个设置位的位索引加载到目标寄存器中
(摘自:http : //dlc.sun.com/pdf/802-1948/802-1948.pdf)
并且比用1增加结果。
所以:
bsr ecx, eax //eax = number
jz @zero
mov eax, 2 // result set the second bit (instead of a inc ecx)
shl eax, ecx // and move it ecx times to the left
ret // result is in eax
@zero:
xor eax, eax
ret
在较新的CPU中,您可以使用快得多的lzcnt
指令(aka rep bsr
)。lzcnt
在一个周期内完成工作。
一种更数学的方式,没有循环:
public static int ByLogs(int n)
{
double y = Math.Floor(Math.Log(n, 2));
return (int)Math.Pow(2, y + 1);
}
2 ^ (floor(log(x) / log(2)) + 1)
n
是2的幂,并且想要获取n
而不是“ 2的下一个幂”,则可以使用2 ^ ( ceil(log(x) / log(2)) )
。但这不是问题(……如何找到下一个数字k > n
……)
这是约翰·费米内拉(John Feminella)的答案,实现为循环,因此它可以处理Python的长整数:
def next_power_of_2(n):
"""
Return next power of 2 greater than or equal to n
"""
n -= 1 # greater than OR EQUAL TO n
shift = 1
while (n+1) & n: # n+1 is not a power of 2 yet
n |= n >> shift
shift <<= 1
return n + 1
如果n已经是2的幂,它也会更快返回。
对于Python> 2.7,对于大多数N来说,这更简单,更快:
def next_power_of_2(n):
"""
Return next power of 2 greater than or equal to n
"""
return 2**(n-1).bit_length()
shift <<= 1
不要使用shift *= 2
。不知道在Python中这是否真的更快,但是应该。
以下摘录适用于下一个数字k> n,以使k = 2 ^ i
(n = 123 => k = 128,n = 128 => k = 256),由OP指定。
如果您希望2的最小乘方大于OR等于n,则只需在上述摘要中替换__builtin_clzll(n)
为即可__builtin_clzll(n-1)
。
constexpr uint64_t nextPowerOfTwo64 (uint64_t n)
{
return 1ULL << (sizeof(uint64_t) * 8 - __builtin_clzll(n));
}
CHAR_BIT
提出的增强功能#include <cstdint>
constexpr uint64_t nextPowerOfTwo64 (uint64_t n)
{
return 1ULL << (sizeof(uint64_t) * CHAR_BIT - __builtin_clzll(n));
}
#include <cstdint>
template <typename T>
constexpr T nextPowerOfTwo64 (T n)
{
T clz = 0;
if constexpr (sizeof(T) <= 32)
clz = __builtin_clzl(n); // unsigned long
else if (sizeof(T) <= 64)
clz = __builtin_clzll(n); // unsigned long long
else { // See https://stackoverflow.com/a/40528716
uint64_t hi = n >> 64;
uint64_t lo = (hi == 0) ? n : -1ULL;
clz = _lzcnt_u64(hi) + _lzcnt_u64(lo);
}
return T{1} << (CHAR_BIT * sizeof(T) - clz);
}
如果您使用的不是GCC或Clang编译器,请访问Wikipedia页面,列出Count Leading Zeroes按位函数:
__builtin_clzl()
为_BitScanForward()
__builtin_clzl()
为__lzcnt()
__builtin_clzl()
为_bit_scan_forward
__builtin_clzl()
为countLeadingZeros()
请在评论中提出改进建议。还建议您使用的编译器或编程语言的替代方案...
如果您使用GCC,MinGW或Clang:
template <typename T>
T nextPow2(T in)
{
return (in & (T)(in - 1)) ? (1U << (sizeof(T) * 8 - __builtin_clz(in))) : in;
}
如果使用Microsoft Visual C ++,请使用function_BitScanForward()
代替__builtin_clz()
。
constexpr uint64_t nextPowerOfTwo64 (uint64_t x) { return 1ULL << (sizeof(uint64_t) * 8 - __builtin_clzll(x)); }
有点纠结,你说?
long int pow_2_ceil(long int t) {
if (t == 0) return 1;
if (t != (t & -t)) {
do {
t -= t & -t;
} while (t != (t & -t));
t <<= 1;
}
return t;
}
每个循环直接剥离最低有效的1位。注意仅当带符号的数字以二进制补码编码时,此功能才有效。
while
为do/while
并保存一个测试,因为您已经通过前面的if
语句确认了初始测试。
do while
。
while(t > (t & -t))
。
您只需要找到最高有效位并将其左移一次即可。这是一个Python实现。我认为x86拥有获取MSB的指令,但是在这里,我将直接用Python来实现它。一旦有了MSB,就很容易。
>>> def msb(n):
... result = -1
... index = 0
... while n:
... bit = 1 << index
... if bit & n:
... result = index
... n &= ~bit
... index += 1
... return result
...
>>> def next_pow(n):
... return 1 << (msb(n) + 1)
...
>>> next_pow(1)
2
>>> next_pow(2)
4
>>> next_pow(3)
4
>>> next_pow(4)
8
>>> next_pow(123)
128
>>> next_pow(222)
256
>>>
// n is the number
int min = (n&-n);
int nextPowerOfTwo = n+min;
n+min+1
吗?