判断一个数是不是2的n次方_带推导和简化、演变

2的n次方都有一个特性,在2进制中,只有1个1。

比如 1,10,100,1000,分别对应1,2,4,8。

0 0 0 0 1 对应的就是1
16 8 4 2 1
0 0 0 0 1
0 0 0 1 0 对应的就是2
16 8 4 2 1
0 0 0 1 0
0 0 1 0 0 对应的就是4
16 8 4 2 1
0 0 1 0 0

以此类推,假如1个2进制数中,只有1个1,那就是2的n次方。

那知道了这个特性,又怎么确定呢?

我们来看看有哪些位运算能帮到我们

符号 描述 运算规则
& 两个位都为1时,结果才为1,否则为0
| 两个位都为0时,结果才为0,否则为1
^ 异或 两个位相同为0,相异为1
~ 取反 0变1,1变0
<< 左移 各二进位全部左移若干位,高位丢弃,低位补0
>> 右移

各二进位全部右移若干位,

对无符号数,高位补0,

有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)

一、&运算

两个位都为1时,结果才为1,否则为0

例1                                                      例2

   001001        (9)                                                    001111      (15)

& 011111         (31)                                              & 000000      (0)

   001001   =    9                                                          000000   =   0

例3                                                     例4

     101001        (41)                                                    101110      (46)

&   010111         (23)                                              &   001100      (12)

     000001   =      1                                                          001100   =   12


二、|运算

两个位都为0时,结果才为0,否则为1

例1                                                      例2

   001001      (9)                                                 001111      (15)

|  011111       (31)                                            |  000000     (0)

   011111  =    31                                                     001111   =   15

例3                                                     例4

    101001        (41)                                                   101110      (46)

|   010111         (23)                                              |    001100      (12)

    000001   =      1                                                         001100   =   12


^运算

两个位相同为0,相异为1

例1                                                      例2

    001001        (9)                                                    001111      (15)

^  011111         (31)                                              ^   000000      (0)

    010110   =      22                                                       001111   =   15

例3                                                     例4

    101001        (41)                                                   101110      (46)

^  010111         (23)                                              ^   001100      (12)

    111110   =      62                                                       100010   =   34


~运算

取反,0变1,1变0

计算机存储数据是以二进制的补码形式来存储的,正数的补码是它本身(如:有二进制00000110,因为他的第一位是0,即代表是正数,反码、补码就是它本身);

负数的补码是它的反码加1,也就是你说的‘取反加一’(如:有二进制10000110,第一位是1,代表它是负数,反码就是每一位都取反,为01111001,所以,补码就是01111010)。

例1                                                     例2

         符号位                                                                   符号位

~           0       0001001        (+9)                          ~        1     0010000      (-15的补码)     

             1       0001010   =      -10                                       0     0001110   =   +14      

 


<<运算

数学意义:在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方。

2 << 2  = 8                                                  3 << 2  = 12 

10  (2)  -> 1000 (8)                                      11  (3)  -> 1100 (12) 


>>运算

>>,有符号右移位,将运算数的二进制整体右移指定位数,整数高位用0补齐,负数高位用1补齐(保持负数符号不变)。

数学意义:在数字没有溢出的前提下,对于正数和负数,右移一位代表除以2的1次方,右移N位代表除以2的N次方。

2 >> 1  = 1                                                  9 >> 2  = 2

10  (2)  -> 1 (1)                                          1001  (9)  -> 10 (2) 


分析题干判断2的n次方,肯定不能使用>>和<<对该数直接操作,一旦溢出,结果就不准确了。

刚才也说了2的n次方,只有一个1。那么这个数减去一个1,二进制就全是1。

比如:1000  -  1   = 111             100   -   1    =    11                10000   -  1  = 1111

利用好这个特性,从&看,两个位都为1时,结果才为1,否则为0

  1000                        100

& 0111                  &   011

————              ————

0                                  0

那么我们找到了答案,设要求的数为a,那么   a  & (a-1) 如果等于0,那么a一定是2的n次方

/// <summary>
/// 判断一个数是否为2的n次方
/// </summary>
/// <param name="n">欲求的数</param>
/// <returns>true=是,false=不是</returns>
public static bool is2pow(int n) {
    return (n & (n - 1)) == 0;
}

举一反三,如果要使用|来求解呢,我们可以发现结果全是1,那联想到刚才的^运算,是不是也全是1。

(两个位都为0时,结果才为0,否则为1)

  1000                        100

| 0111                     |   011

————              ————

1111                            111

 

(两个位相同为0,相异为1)

  1000                        100

^ 0111                     ^  011

————              ————

1111                            111

所以我们这么看 n |(n-1) == n^(n-1)成立的话,是不是也可以判断该数为2的n次方呢,虽然看起来效率要低得多。

/// <summary>
/// 判断一个数是否为2的n次方
/// </summary>
/// <param name="n">欲求的数</param>
/// <returns>true=是,false=不是</returns>
public static bool is2pow_2(int n)
{
    return (n | (n - 1)) == (n ^ ( n - 1)) ;
}

 

验证猜想

哈哈,过程是辛苦的,结果是让人欣慰的。

这就完啦?再来想一想还有没有其他方法呢?

回到刚才第一个方法,a  & (a-1) == 0 可以判断,其实还可以简化一下,初中数学,左右同时加a,减a,得出 n == (n & -n)

于是咱们又得到一条新的公式,这个省去了0,比第一条效率高。

/// <summary>
/// 判断一个数是否为2的n次方
/// </summary>
/// <param name="n">欲求的数</param>
/// <returns>true=是,false=不是</returns>
public static bool is2pow_3(int n)
{
    return   n == (n & -n);
}

看到了这个结果,我们发现了-n,好像想到了啥,!!!刚才的取反符号!

来吧,基于此,又来一条效率不怎么样的公式

/// <summary>
/// 判断一个数是否为2的n次方
/// </summary>
/// <param name="n">欲求的数</param>
/// <returns>true=是,false=不是</returns>
public static bool is2pow_4(int n)
{
    return  (n & (~n + 1)) == n;
}

再来验证,以上公式3和公式4是否正确。

验证猜想

红红火火恍恍惚惚~~~~

结尾

还有位移运算,虽然刚才说不能直接对数进行左右位移运算,但咱可以新建一个数,让它左移,每左移动一次就对该数进行判断,相等就说明是2的次方。

其实抛开位运算,数学的方法可以一直除以2,如果结果不等于1,而且期间余数不等于0就说明不是2的n次方,还有很多方法,比如就计算成二进制字符串,再看看是不是只有一个1。又或者int类型中,二进制的长度是有限的,2的次方就那几个,咱给做成数组,一个一个判断。等等等等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章