当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何检查数字是否为2的幂》经验,为你挑选了10个好方法。

今天我需要一个简单的算法来检查一个数是否是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;
}

但后来我想,如果检查是否是一个完整的圆数呢?但是当我检查2 ^ 63 + 1时,因为四舍五入而准确地返回了63.所以我检查了功率63的2是否等于原始数字 - 它是,因为计算是在s中完成而不是在确切的数字中:log2 xMath.Logdouble

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.

有更好的算法吗?



1> Greg Hewgill..:

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

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

注意,此功能将报告true0,这是不是一个动力2.如果你想排除它,这是如何:

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

说明

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

二进制和运算符是为整数类型和bool预定义的.对于整数类型,&计算其操作数的逻辑按位AND.对于bool操作数,&计算其操作数的逻辑AND; 也就是说,当且仅当它的两个操作数都为真时,结果才为真.

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

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

bool b = IsPowerOfTwo(4)

现在我们将每次出现的x替换为4:

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

好吧,我们已经知道4!= 0 evals to true,到目前为止一直很好.但是关于:

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

这当然转化为:

((4 & 3) == 0)

但到底是4&3什么?

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

100 = 4
011 = 3

想象一下,这些价值就像基本的加法一样堆积起来.该&运营商表示,如果这两个值等于1,则结果为1,否则为0,所以1 & 1 = 1,1 & 0 = 0,0 & 0 = 0,和0 & 1 = 0.所以我们做数学:

100
011
----
000

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

return (4 != 0) && ((4 & 3) == 0);

现在翻译为:

return true && (0 == 0);
return true && true;

我们都知道这true && true很简单true,这表明对于我们的例子,4是2的幂.


@Kripp:数字将是二进制形式1000 ... 000.当你-1它时,它的形式为0111 ... 111.因此,两个数字的二进制和结果将是000000.这不会发生非二次幂,因为1010100例如将变为1010011,导致(继续......)
...导致二进制和之后的1010000.唯一的误报是0,这就是为什么我会使用:return(x!= 0)&&((x&(x - 1))== 0);
@ShuggyCoUk:两个补码是负数表示的方式.由于这是无符号整数,因此负数的表示无关紧要.该技术仅依赖于非负整数的二进制表示.
克里普,考虑(2:1,10:1)(4:3,100:11)(8:7,1000:111)(16:15,10000:1111)看模式?
@SoapBox - 什么更常见?零或非零数字不是两个的幂?如果没有更多的背景,这是一个你无法回答的问题.它真的,_really_无论如何都无所谓.
因为评论是挑剔的事情(并且可能被删除),我已经在评论中添加了作为"编辑注释"的答案.
统计上,零不是比其他40亿不是2的幂的数字更常见.因此,对于40亿个案例,您将进行两次比较,其中一个可以工作.它确实有所不同,因为在C和C++中,如果左侧是假的,则甚至不会检查右侧.所以你要保存比较.这并不是什么大问题,但是在阅读和编写代码时很容易思考,没有理由不去做.
@SoapBox:如果你进行那种微优化,那么使用单个`&`代替`&&`可能比两个命令都快.当我们处理布尔值时,它会产生相同的结果,但是`&`没有定义一个序列点,这通常需要很高的成本(嗯,实际上我们必须测量以确保它有任何区别).
我可以建议一个正确过滤"0"的无分支版本吗?就是这样:`((x | 0x8000000000000000)&(x - 1))== 0`.其工作原因可以在[这里](https://gist.github.com/4666248)找到.
@snemarch:这是64位整数(在问题中指定).16位使用"0x8000",32位是"0x80000000".所以,一般来说,对于_n_-bit,使用`1 <<(n - 1)`.

2> Michael Burr..:

一些记录和解释这个以及其他一些杂乱无章的黑客的网站是:

http://graphics.stanford.edu/~seander/bithacks.html
(http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2)

http://bits.stephan-brumme.com/
(http://bits.stephan-brumme.com/isPowerOfTwo.html)

还有他们的祖父,小亨利·沃伦(Henry Warren,Jr.)写的"黑客的喜悦"一书:

http://www.hackersdelight.org/

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

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

纠正这个问题.


0是2的幂... 2 ^ -inf = 0.;););)
由于这是一个___ C#___标记的线程,值得指出的是(Sean Anderson的)最后一个表达式在C#中是非法的,因为`!`只能应用于布尔类型,而`&&`也需要两个操作数都是boolean-(除了用户定义的运算符使其他事情成为可能,但这与`ulong`无关.)

3> Andreas Pete..:

return (i & -i) == i


它利用了二进制补码表示法的一个属性:计算数字的负值,执行按位求反,并将1加到结果中.设置的最低位`i`也将在`-i`中设置.下面的位将是0(在两个值中),而其上方的位将相对于彼此反转.因此,`i&-i`的值将是`i`中最不重要的设置位(它是2的幂).如果`i`具有相同的值,那么这是唯一的位集.当`i`为0时,它失败的原因与`i&(i - 1)== 0`相同.
如果`i`是无符号类型,则二进制补码与它无关.你只是利用了模运算和按位的特性.
任何暗示为什么会这样或不会起作用?我只在java中检查了它的正确性,其中只有签名的int/long.如果它是正确的,这将是最好的答案.快+小

4> Matt Howells..:
bool IsPowerOfTwo(ulong x)
{
    return x > 0 && (x & (x - 1)) == 0;
}


这个解决方案更好,因为如果负数可以传入,它也可以处理负数.(如果长而不是ulong)

5> Rick Regan..:

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



6> deft_code..:

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

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


在gcc上,这会编译成一个名为`__builtin_popcount`的单个gcc内置函数.不幸的是,一系列处理器还没有单一的汇编指令来执行此操作(x86),因此它是比特计数的最快方法.在任何其他架构上,这是一个汇编指令.
@deft_code更新的x86微体系结构支持`popcnt`

7> configurator..:

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

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

private static bool IsPowerOfTwo(ulong number)
{
    while (number != 0)
    {
        if (number == 1)
            return true;

        if ((number & 1) == 1)
            // number is an odd number and not 1 - so it's not a power of two.
            return false;

        number = number >> 1;
    }
    return false;
}

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



8> Raz Megrelid..:
    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;
    }



9> displayName..:

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

当以二进制表示时,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)
.          .
.          .
.          .

等等.

来到了症结所在

当我们对数字进行按位AND时,会发生什么x,这是2的幂,并且x - 1

x得到的一个与零的对齐x - 1和所有的零x对齐x - 1,导致按位AND得到0.这就是我们如上所述的单行答案是正确的.


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

所以,我们现在拥有一处房产:

当我们从任何数字中减去1时,那么在二进制表示中,最右边的1将变为0,而最右边的1之前的所有零将变为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



10> abelenky..:
bool isPow2 = ((x & ~(x-1))==x)? !!x : 0;

推荐阅读
TXCWB_523
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有