位運算的一些有用的操作


位運算的騷操作(一)之四則運算

​ 可以這樣說,位運算是我們剛開始學計算機就會接觸到的一種東西。那么位運算這么常見,我們是否可以使用它來做一些騷操作呢?

使用的運算符包括下面(java還有一個>>>無符號右移):

含義 運算符 例子
左移(后面補0) << 0011 => 0110
右移(正數前面補0,負數補1) >> 0110 => 0011
按位或 0011 ------- => 1011 1011
按位與 & 0011 ------- => 1011 1011
按位取反 ~ 0011 => 1100
按位異或 (相同為0不同為1) ^ 0011 ------- => 1000 1011

左移右移運算
右移相當於是除,左移相當於就是乘,左移一位乘以2,左移二位乘以4,依此類推.無論正數、負數,它們的右移、左移、無符號右移32位都是其本身,

位運算與四則運算(這里都是考慮整數【Java版本】)

加法

加法有兩個操作:求和進位

求和:1+1=0 1+0=1 0+0=0

異或:1^1=0 1^0=1 0^0=0

進位:1+1=1 1+0=0 0+0=0

位與:1&1=1 1&0=0 0&0=0

那么這時候,我們使用位運算實現加法肯定是使用異或位與

  • 遞歸版本

    // 使用遞歸實現
    int add(int a,int b){
        // 假如進位為0
        if(b ==0){
            return a;
        }
        // 獲得求和位
        int s = a^b;
        // 獲得進位,然后需要向做移動一位表示進位
        int c = ((a&b)<<1);
        return add(s,c);
    }
  • 非遞歸版本

    非遞歸版本和遞歸版本沒什么不一樣的

     int add2(int a,int b){
    
        int s;
        int c;
        while(b != 0){
            // 獲得求和位
            s = (a^b);
            // 獲得進位,然后需要向做移動一位表示進位
            c = ((a&b)<<1);
            a = s;
            b = c;
        }
        return a;
    }

減法

減法,emm,簡單點來說,就是使用加法來做的,只是說加了一個負數而已

首先我們得先將被減數取反(a取反就是~a+1

int adverse(int a){
    return add(~a,1);
}

減法的完全代碼

// 取得相反數
int adverse(int a){
    return add(~a,1);
}
// 減法函數 其中add就是前面的加法函數
int subtract(int a,int b){
    return add(a,adverse(b));
}

乘法

說乘法的前面我們先考慮下符號問題。

首先呢,我們得知道一個數是正數還是負數

// 負數返回-1,正數返回0
int getsign(int i){
    return (i >> 31);
}

如果為負數,則進行取反

// 如果為負數,則進行取反
int toPositive(int a) {
    if (a >> 31 == -1)
        // 進行取反
        return add(~a, 1);
    else
        return a;
}
  • 解決方法一:

    乘法,我們小時候學習乘法,老師告訴我們,乘法就是累加,例如6*7就是7個6相加而已,so,我們肯定可以使用加法來實現乘法。時間復雜度O(n)。

int multiply(int a, int b){
    Boolean flag = true;
    // 如果相乘為正數,flag為false
    if (getsign(a) == getsign(b))
        flag = false;
    // 將a取正數
    a = toPositive(a);
    b = toPositive(b);
    int re = 0;

    while (b!=0) {
        // 相加
        re = add(re, a);
        // b進行次數減一
        b = subtract(b, 1);
    }
    // 假如結果是負數,則進行取反
    if (flag)
        re = adverse(re);
    return re;
}
  • 解決方法二

    首先先看一張圖吧,這張圖是我用筆記本的觸摸板畫的,難看了一點(en,我自己都受不了的難看),上面畫的是6*2的二進制相乘。

     

    1
    1

     

    上面我們可以發現,相乘有點類似與&,只不過它會進位而已。那么我們的問題就可以簡單化了。相與,然后進位即可,然后再相加。換個方向想:a向左移動,b向右移動,如果b的最后一位為1,則可以將a加上。(時間復雜度O(logn))

    int multiply2(int a, int b) {
        Boolean flag = true;
        // 如果相乘為正數,flag為false
        if (getsign(a) == getsign(b))
            flag = false;
        // 將a取正數
        a = toPositive(a);
        b = toPositive(b);
        int re = 0;
        while (b!=0) {
            // 假如b的最后一位為1
            if((b&1) == 1){
                // 相加
                re = add(re, a);
            }  
            b = (b>>1);
            a = (a<<1);
        }
        // 假如結果是負數
        if (flag)
            re = adverse(re);
        return re;
    }

除法

除法和乘法類似。

  • 解決方法一

    除法解決方法一和乘法的解決方法一類似,從被除數上面減去除數,直到不夠減了,才罷休。時間復雜度O(n)。

  • 解決方法二
    解決方法二的思路是這樣的:從b*(2**i)開始減(2**i代表2的i次方),然后一直減到b*(2**0)。這樣的解決方法的時間復雜度是O(logn)。

    // a/b
    int divide(int a,int b){
       Boolean flag = true;
       // 如果相除為正數,flag為false
       if (getsign(a) == getsign(b))
           flag = false;
       // 將a取正數
       a = toPositive(a);
       b = toPositive(b);
       int re = 0;
       int i = 31;
       while(i>=0){
           // 如果夠減
           // 不用(b<<i)<a是為了防止溢出
           if((a>>i)>=b){
               // re代表結果
               re = add(re, 1<<i);
               a = subtract(a, (b<<i));
           }
           // i減一
           i = subtract(i, 1);
       }
       // 假如結果是負數
       if (flag)
           re = adverse(re);
       return re;
    }

ok,今天的位運算四則運算就到這里了,在這里我都沒有考慮到數據溢出的情況,因為重點不在這里,下次的博客我將會說一下位運算在算法中的一些騷操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM