1. 程式人生 > >【演算法】位元位計算(A+B Problem)-位運算子介紹、位運算實現加減乘除

【演算法】位元位計算(A+B Problem)-位運算子介紹、位運算實現加減乘除

問題描述

問題:計算A+B,不適用“+”運算子(LintCode 1.A + B Problem)
注意:A B均為32位整數,可使用位元位計算

解決思路

演算法示例

程式碼如下:(通過LintCode測試)

class Solution {
public:
    /*
     * @param : An integer
     * @param : An integer
     * @return: The sum of a and b
     */
    int aplusb(int a, int b) {
        // write your code here
int s, c; while(b != 0) { s = a^b; c = (a&b)<<1; a = s; b = c; } return a; } };

知識整理

位運算

位運算是按照二進位制進行的運算,c語言提供6個位運算操作符,這些操作符適用於整型運算元。具體6個操作符如下:

符號 名稱 含義
& 按位與 兩個相應的二進位制位都為1,該位的結果值為1
| 按位或 兩個相應的二進位制位中只要有一個為1,該位的結果值為1
^ 按位異或 兩個相應的二進位制位值相同則為0,否則為1
~ 取反 一元運算子,對一個二進位制數按位取反,將0變1,將1變0
<< 左移 將一個數的各二進位制位全部左移N位,右補0
>> 右移 將一個數的各二進位制位右移N位,移到右端的低位被捨棄,無符號數高位補0

1.按位與 &

參與運算的兩個數按照二進位制位依次進行與運算,即兩位都是1結果為1,否則為0。例如:3&6,換算為8位二進位制數(高位補0),為
00000011&
00000110=
00000010

按位與可用於:清零、保留指定位
清零:想將原數清零,只需要原數中所有為1的位,在新數位相應為0,兩數&操作即可為00000000。
保留指定位:想保留原數的第n位,只要新數的第n位保證為1,兩數&操作即可保留原數的第n位,想保留多位同理。

2.按位或 |

參與運算的兩個數對應的二進位制位只要有一個為1,結果為1。例如
00011001 |
01001010=
01011011

按位或可用於:將原數的某些位定值為1,只需要與它進行按位或操作的新數對應的位數為1即可。

3.按位異或 ^

參與運算的兩個數對應二進位制位相同為0,不同為1。例如:
00110011 ^
01000110 =
01110101

按位異或可用於:指定位翻轉(與1異或)、保留原值(與0異或)、交換兩數
交換兩數:交換a和b:
a=a∧b;
b=b∧a;
a=a∧b; 交換成功

4.取反 ~

二進位制位1變0,0變1。

5.左移 <<

根據指定位數將原數各個二進位制位左移指定位,高位左移溢位則捨棄,低位空位則用0補齊。

當該數左移時溢位捨棄的高位中不包含1時,左移類似乘法,左移n位相當於該數乘以2^n

6.右移 >>

根據指定位數將原數各個二進位制位右移指定位,移到右端的低位被捨棄。左端高位,如果無符號數,補0。對於有符號數,移入0稱為邏輯右移,移入1的稱為算術右移

位運算實現加減乘除

1.位運算實現加法

無論哪種進位制的加法,其核心都是“和”、“進位”。位運算有一個特點,首先是:
位運算的異或運算與“和”一致
異或 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) //遞迴形式
{
    if(b==0) //遞迴結束條件:如果右加數為0,即不再有進位,結束運算
        return a;
    int s = a^b;
    int c = (a&b)<<1; //進位左移1位
    return add(s, c); //把'和'和'進位'相加
}

int add(int a, int b) //迴圈形式
{
    int s, c;
    while(b != 0)
    {
        s = a^b;
        c = (a&b)<<1;
        a = s;
        b = c;
    }
    return a;
}

2.位運算實現減法

減法實質使用加法實現的。先把減數求負,然後做加法。對一個數求負的方法是:將該數連符號位一起取反,然後加一
程式碼如下:(先求負,在相加)

int negtive(int i) //求負
{
    return add(~i, 1);
}

int subtraction(int a, int b) //減法運算:利用求負操作和加法操作
{
    return add(a, negtive(b));
}

3.位運算實現乘法

乘法需要考慮符號位,需要進行符號位的提取,以及負數的取正。乘法的實現也可藉助加法,a*b可以看成b個a相加,通過迴圈實現,程式碼如下:(此法慢,其他方法不做贅述)

int getsign(int i){ //取一個數的符號,看是正還是負
    return (i>>31);
}

int bepositive(int i){ //將一個數變為正數,如果本來就是正,則不變;如果是負,則變為相反數。注意對於-2147483648,求負會溢位。
    if(i>>31)
        return negtive(i);
    else
        return i;
}

int multiply(int a, int b){ //迴圈法實現加法
    bool flag = true;
    if(getsign(a) == getsign(b)) //積的符號判定
        flag = false;

    a = bepositive(a);//先把乘數和被乘數變為正數
    b = bepositive(b);
    int ans = 0;
    while(b){
        ans = add(ans, a);
        b = subtraction(b, 1);
    }

    if(flag)
        ans = negtive(ans);
    return ans;
}

4.位運算實現除法

除法可藉助減法實現,從被除數上減去除數,看能減多少次之後變得不夠減。程式碼如下:(此法慢,其他方法不做贅述)

int division(int a, int b){
    if(b==0)
        return 0;

    bool flag = true;
    if(getsign(a) == getsign(b)) //積的符號判定
        flag = false;

    a = bepositive(a);
    b = bepositive(b);

    int n = 0;
    a = subtraction(a, b);
    while(a>=0){
        n = add(n, 1);
        a = subtraction(a, b);
    }

    if(flag)
        n = negtive(n);
    return n;
}

位運算常用技巧

1.計算二進位制數中1的個數

通過與初始值為1的標誌位進行與運算,判斷最低位是否為1;然後將標誌位左移,判斷次低位是否為1;一直這樣計算,直到將每一位都判斷完畢。

int countOf1(int num){
    int count = 0;
    unsigned int flag = 1;
    while(flag){
        if(num & flag)
            count++;
        flag = flag << 1;
    }
    return count;
}

2.判斷某數是否為2的n次方

一個數是2的n次方,則這個數的最高位是1,其餘位為0。

bool is2Power(int num){
    bool flag = true;
    num = num & (num - 1); //計算num和num - 1的與的結果
    if(num) //如果結果為0,則不是2的n次方   
        flag = false;   
    return flag;
}

3.判斷某數的奇偶性

判斷奇偶性,實質是判斷最後一位是否是1。

// 判斷一個數的奇偶性.返回1,為奇數;返回0,為偶數
bool isOdd(int num){
    return num & 1 == 1;
}

4.交換兩數

交換a和b:
a=a∧b;
b=b∧a;
a=a∧b;

5.求絕對值

將原數n右移31位,可以獲得n的符號。

// 若n為正數,得到0;若n為負數,得到-1 
int myAbs(int n){
    return (n ^ n >> 31) - (n >> 31);
}

6.求平均值

int getAverage(int m,int n){
    return (m + n) >> 1;
}