1. 程式人生 > >神奇的位運算(bitwise trick)

神奇的位運算(bitwise trick)

   在計算機中,資料都以二進位制補碼的形式儲存,根據這一特點,適當採用位運算(bitwise operation)可以很巧妙地解決問題,同時運算效率更高。時刻牢記,最大的負數是-1,在計算機中,它的儲存形式是全1。

位運算子

  • 左移<< 和 右移>>

   左移相當於乘以2,友誼相當於除以2.在計算機中,位運算比乘法、除法運算要快得多,所以適當採用移位運算取代乘除運算,能夠提高運算效率。右移時要注意符號位的擴充套件。

  • 按位與&

   &運算的特點是可以將某些位置為0。
   &運算也可用於提取某些特定的位。做法是定義一個mask,mask中對應要提取的位置為1,而其它位為0,將整數與mask做&運算,就可以提取出整數中相應的位。

  • 按位或|

   |運算的特點是,可以將指定的位元置為1。

  • 按位異或^

   以下運算的規律在解決問題時可能是會經常用到的。
   x^x=0
  bit^1=~bit x^(-1)=~x
  bit^0=bit x^0=x

  • 按位取反~

   ~運算的特點是將0和1點到。以下規律值得牢記,此規律對正整數或負整數都成立。
   -x=~x+1

一些小技巧

   位運算結合在一起,惡意巧妙地解決很多問題,下面就是一些bitwise trick

  • 交換兩個int
void swap(int &x, int &y)
{
    x = x
^y; y = x^y; //(x^y)^y = x^(y^y) = x^0 = x; x = x^y; //(x^y)^x = (x^x)^y = 0^y = y; }

   最早以前,我所知道的交換方法是下面這樣的

void swap(int &x,int &y)
{
    x = x+y;
    y = x-y;
    x = x-y;
}

   這樣在大多數情況下也是可以的,但是不及位運算。首先,撇開運算速度不說,通過加減交換最大的隱患在於,如果x和y的值都很大,那麼在計算x+y的時候,可能發生溢位,這在位運算中是絕對不會出現的。

  • 判斷一個數是不是2的冪
void PowerOf2(int x)
{
    if (x == 0)
        return false;
    return x&(x-1) == 0;
}

   x&(x-1)的作用是,使得x最右邊的1變為0,x是2的冪,意味著x中只有一個1,所以經過這一運算後,x&(x-1)等於0。

   還有一個特點需要注意的是,如果x是2的冪,那麼x-1的形式是,左邊全為0,右邊全為1。

   另一個解為return x&(-x) ==x;,但是具體怎麼解,我還不明所以。

   x&(x-1)的作用不容小覷,我們可以用它來計算整數中1的個數,並藉此來對整數做奇偶校驗。

  • 計算整數中1的個數
int CountOf1(int x)
{
    int count=0;
    while(x)
    {
        count ++;
        x = x&(x-1);    //將x最低位的1置為0
    }
    return count;
}
  • 計算m和n中不同的位的個數
int diffmn(int m,int n)
{
    int count=0;
    int x = m^n;    //x中為1的位元代表了m和n中不同的位
    while(x)
    {
        count ++;
        x = x&(x-1);
    }
    return count;
}
  • 整數加1和整數減1
int plus1(int x)
{
    return -~x;    //x+1 = ~(~x)+1 = -(~x)
}
int minus1(int x)
{
    return ~-x;    //x = -(-x) = ~(-x)+1
}

   注意:通過上述方法計算整數的加1或減1,比直接計算是要慢的。不過,這兩個例子充分說明了位運算可以做的事情是很多的。

   老實說,這兩個結果應該不是那麼容易記清的吧,起碼對我來說是這樣的,牢記-x=~x+1,並記住大概的形式,應該就能夠通過簡單的變換很快得出正確的結果的吧。

  • 整數反號
void revsign(int x)
{
    return ~x+1;    //-x = ~x+1
}

void revsign(int x)
{
    return (x^(-1))+1;    //(x^(-1))+1 = ~x+1 = -x
}

   顯然,第二種方法是不如第一種方法的,還是那句話,通過推導,好好體會一下位運算的神奇效果吧。

  • 判斷一個數是否是奇數
bool isodd(int x)
{
    return x&1
}

   偶數的判斷當然也很簡單了!

  • 計算32位整數的絕對值
int abs(int x)
{
    return (x&(x>>31))-(x>>31);
}

   首先需要注意的是右移時會擴充套件符號位。如果x是正數,那麼x>>31=0,毫無疑問,return語句中的表示式結果就是x;如果x是負數,那麼x>>31的結果就是全1,也就是我們必須牢記的-1,於是

 (x^(x>>31)) - (x>>31)
=(x^(-1)) - (-1)
=~x + 1
=-x

   所以,(x^(x>>31))-(x>>31)就是我們要求的x的絕對值。

  • 求a和b的最大值
int max(int a,int b)
{
    int maxab = b&((a-b)>>31) | a&(~(a-b)>>31);
    return maxab;
}

   對於unsigned int,右移31位是一個很特別的操作,其作用是將所有的位都置為符號位的值,也就是說,正數移位後變為0,負數移位後變為-1。所以,如果a<b,那麼(a-b)>>31結果就是-1,而~(a-b)>>31結果就是0,最終maxab = b;反過來,如果a>b,那麼(a-b)>>31結果是0,而~(a-b)>>31結果是-1,最終maxab = a

   需要注意的是,此處假定在32位機器上進行操作,因此移位的位數是31,為了保證表示式的通用性,可以通過sizeof(int)*8 - 1來確定移位的位數。

   下面是另外一種解法,成立的條件是true=1,false=0

int max(int a,int b)
{
    int maxab = a^((a^b)&(-(a<b)));
    return maxab;
}

   如果a<b,那麼-(a<b)就是-1,於是maxab = a^(a^b) = b;如果a>b,那麼-(a<b)就是0,於是maxab = a

   可以看到,兩種解法的核心都是將a、b的關係通過-1或0來間接表示,並利用表示結果作進一步的運算。

  • 求a和b的最小值
int min(int a,int b)
{
    int minab = a&((a-b)>>31) |b&(~(a-b)>>31);
    return minab;
}

   同樣的,只要滿足true=1,false=0,也同樣可以採用第二種方法來求最小值。

int min(int a)
{
    int minab = b^((a^b)&(-(a<b)));
    return minab;
}

   推導過程與注意事項同求最大值的解法是基本相同的,在此不做贅述。

  • 計算兩個數的平均值
int mean(int x,int y)
{
    return (x&y)+((x^y)>>1);
}

   我們知道,計算兩個數的平均值,最直接的想法就是(x+y)/2,表示式就是通過位運算實現了這一想法。表示式的核心思想是,將x+y分解成兩部分,一部分是x和y相同的位,一部分是x和y不同的位,前者通過&運算完成相加(由於沒有進位,所以無需除以2),後者通過^運算完成相加,然後除以2得出平均值,最後,將兩部分相加,就是所求的結果了。

   同樣的,通過位運算,可以成功規避相加溢位的問題。

   在此,我們瞥見了位運算的一抹影子,你當然可以在其他很多地方領略到更多神奇之處,比如在演演算法筆記中。

相關推薦

神奇運算(bitwise trick)

   在計算機中,資料都以二進位制補碼的形式儲存,根據這一特點,適當採用位運算(bitwise operation)可以很巧妙地解決問題,同時運算效率更高。時刻牢記,最大的負數是-1,在計算機中,它的儲存形式是全1。 位運算子 左移<< 和

神奇運算

eof googl 去掉 www 知識點 binarys 深入理解 網上 負數 位運算的威力 前言: 在學習Java二進制各種轉換時,發現對位運算很不熟悉,怪我基礎沒打好,更要好好學習了。然後從網上搜刮了一些位運算的小應用,然後有一些不熟悉的自己用Java代碼試了一下,發

神奇運算(不會不行啊。。。被坑了好多題了。。。。)

位運算應用口訣 清零取反要用與,某位置一可用或 若要取反和交換,輕輕鬆鬆用異或 移位運算 要點 1 它們都是雙目運算子,兩個運算分量都是整形,結果也是整形。     2 " < <" 左移:右邊空出的位上補0,左邊的位將從字頭擠掉,其值相當於乘2。

神奇運算——二進位制中1的個數

輸入一個整數,輸出該數二進位制表示中1的個數。其中負數用補碼錶示。 方法1:依次右移,使最右側的1依次消失。但需要注意負數右移後會在高位補1,需要將負數轉換為無符號數。      int NumberOf1(int n) {         int cnt = 0;  

zoj 3432 神奇運算。。

不得不說。。。這個真的很神奇。。又學會一招。 #include <stdio.h> #include <string.h> char str[8]; char str1[8]; int main(void) { int n; while (scan

神奇的C語言二十一:談談運算

寫C程式時位運算還是很常用的,您肯定也很瞭解位運算的基本規則。下面是位運算的部分應用。 亦或^: 可以實現位取反,例如: char a = 0x0F; a ^= 0xFF; // 現在,a變成了0xF0 特性:兩個相同的數的亦或結果是0。多個數亦或滿足交換律。 有道ACM

javascript運算符——運算

javascript 二進制 二進制表示  ECMAScript中的所有數值都以IEEE-754 64位格式存儲,但位操作符並不直接操作64位的值,而是以32位帶符號的整數進行運算的,並且返回值也是一個32位帶符號的整數  這種位數轉換使得在對特殊的NaN和Infinity值應用位操作時,這兩個值都會

運算的特殊運用

font 掌握 一個數 個數 pan nbsp 異或 二進制位 位數 位運算的特殊運用 位運算主要有。&。|。~,^等幾種。這幾種在編程方面能極大地優化程序,所以掌握他們勢在必行,所以就總計一下。 &: 主要能夠用來求某數的當中一個二進制位。經

運算相關

使用 優化 lin 位數 pan ble 移位 移動 等於 位運算符/移位運算符 運算符 &運算符 操作數1的位操作數2的位&的結果位 1 1 1 1 0 0 0 1 0 0 0 0 |運算符 操作數1的位操作數2的位

運算

沒有 各種運算符 plain 簡單 center static lai 第一個 結果 java中有三種移位運算符 << : 左移運算符,num << 1,相當於num乘以2 >> : 右移運算符,num

JAVA 按運算符的解釋

按位運算符 位運算 按位按位運算符Java定義了幾個按位運算符,可以將其應用於整數類型long,int,short,char和byte。按位運算符對位執行,並執行逐位運算。假設a = 60和b = 13; 現在以二進制格式,他們將如下 -a = 0011 1100b = 0000 1101--------

計蒜客15430 XOR Queries(Trie處理運算問題)

ron 二進制 進制 插入 我們 整數 容易 位置 xor 題意: 給出一個長度為n的數組C,回答m個形式為(L, R, A, B)的詢問, 含義為存在多少個不同的數組下標k屬於[L, R]滿足C[k] XOR A >= B(式中XOR為異或運算)。 T組測試數

第12章第1講運算符與表達式

表達式 pla image cnblogs mar mage 運算符 nbsp 技術 第12章第1講位運算符與表達式

進制轉換及運算

index 位運算 其余 dia 間接 cbe 不足 hot 十六進制 二進制與十進制之間的轉換 1 十進制轉二進制 方法為:十進制數除2取余法,即十進制數除2,余數為權位上的數,得到的商值繼續除2,依此步驟繼續向下運算直到商為0為止。 (具體

《劍指Offer》題目——運算

ati 劍指offer 多少 amp nes 位運算 規則 -s n) 題目描述:輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼表示。 題目分析:無論使用什麽方式,最關鍵的就是要考慮負數的處理方式 public class NumberOfOne {

【NOI2014】起床困難綜合癥 運算+貪心

困難 hide 答案 none har class gif pre 位運算 這道題先求出0和-1經過處理後的答案 具體看代碼吧 #include<cstdio> #include<cstring> #include<algorithm>

運算(&amp;、|、^)與邏輯運算(&amp;&amp;、 ||)差別

邏輯與 清空 作用 一個數 總結 與運算 位或 定位 按位與 剛無意在一篇文章中看到了位運算(&、|)和邏輯運算(&&、||)的介紹。想起了自己薄弱的基礎知識。於是百度了幾把總結了下。 首先從概念上區分下,位

Java 運算符 深入理解

clas 簡單的 二進制 ava white 最簡 bit ont 基於 在Java中存在著這樣一類操作符,是針對二進制進行操作的。它們各自是&、|、^、~、>>、<<、>>>幾個位操作符。不管是初始

nyist oj 138 找球號(二)(hash 表+運算

pan char s geo 運算 arch font msu 哈希 next 找球號(二) 時間限制:1000 ms | 內存限制:65535 KB 難度:5 描寫敘述 在某一國度裏流行著一種遊戲。遊戲規則為:現有一堆球中。每一個球上都有一個整數編號i

運算

真值表 如果 嵌入 右移 左移 進制 邏輯或 位移 xe8 1.位與運算 & 註意:& 和 && 是有區別的。前面這個是位與運算,後者是邏輯與運算,也就是算數運算 1 1 1 1 0 1 0 1 看看位與運算的真值表 結論:全真為真。