今天我需要一个简单的算法来检查一个数字是否是2的幂。

该算法需要:

简单的 适用于任何ulong值。

我想出了这个简单的算法:

private bool IsPowerOfTwo(ulong number)
{
    if (number == 0)
        return false;

    for (ulong power = 1; power > 0; power = power << 1)
    {
        // This for loop used shifting for powers of 2, meaning
        // that the value will become 0 after the last shift
        // (from binary 1000...0000 to 0000...0000) then, the 'for'
        // loop will break out.

        if (power == number)
            return true;
        if (power > number)
            return false;
    }
    return false;
}

但后来我想:如何检查log2x是否恰好是一个整数呢?当我检查2^63+1时,Math.Log()因为四舍五入而返回恰好63。我检查了2的63次方是否等于原来的数,结果是正确的,因为计算是双倍的,而不是精确的数字。

private bool IsPowerOfTwo_2(ulong number)
{
    double log = Math.Log(number, 2);
    double pow = Math.Pow(2, Math.Round(log));
    return pow == number;
}

这对于给定的错误值返回true: 9223372036854775809。

有没有更好的算法?


当前回答

以下对已接受答案的补充可能对某些人有用:

2的幂,当用二进制表示时,总是像1后面跟着n个0,其中n大于等于0。例:

Decimal  Binary
1        1     (1 followed by 0 zero)
2        10    (1 followed by 1 zero)
4        100   (1 followed by 2 zeroes)
8        1000  (1 followed by 3 zeroes)
.        .
.        .
.        .

等等。

当我们把这些数减1,它们就变成0后面跟着n个1,同样,n和上面一样。例:

Decimal    Binary
1 - 1 = 0  0    (0 followed by 0 one)
2 - 1 = 1  01   (0 followed by 1 one)
4 - 1 = 3  011  (0 followed by 2 ones)
8 - 1 = 7  0111 (0 followed by 3 ones)
.          .
.          .
.          .

等等。

说到关键

当我们对一个数字x做位与运算时会发生什么,x是a 2的幂,x - 1呢?

x的1与x - 1的0对齐,x的所有0与x - 1的1对齐,导致按位and的结果为0。这就是为什么上面提到的单行答案是正确的。


进一步增加了上述公认答案的美感

所以,我们现在有一个属性可供我们使用:

当我们用任何数减去1时,那么在二进制表示法中,最右边的1将变成0,而最右边1左边的所有0也将变成1。

这个性质的一个很棒的用途是求出一个给定数字的二进制表示中有多少个1 ?对于给定的整数x,简短而甜蜜的代码是:

byte count = 0;
for ( ; x != 0; x &= (x - 1)) count++;
Console.Write("Total ones in the binary representation of x = {0}", count);

从上面解释的概念可以证明数字的另一个方面是“每个正数都可以表示为2的幂的和吗?”

是的,每一个正数都可以表示成2的幂的和。对于任何数字,取其二进制表示。乘117路车。

The binary representation of 117 is 1110101

Because  1110101 = 1000000 + 100000 + 10000 + 0000 + 100 + 00 + 1
we have  117     = 64      + 32     + 16    + 0    + 4   + 0  + 1

其他回答

return ((x != 0) && !(x & (x - 1)));

如果x是2的幂,它唯一的1位在位置n。这意味着x - 1在位置n有一个0。要了解原因,请回忆一下二进制减法是如何工作的。当x减去1时,借位一直传播到位置n;位n变为0,所有低位变为1。现在,由于x和x - 1没有共同的1位,x & (x - 1)为0,并且!(x & (x - 1))为真。

int isPowerOfTwo(unsigned int x)
{
    return ((x != 0) && ((x & (~x + 1)) == x));
}

这真的很快。检查所有2^32个整数大约需要6分43秒。

我看到很多答案都建议返回n && !(n & (n - 1)),但根据我的经验,如果输入值为负,它会返回假值。 我将在这里分享另一种简单的方法,因为我们知道2的幂数只有一个集位,所以简单地,我们将计算集位的数量,这将花费O(log N)时间。

while (n > 0) {
    int count = 0;
    n = n & (n - 1);
    count++;
}
return count == 1;

检查这篇文章数不清。固定位的

返回(i & -i) == i

改进@user134548的答案,不含比特算术:

public static bool IsPowerOfTwo(ulong n)
{
    if (n % 2 != 0) return false;  // is odd (can't be power of 2)

    double exp = Math.Log(n, 2);
    if (exp != Math.Floor(exp)) return false;  // if exp is not integer, n can't be power
    return Math.Pow(2, exp) == n;
}

这适用于:

IsPowerOfTwo(9223372036854775809)