位運算的妙用_判斷2的乘方和二進制1的個數

判斷一個整數是不是2的乘方

1.題目:實現一個方法,判斷一個正整數是不是2的乘方(好比 16 是 2 的 4 次方,返回 True;18 不是 2 的乘方,返回 False )。要求性能儘量高。java

解法一:
建立一個變量 Temp ,初始值是 1 。而後進入一個循環,循環中每次讓Temp和目標整數比較,若是相等,則說明目標整數是 2 的乘方;若是不相等,則讓 Temp 增大乘 2 ,繼續循環比較。當 Temp 大於目標整數時(因此循環的判斷條件是小於等於),說明目標整數不是 2 的乘方。算法

/**
     * 判斷整數(number)是不是2的乘方
     * 
     * @param number
     * @return
     */
    public static boolean isPower2_ONE(int number) {
        int temp = 1;
        while (temp <= number) {
            if (temp == number) {
                return true;
            }
            temp = temp * 2;
        }
        return false;
    }

 

優化:
先了解一個知識點:<<(左移)乘二,>>(右移)除二性能

具體的介紹:優化

Java的左移和右移不是循環移動,遵循下面的規則:spa

1.右移
右移運算用來將一個數的二進制位序列右移若干位。例如 x>>=2 ,使 x 的各二進制位右移兩位,移到右端的低位被捨棄,最高位則移入原來高位的值。例如:x=00110111,則 x>>2 爲 00001101 ; y=11010011 ,則 y>>2 爲11110100.net

2.左移
左移運算用來將一個數的二進制位序列左移若干位。例如 x<<=2 ,使 x 的各二進制位左移兩位,右邊補 0 ,若x=00001111,則x<<2爲00111100.最高位左移後溢出,捨棄不起做用。code

右移運算至關於對這個數字除2取商,左移運算至關於對這個數字乘2,並且使用左移右移實現乘法除法比使用乘法除法運算速度要快。get

注意:以上等效是在不溢出的狀況下進行。對於負數運算的左移是必然會溢出的源碼

固然,若是要實現對負數的操做,因爲計算機在處理負數的時候是對補碼進行操做,因此除2實際上是對補碼的操做,所以對負數的操做須要對補碼進行處理。it

注意:補碼運算的時候,最與最高位符號位是要保持不變的。另外,若是左移右移大於數據類型長度時候,會先取模。好比 int i ,左移 33 ,會變爲左移 1 ,也就是 33%32

所以咱們能夠把上面循環中的乘 2 ,換成左移一位,由於左移運算符會比乘法的效率快不少。

/**
     * 判斷整數(number)是不是2的乘方(把乘2換成左移一位)
     * 
     * @param number
     * @return
     */
    public static boolean isPower2_TWO(int number) {
        int temp = 1;
        while (temp <= number) {
            if (temp == number) {
                return true;
            }
            temp = temp << 1;
        }
        return false;
    }

 

雖然換成左移運算法效率會變快,但是時間複雜度仍是沒有變化的,也就是說本質仍是沒有改變。

解法二:
觀察下面的圖,咱們能夠發現什麼規律呢?

十進制轉二進制

經過觀察咱們能夠發現:
(1) 凡是2的乘方的正整數,其二進制數必然是以 1 爲首位,其它位都是 0
(2) 若是給它減 1 ,(在位數相同的狀況下)就會變成首位是 0 ,其它位所有是 1 的結果
(3) 0 和 1 的按位與運算結果是 0 ,所以 2 的乘方和他自己減1相與,即 N & N-1,結果必然是 0;也就是說用「位與」運算,獲得的結果是0,就說明這個正整數是2的乘方

因此,2 的乘方都符合一個規律,即 N&N-1 等於 0,因此直接用這個規律判斷便可,可是,這個結論就必定正確嗎?這只是咱們經過觀察部分數據得出的結果,何況咱們的數據量很是的少,怎樣才能保證這個結論是正確的呢?咱們能夠經過反證法來證實:

假設真的存在一個正整數 N,N 不是 2 的冪,可是 N 符合 N&N-1 =0。

由 N 不是 2 的冪能夠推斷出,N 的二進制形式並非除了最高位是1之外,其他爲全是 0 。

既然其他位不全是 0 ,那麼 N-1 的結果的最高位必定不會改變,仍然是1。

既然 N-1 的最高位是 1 ,N的最高位也是 1 ,那麼 N&N-1!=0,和假設矛盾。

由此證實,符合 N&N-1 =0 的正整數必然是 2 的冪。

最後咱們用代碼來實現:

/**
     * 判斷整數(number)是不是2的乘方(位與運算)
     * 
     * @param number
     * @return
     */
    public static boolean isPower2_THREE(int number) {
        return (number & number - 1) == 0;
    }

2、求出一個正整數轉換成二進制後的數字「1」的個數

題目:求出一個正整數轉換成二進制後的數字「1」的個數
如:
int 型數值爲 80
轉化成二進制形式:80 = 00000000 00000000 00000000 01010000
所以 1 的個數爲 2

解法一:
由上面的位運算判斷 2 的乘方能夠知道,n&(n-1) 能夠把整數二進制的最右邊的數由 1 變爲 0 ,利用這個咱們就能夠解決這個問題了。

具體實現的代碼以下:

/**
     * 計算一個int型數值中bit-1的個數
     * 
     * @param n
     * @return
     */
    public static int bitCount1(int n) {
        int count = 0;
        while (n != 0) {
            n = n & (n - 1);
            count++;
        }
        return count;
    }

 

解法二:

咱們也能夠經過移位來解決這題,由於整數的二進制與 1 進行 & 運算的時候,當最末位也就是最右邊的一位爲 1 的時候,結果就是 1 ,判斷完最後一位,而後把整數的二進制右移一位,再判斷,直到整數等於 0 結束循環

/**
     * 計算一個int型數值中bit-1的個數
     * 
     * @param n
     * @return
     */
    public static int bitCount2(int n) {
        int count = 0;
        while (n > 0) {
            if ((n & 1) == 1) {// 若是最右邊的值是1
                count++;
            }
            n >>= 1; // 向右一位
        }
        return count;
    }

但是這種作法不是太好,由於 Java 中 int 佔 4 個字節,一共 32 位,那麼就是說,要循環 32 次才能結束

解法三:

其實這個題目在 Java ,Integer 類中 bitCount 方法的已經解決了的,咱們能夠看下大神們是如何巧妙的解決的。

Jdk中Integer的bitCount源碼

一開始沒想明白怎麼推出來的,最後上網查了一下,

推斷1

推斷2

相關文章
相關標籤/搜索