以前的力量2

时间:2010-04-21 01:50:50

标签: algorithm

有很多关于如何找到给定值2的下一个幂的信息(参见参考文献),但我找不到任何以前的2的幂。

到目前为止,我找到的唯一方法是保持一张2到64的所有2的幂,并进行简单的查找。

13 个答案:

答案 0 :(得分:31)

来自Hacker's Delight,一个不错的无分支解决方案:

uint32_t flp2 (uint32_t x)
{
    x = x | (x >> 1);
    x = x | (x >> 2);
    x = x | (x >> 4);
    x = x | (x >> 8);
    x = x | (x >> 16);
    return x - (x >> 1);
}

这通常需要12条指令。如果你的CPU有“计数前导零”指令,你可以少花钱。

答案 1 :(得分:2)

可能是最简单的方法(对于正数):

// find next (must be greater) power, and go one back
p = 1; while (p <= n) p <<= 1; p >>= 1;

如果您想进行优化,可以通过多种方式进行修改。

答案 2 :(得分:2)

uint32_t previous_power_of_two( uint32_t x ) {
    if (x == 0) {
        return 0;
    }
    // x--; Uncomment this, if you want a strictly less than 'x' result.
    x |= (x >> 1);
    x |= (x >> 2);
    x |= (x >> 4);
    x |= (x >> 8);
    x |= (x >> 16);
    return x - (x >> 1);
}

感谢您的回复。我会尝试总结它们并解释一下。 这个算法的作用是在第一个“1”位之后将所有位更改为“1”,因为这些位是唯一能使我们的“x”大于其先前2次幂的位。 在确定它们是'1'之后,它只是移除它们,使第一个'一个'位保持不变。取而代之的是我们之前的两个力量。

答案 3 :(得分:1)

这是后人(红宝石)的一个班轮:

2**Math.log(input, 2).floor(0)

答案 4 :(得分:0)

如果你可以获得2的更高功率,那么下一个更低的2的功率要么是下一个更高,要么是它的一半。这取决于你认为2的任何幂的“下一个更高”(以及你认为是2的下一个更低的幂)。

答案 5 :(得分:0)

怎么样?
if (tt = v >> 16)
{
   r = (t = tt >> 8) ? 0x1000000 * Table256[t] : 0x10000 * Table256[tt];
}
else 
{
  r = (t = v >> 8) ? 0x100 * Table256[t] : Table256[v];
}

这只是来自http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup的修改方法。 这需要7次操作,替换乘法转换可能会更快。

答案 6 :(得分:0)

仅使用位操作的解决方案:

long FindLargestPowerOf2LowerThanN(long n)
{
    Assert.IsTrue(n > 0);

    byte digits = 0;
    while (n > 0)
    {
        n >>= 1;
        digits++;
    }                            

    return 1 << (digits - 1);
}

示例:

FindLargestPowerOf2LowerThanN(6):
Our Goal is to get 4 or 100
1) 6 is 110
2) 110 has 3 digits
3) Since we need to find the largest power of 2 lower than n we subtract 1 from digits
4) 1 << 2 is equal to 100 

FindLargestPowerOf2LowerThanN(132):
Our Goal is to get 128 or 10000000
1) 6 is 10000100
2) 10000100 has 8 digits
3) Since we need to find the largest power of 2 lower than n we subtract 1 from digits
4) 1 << 7 is equal to 10000000 

答案 7 :(得分:0)

g++ 编译器提供了一个内置函数 __builtin_clz 来计算前导零:

所以我们可以这样做:

int previousPowerOfTwo(unsigned int x) {
  return 1 << (sizeof(x)*8 - 1) - __builtin_clz(x);
}

int main () {
  std::cout << previousPowerOfTwo(7)  << std::endl;
  std::cout << previousPowerOfTwo(31) << std::endl;
  std::cout << previousPowerOfTwo(33) << std::endl;
  std::cout << previousPowerOfTwo(8)  << std::endl;
  std::cout << previousPowerOfTwo(91) << std::endl;

  return 0;
}

结果:

4
16
32
8
64

但请注意,对于 x == 0__builtin_clz 返回是未定义的。

答案 8 :(得分:0)

我在这里写下我的答案,以防将来需要参考。

对于 C 语言,这就是我认为是之前 2 次幂函数的“终极”解决方案。以下代码:

  • 针对 C 语言(不是 C++),

  • 如果编译器支持,则使用编译器内置程序生成高效代码 (CLZ or BSR instruction),

  • 是可移植的(标准 C 语言,无汇编),内置程序除外,并且

  • 解决编译器内置函数的未定义行为(当 x 为 0 时)。

如果您使用 C++ 编写,则可以适当地调整代码。请注意,C++20 引入了 std::bit_floor,它的作用完全相同。

#include <limits.h>

#ifdef _MSC_VER
# if _MSC_VER >= 1400
/* _BitScanReverse is introduced in Visual C++ 2005 and requires
   <intrin.h> (also introduced in Visual C++ 2005). */
#include <intrin.h>
#pragma intrinsic(_BitScanReverse)
#pragma intrinsic(_BitScanReverse64)
#  define HAVE_BITSCANREVERSE 1
# endif
#endif

/* Macro indicating that the compiler supports __builtin_clz().
   The name HAVE_BUILTIN_CLZ seems to be the most common, but in some
   projects HAVE__BUILTIN_CLZ is used instead. */
#ifdef __has_builtin
# if __has_builtin(__builtin_clz)
#  define HAVE_BUILTIN_CLZ 1
# endif
#elif defined(__GNUC__)
# if (__GNUC__ > 3)
#  define HAVE_BUILTIN_CLZ 1
# elif defined(__GNUC_MINOR__)
#  if (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
#   define HAVE_BUILTIN_CLZ 1
#  endif
# endif
#endif


/**
 * Returns the largest power of two that is not greater than x. If x
 * is 0, returns 0.
 */
unsigned int prev_power_of_2(unsigned int x)
{
#ifdef HAVE_BITSCANREVERSE
    if (x <= 0) {
        return 0;
    } else {
        unsigned long int index;
        (void) _BitScanReverse(&index, x);
        return (1U << index);
    }
#elif defined(HAVE_BUILTIN_CLZ)
    if (x <= 0) {
        return 0;
    }
    return (1U << (sizeof(x) * CHAR_BIT - 1 - __builtin_clz(x)));
#else
    /* Fastest known solution without compiler built-ins or integer
       logarithm instructions.
       From the book "Hacker's Delight".
       Converted to a loop for smaller code size.
       ("gcc -O3" will unroll this.) */
    {
        unsigned int shift;
        for (shift = 1; shift < sizeof(x) * CHAR_BIT; shift <<= 1) {
            x |= (x >> shift);
        }
    }
    return (x - (x >> 1));
#endif
}

unsigned long long prev_power_of_2_long_long(unsigned long long x)
{
#if (defined(HAVE_BITSCANREVERSE) && \
    ULLONG_MAX == 18446744073709551615ULL)
    if (x <= 0) {
        return 0;
    } else {
        /* assert(sizeof(__int64) == sizeof(long long)); */
        unsigned long int index;
        (void) _BitScanReverse64(&index, x);
        return (1ULL << index);
    }
#elif defined(HAVE_BUILTIN_CLZ)
    if (x <= 0) {
        return 0;
    }
    return (1ULL << (sizeof(x) * CHAR_BIT - 1 - __builtin_clzll(x)));
#else
    {
        unsigned int shift;
        for (shift = 1; shift < sizeof(x) * CHAR_BIT; shift <<= 1) {
            x |= (x >> shift);
        }
    }
    return (x - (x >> 1));
#endif
}

答案 9 :(得分:-1)

当您在基地2工作时,只需添加或删除右侧的数字,即可从2的幂跳到下一个。

例如,数字8的前两个幂是数字4.在二进制:

01000 - &gt; 0100(我们删除尾随零以获得数字4)

因此,解决前两次幂的微积分的算法是:

previousPower:= number shr 1

previousPower = number&gt;&gt; 1

(或任何其他语法)

答案 10 :(得分:-1)

这可以在一行中完成。

int nextLowerPowerOf2 = i <= 0
                        ? 0
                        : ((i & (~i + 1)) == i)
                            ? i >> 1
                            : (1 << (int)Math.Log(i, 2));

结果

i    power_of_2
-2    0
-1    0
0    0
1    0
2    1
3    2
4    2
5    4
6    4
7    4
8    4
9    8

这是c#中更易读的版本,&lt; = 0 guard子句分发给实用程序方法。

int nextLowerPowerOf2 = IsPowerOfTwo(i) 
    ? i >> 1 // shift it right
    : GetPowerOfTwoLessThanOrEqualTo(i);

public static int GetPowerOfTwoLessThanOrEqualTo(int x)
{
    return (x <= 0 ? 0 : (1 << (int)Math.Log(x, 2)));
}

public static bool IsPowerOfTwo(int x)
{
    return (((x & (~x + 1)) == x) && (x > 0));
}

答案 11 :(得分:-2)

下面的代码将找到之前的2的强大功能:

int n = 100;
    n /= 2;//commenting this will gives the next power of 2
    n |= n>>1;
    n |= n>>2;
    n |= n>>4;
    n |= n>>16;

System.out.println(n+1);

答案 12 :(得分:-5)

这是我目前的解决方案,用于查找任何给定正整数n中的两个的下一个和前一个幂,还有一个小函数来确定一个数是2的幂。

此实现适用于Ruby。

class Integer

  def power_of_two?
    (self & (self - 1) == 0)
  end

  def next_power_of_two
    return 1 if self <= 0
    val = self
    val = val - 1
    val = (val >> 1) | val
    val = (val >> 2) | val
    val = (val >> 4) | val
    val = (val >> 8) | val
    val = (val >> 16) | val
    val = (val >> 32) | val if self.class == Bignum
    val = val + 1
  end

  def prev_power_of_two
   return 1 if self <= 0
   val = self
   val = val - 1
   val = (val >> 1) | val
   val = (val >> 2) | val
   val = (val >> 4) | val
   val = (val >> 8) | val
   val = (val >> 16) | val
   val = (val >> 32) | val if self.class == Bignum
   val = val - (val >> 1)
  end
end

使用示例:

10.power_of_two? => false
16.power_of_two? => true
10.next_power_of_two => 16
10.prev_power_of_two => 8

对于之前的2的幂,找到下一个并除以2比上面的方法稍慢。

我不确定它如何与Bignums合作。