判斷一個數是不是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的次方就那幾個,咱給做成數組,一個一個判斷。等等等等。

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