代码之家  ›  专栏  ›  技术社区  ›  AndreasT

给定一个整数,我如何用比特旋转找到二的下一个最大的幂?

  •  72
  • AndreasT  · 技术社区  · 15 年前

    如果我有一个整数 n ,我怎样才能找到下一个号码 k > n 这样 k = 2^i ,加上一些 i 元素 N 按位移位或逻辑。

    示例:如果我有 n = 123 ,我怎么能找到 k = 128 是二次幂,而不是 124 它只能被二整除。这应该很简单,但我没想到。

    17 回复  |  直到 7 年前
        1
  •  93
  •   James McManus John Feminella    8 年前

    对于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位的数字,等于原始数字中的位数。在该值上加一将产生2的新幂。

    另一个例子;我们将使用131,即二进制中的1000001:

    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的最高功率。

    如果用于表示整数的位数本身是2的幂,则可以继续有效地无限期地扩展此技术(例如,添加 n >> 32 64位整数的行)。

        2
  •  29
  •   Johan    9 年前

    实际上,这是一个汇编解决方案(从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公司 在一个周期内完成它的工作。

        3
  •  21
  •   DanDan    15 年前

    一种更为数学化的方法,没有循环:

    public static int ByLogs(int n)
    {
        double y = Math.Floor(Math.Log(n, 2));
    
        return (int)Math.Pow(2, y + 1);
    }
    
        4
  •  13
  •   JustLoren    15 年前

    这是一个逻辑答案:

    function getK(int n)
    {
      int k = 1;
      while (k < n)
        k *= 2;
      return k;
    }
    
        5
  •  8
  •   endolith    10 年前

    这是John Feminella的答案,实现为一个循环,因此它可以处理 Python's long integers :

    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()
    

    enter image description here

        6
  •  3
  •   I. J. Kennedy ShankarSangoli    15 年前

    这是一个没有循环但使用中间浮点的野生循环。

    //  compute k = nextpowerof2(n)
    
    if (n > 1) 
    {
      float f = (float) n;
      unsigned int const t = 1U << ((*(unsigned int *)&f >> 23) - 0x7f);
      k = t << (t < n);
    }
    else k = 1;
    

    这一点,以及许多其他的琐碎的黑客,包括约翰·费米内拉提交的on,都可以找到。 here .

        7
  •  2
  •   Alex Cohn    13 年前

    假设x不是负数。

    int pot = Integer.highestOneBit(x);
    if (pot != x) {
        pot *= 2;
    }
    
        8
  •  2
  •   oHo Denis Tulskiy    8 年前

    如果使用gcc、mingw或clang:

    template <typename T>
    T nextPow2(T in)
    {
      return (in & (T)(in - 1)) ? (1U << (sizeof(T) * 8 - __builtin_clz(in))) : in;
    }
    

    如果使用微软Visual C++,请使用函数 _BitScanForward() 替代 __builtin_clz() .

        9
  •  1
  •   Rik Heywood    15 年前
    function Pow2Thing(int n)
    {
        x = 1;
        while (n>0)
        {
            n/=2;
            x*=2;
        }
        return x;
    }
    
        10
  •  1
  •   Derek Illchuk    10 年前

    你说有点无聊?

    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;
    }
    

    每个循环直接剥离最低有效位。注意:这只适用于有符号的数字被编码为二的补码的情况。

        11
  •  1
  •   oHo Denis Tulskiy    7 年前

    大于/大于或等于

    以下代码段用于 下一个数字k>n这样k=2^i
    (n=123=>k=128,n=128=>k=256),如操作说明所示。

    如果你想要 2的最小幂大于或等于n 那就换一个吧 __builtin_clzll(n) 通过 __builtin_clzll(n-1) 在上面的片段中。

    使用GCC或Clang(64位)的C++ 11

    constexpr uint64_t nextPowerOfTwo64 (uint64_t n)
    {
        return 1ULL << (sizeof(uint64_t) * 8 - __builtin_clzll(n));
    }
    

    增强使用 CHAR_BIT 由提议 martinec

    #include <cstdint>
    
    constexpr uint64_t nextPowerOfTwo64 (uint64_t n)
    {
        return 1ULL << (sizeof(uint64_t) * CHAR_BIT - __builtin_clzll(n));
    }
    

    使用GCC或Clang(从8到128位)的C++ 17

    #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,请访问维基百科页面,列出 Count Leading Zeroes bitwise functions :

    • Visual C++ 2005 = & gt;替换 __builtin_clzl() 通过 _BitScanForward()
    • Visual C++ 2008 = & gt;替换 _内置的_Clzl() 通过 __lzcnt()
    • ICC=>替换 _内置的_Clzl() 通过 _bit_scan_forward
    • ghc(haskell)=>替换 _内置的_Clzl() 通过 countLeadingZeros()

    捐款欢迎

    请在评论中提出改进意见。还建议您使用的编译器或编程语言的替代方案…

    另见类似答案

        12
  •  0
  •   Eric    15 年前

    像这样的东西怎么样:

    int pot = 1;
    for (int i = 0; i < 31; i++, pot <<= 1)
        if (pot >= x)
            break;
    
        13
  •  0
  •   FogleBird    15 年前

    您只需要找到最重要的位,然后将其左移一次。下面是一个Python实现。我认为x86有一个获取MSB的指令,但这里我将用纯Python实现它。一旦你有了最高有效位,就很容易了。

    >>> 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
    >>>
    
        14
  •  0
  •   nunkown    11 年前

    算了吧!它使用循环!

         unsigned int nextPowerOf2 ( unsigned int u)
         {
             unsigned int v = 0x80000000; // supposed 32-bit unsigned int
    
             if (u < v) {
                while (v > u) v = v >> 1;
             }
             return (v << 1);  // return 0 if number is too big
         }
    
        15
  •  0
  •   lavish    9 年前
    private static int nextHighestPower(int number){
        if((number & number-1)==0){
            return number;
        }
        else{
            int count=0;
            while(number!=0){
                number=number>>1;
                count++;
            }
            return 1<<count;
        }
    }
    
        16
  •  -3
  •   Paras    14 年前
    // n is the number
    int min = (n&-n);
    int nextPowerOfTwo = n+min;
    
        17
  •  -3
  •   seh Alexei    9 年前
    #define nextPowerOf2(x, n) (x + (n-1)) & ~(n-1)
    

    甚至

    #define nextPowerOf2(x, n)  x + (x & (n-1))