如何检查数字是否为2的幂

时间:2009-03-01 19:01:30

标签: c# algorithm math

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

算法必须是:

  1. 简单
  2. 更正任何ulong值。
  3. 我提出了这个简单的算法:

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

    但后来我想,检查log2 x是否是一个完整的数字怎么样?但当我检查2 ^ 63 + 1时,由于四舍五入,Math.Log正好返回63。所以我检查了功率63的2是否等于原始数字 - 它是,因为计算是在double s完成而不是确切的数字:

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

    对于给定的错误值,我们返回true9223372036854775809

    有更好的算法吗?

28 个答案:

答案 0 :(得分:1119)

这个问题有一个简单的技巧:

bool IsPowerOfTwo(ulong x)
{
    return (x & (x - 1)) == 0;
}

注意,此功能会报告true的{​​{1}},这不是0的强项。如果你想排除它,请按照以下方式:

2

解释

首先是按位二进制&amp;来自MSDN定义的运算符:

  

Binary&amp;运算符是为整数类型和bool预定义的。对于   积分类型,&amp;计算其操作数的逻辑按位AND。   对于bool操作数,&amp;计算其操作数的逻辑AND;那   是的,当且仅当两个操作数都为真时,结果才为真。

现在让我们来看看这一切是如何发挥作用的:

该函数返回boolean(true / false)并接受一个unsigned long类型的传入参数(在本例中为x)。让我们为了简单起见假设某人已经传递了值4并且调用了这样的函数:

bool IsPowerOfTwo(ulong x)
{
    return (x != 0) && ((x & (x - 1)) == 0);
}

现在我们用4代替每次出现的x:

bool b = IsPowerOfTwo(4)

好吧,我们已经知道4!= 0 evals to true,到目前为止一直很好。但是怎么样:

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

这当然转化为:

((4 & (4-1)) == 0)

但究竟是什么((4 & 3) == 0)

4的二进制表示为100,3的二进制表示为011(记住&amp;取这些数字的二进制表示)。所以我们有:

4&3

想象一下这些价值就像基本的加法一样堆积起来。 100 = 4 011 = 3 运算符表示如果两个值都等于1,则结果为1,否则为0.所以&1 & 1 = 11 & 0 = 0和{{1 }}。所以我们做数学:

0 & 0 = 0

结果只是0.所以我们回过头来看看我们的返回语句现在转换为:

0 & 1 = 0

现在转换为:

100
011
----
000
return (4 != 0) && ((4 & 3) == 0);

我们都知道return true && (0 == 0); 只是return true && true; ,这表明对于我们的例子,4是2的幂。

答案 1 :(得分:92)

有些网站记录并解释了这个以及其他一些令人讨厌的黑客行为:

他们的爷爷,the book "Hacker's Delight" by Henry Warren, Jr.

正如Sean Anderson's page所解释的那样,表达式((x & (x - 1)) == 0)错误地表明0是2的幂。他建议使用:

(!(x & (x - 1)) && x)

纠正这个问题。

答案 2 :(得分:39)

return (i & -i) == i

答案 3 :(得分:21)

bool IsPowerOfTwo(ulong x)
{
    return x > 0 && (x & (x - 1)) == 0;
}

答案 4 :(得分:20)

我最近在http://www.exploringbinary.com/ten-ways-to-check-if-an-integer-is-a-power-of-two-in-c/写了一篇关于此事的文章。它包括比特计数,如何正确使用对数,经典的“x&amp;!(x&amp;(x - 1))”检查等等。

答案 5 :(得分:15)

这是一个简单的C++解决方案:

bool IsPowerOfTwo( unsigned int i )
{
    return std::bitset<32>(i).count() == 1;
}

答案 6 :(得分:10)

在发布问题后,我想到了以下解决方案:

我们需要检查其中一个二进制数字是否为1。因此,我们只需将数字右移一位数,如果等于1,则返回true。如果在任何时候我们得到一个奇数((number & 1) == 1),我们知道结果是{{ 1}}。这证明(使用基准)比(大)真值的原始方法略快,对于假值或小值更快。

false

当然,Greg的解决方案要好得多。

答案 7 :(得分:10)

    bool IsPowerOfTwo(int n)
    {
        if (n > 1)
        {
            while (n%2 == 0)
            {
                n >>= 1;
            }
        }
        return n == 1;
    }

这是一个通用算法,用于查明数字是否是另一个数字的幂。

    bool IsPowerOf(int n,int b)
    {
        if (n > 1)
        {
            while (n % b == 0)
            {
                n /= b;
            }
        }
        return n == 1;
    }

答案 8 :(得分:8)

接受答案的以下附录可能对某些人有用:

2的幂,以二进制表示时,总是看起来像 1后跟n个零,其中n大于或等于0. Ex:

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个,并且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执行按位AND时会发生什么,这是一个   2的幂,x - 1

x中的一个与x - 1的零对齐,x的所有零与x - 1的零对齐,从而导致按位AND导致0。这就是我们上面提到的单行答案是正确的。

进一步增加了上面接受的答案之美 -

因此,我们现在拥有一处房产:

  

当我们从任何数字中减去1时,则在二进制表示中   最右边的1将变为0,并且最右边的所有零   1现在将成为1

这个属性的一个很棒的用法是找出 - 给定数字的二进制表示中有多少1?给定整数{{1}的短而甜的代码是}:

x

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

是的,每个正数都可以表示为2的幂的总和。对于任何数字,取其二进制表示。例如:取数byte count = 0; for ( ; x != 0; x &= (x - 1)) count++; Console.Write("Total ones in the binary representation of x = {0}", count);

501

答案 9 :(得分:6)

bool isPow2 = ((x & ~(x-1))==x)? !!x : 0;

答案 10 :(得分:4)

查找给定数字是否为2的幂。

#include <math.h>

int main(void)
{
    int n,logval,powval;
    printf("Enter a number to find whether it is s power of 2\n");
    scanf("%d",&n);
    logval=log(n)/log(2);
    powval=pow(2,logval);

    if(powval==n)
        printf("The number is a power of 2");
    else
        printf("The number is not a power of 2");

    getch();
    return 0;
}

答案 11 :(得分:4)

bool isPowerOfTwo(int x_)
{
  register int bitpos, bitpos2;
  asm ("bsrl %1,%0": "+r" (bitpos):"rm" (x_));
  asm ("bsfl %1,%0": "+r" (bitpos2):"rm" (x_));
  return bitpos > 0 && bitpos == bitpos2;
}

答案 12 :(得分:4)

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

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

答案 13 :(得分:4)

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

如果x是2的幂,则其唯一的1位处于位置n。这意味着x – 1的位置n为0。要了解原因,请回想一下二元减法是如何工作的。当从x减去1时,借用一直传播到位置n;位n变为0,所有低位变为1.现在,由于xx – 1没有共同的1位,x & (x – 1)为0,!(x & (x – 1))是的。

答案 14 :(得分:3)

以下是我设计的另一种方法,在这种情况下使用|代替&

bool is_power_of_2(ulong x) {
    if(x ==  (1 << (sizeof(ulong)*8 -1) ) return true;
    return (x > 0) && (x<<1 == (x|(x-1)) +1));
}

答案 15 :(得分:3)

如果数字仅包含1个设置位,则数字为2的幂。我们可以使用此属性和泛型函数countSetBits来查找数字是否为2的幂。

这是一个C ++程序:

int countSetBits(int n)
{
        int c = 0;
        while(n)
        {
                c += 1;
                n  = n & (n-1);
        }
        return c;
}

bool isPowerOfTwo(int n)
{        
        return (countSetBits(n)==1);
}
int main()
{
    int i, val[] = {0,1,2,3,4,5,15,16,22,32,38,64,70};
    for(i=0; i<sizeof(val)/sizeof(val[0]); i++)
        printf("Num:%d\tSet Bits:%d\t is power of two: %d\n",val[i], countSetBits(val[i]), isPowerOfTwo(val[i]));
    return 0;
}

我们不需要明确检查0是2的幂,因为它也为0返回False。

<强>输出

Num:0   Set Bits:0   is power of two: 0
Num:1   Set Bits:1   is power of two: 1
Num:2   Set Bits:1   is power of two: 1
Num:3   Set Bits:2   is power of two: 0
Num:4   Set Bits:1   is power of two: 1
Num:5   Set Bits:2   is power of two: 0
Num:15  Set Bits:4   is power of two: 0
Num:16  Set Bits:1   is power of two: 1
Num:22  Set Bits:3   is power of two: 0
Num:32  Set Bits:1   is power of two: 1
Num:38  Set Bits:3   is power of two: 0
Num:64  Set Bits:1   is power of two: 1
Num:70  Set Bits:3   is power of two: 0

答案 16 :(得分:2)

示例

nothing display

<强>算法

  1. 使用位掩码,将0000 0001 Yes 0001 0001 No 变量分成二进制

  2. NUM

  3. 否则,IF R > 0 AND L > 0: Return FALSE将变为非零

  4. NUM

  5. 否则,请转到步骤1

  6. <强>复杂性

    时间〜IF NUM = 1: Return TRUE其中O(log(d))是二进制数字的数量

答案 17 :(得分:2)

对于任何2的幂,以下也适用。

N'安培;( - N)==Ñ

注意:n = 0失败,因此需要检查它 这有效的原因是:
-n是n的2s补码。与n相比,-n将n翻转的最右设置位左侧的每一位都设置为n。对于2的幂,只有一个设置位。

答案 18 :(得分:2)

改进@ 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)

答案 19 :(得分:1)

如果number是2的幂,则java中的这个程序返回“true”,如果它不是2的幂,则返回“false”

// To check if the given number is power of 2

import java.util.Scanner;

public class PowerOfTwo {
    int n;
    void solve() {
        while(true) {
//          To eleminate the odd numbers
            if((n%2)!= 0){
                System.out.println("false");
                break;
            }
//  Tracing the number back till 2
            n = n/2;
//  2/2 gives one so condition should be 1
            if(n == 1) {
                System.out.println("true");
                break;
            }
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner in = new Scanner(System.in);
        PowerOfTwo obj = new PowerOfTwo();
        obj.n = in.nextInt();
        obj.solve();
    }

}

OUTPUT : 
34
false

16
true

答案 20 :(得分:1)

如果您拥有.NET Core 3 this

,Mark Grall建议使用System.Runtime.Intrinsics.X86.Popcnt.PopCount
public bool IsPowerOfTwo(uint i)
{
    return Popcnt.PopCount(i) == 1
}

单条指令,比(x != 0) && ((x & (x - 1)) == 0)更快,但移植性更差。

答案 21 :(得分:0)

private static bool IsPowerOfTwo(ulong x)
{
    var l = Math.Log(x, 2);
    return (l == Math.Floor(l));
}

答案 22 :(得分:0)

在C语言中,我测试了i && !(i & (i - 1)技巧,并在Linux上使用gcc将其与__builtin_popcount(i)进行了比较,并带有-mpopcnt标志以确保使用CPU的POPCNT指令。我的测试程序计算了0到2 ^ 31之间的整数(是2的幂)。

起初,我以为i && !(i & (i - 1)的速度提高了10%,即使我已验证在使用__builtin_popcount的反汇编中使用了POPCNT。

但是,我意识到我已经包含了一个if语句,并且分支预测可能在位扭曲版本上做得更好。我删除了if,并且POPCNT以更快的速度结束了。

结果:

英特尔(R)酷睿TM i7-4771 CPU最大3.90GHz

Timing (i & !(i & (i - 1))) trick
30

real    0m13.804s
user    0m13.799s
sys     0m0.000s

Timing POPCNT
30

real    0m11.916s
user    0m11.916s
sys     0m0.000s

AMD锐龙Threadripper 2950X 16核处理器最大3.50GHz

Timing (i && !(i & (i - 1))) trick
30

real    0m13.675s
user    0m13.673s
sys 0m0.000s

Timing POPCNT
30

real    0m13.156s
user    0m13.153s
sys 0m0.000s

请注意,这里的Intel CPU似乎比AMD稍慢一些,但是POPCNT更快。 AMD POPCNT没有提供太多的推动力。

popcnt_test.c:

#include "stdio.h"

// Count # of integers that are powers of 2 up to 2^31;
int main() {
  int n;
  for (int z = 0; z < 20; z++){
      n = 0;
      for (unsigned long i = 0; i < 1<<30; i++) {
       #ifdef USE_POPCNT
        n += (__builtin_popcount(i)==1); // Was: if (__builtin_popcount(i) == 1) n++;
       #else
        n += (i && !(i & (i - 1)));  // Was: if (i && !(i & (i - 1))) n++;
       #endif
      }
  }

  printf("%d\n", n);
  return 0;
}

运行测试:

gcc popcnt_test.c -O3 -o test.exe
gcc popcnt_test.c -O3 -DUSE_POPCNT -mpopcnt -o test-popcnt.exe

echo "Timing (i && !(i & (i - 1))) trick"
time ./test.exe

echo
echo "Timing POPCNT"
time ./test-opt.exe

答案 23 :(得分:0)

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

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

count no. of set bits上查看这篇文章

答案 24 :(得分:0)

这也是另一种方法

package javacore;

import java.util.Scanner;

public class Main_exercise5 {
    public static void main(String[] args) {
        // Local Declaration
        boolean ispoweroftwo = false;
        int n;
        Scanner input = new Scanner (System.in);
        System.out.println("Enter a number");
        n = input.nextInt();
        ispoweroftwo = checkNumber(n);
        System.out.println(ispoweroftwo);
    }
    
    public static boolean checkNumber(int n) {
        // Function declaration
        boolean ispoweroftwo= false;
        // if not divisible by 2, means isnotpoweroftwo
        if(n%2!=0){
            ispoweroftwo=false;
            return ispoweroftwo;
        }
        else {
            for(int power=1; power>0; power=power<<1) {
                if (power==n) {
                    return true;
                }
                else if (power>n) {
                    return false;
                }
            }
        }
        return ispoweroftwo;
    }
}

答案 25 :(得分:0)

通过这种方法,您可以检查整数中是否只有1个置1位,并且整数> 0(c ++)。

bool is_pow_of_2(int n){
    int count = 0;
    for(int i = 0; i < 32; i++){
        count += (n>>i & 1);
    }
    return count == 1 && n > 0;
}

答案 26 :(得分:0)

如果数字是 2 的幂,则返回最多 64 个值(您可以在 for 循环条件内更改它(“6”表示 2^6 为 64);

 cp.exec(`start "app" "chrome.exe"`)

答案 27 :(得分:-1)

返回i> 0 &&(i ^ -i)==(-i << 1);

没有找到这样的答案。成为我的