位運算的威力

1、有關位運算的基礎知識總結算法

位運算包括:&(與)、|(或)、^(異或)、~(取反)、>>(右移)、<<(左移)網絡

環境預設:32位機下面,int佔2個字節,有符號學習

int a = 11;google

int b = 1000;編碼

(a)2 = (00000000 00001011 )2                                            //a的二進制表示blog

(b)2 = (00000011 11101000 )2                                            //b的二進制表示ip

a&b =(00000000 00001000 )2 =(8)10                             //一一爲一,其它爲0get

a|b =  (00000011 11101011 )2 =(1003)10                    //有一爲一,零零爲0it

a^b = (00000011 11100011 )2 =(995)10                       //相同爲0,不相同爲1編譯

~b =   (11111100 00010111 )2 =(-31767)10               //按位取反

b>>3 =  (00000000 01111101 )2 =(125)10                   // 去掉低3位,高位補0

或      =  (11100000 01111101 )2 =(-24701)10           //去掉低3位,高位補1    補0仍是1具體狀況視編譯環境決定

a<<3 =  (00000000 01011000 )2 =(88)10                    //去掉高3位,低位補0

 

    看了上面的例子,相信你已經明白具體規則了,不明白本身去google。下面講具體做用。

    位運算應用口訣

清零取數要用與,某位置一可用或
若要取反和交換,輕輕鬆鬆用異或

 

例1.子網掩碼

    子網掩碼是個啥東東我也就不講了,計算機科學技術自己就是個很是龐大系統,一我的不可能面面俱到,可是一些基本的嘗試仍是要懂的,不懂的能夠本身去google,也能夠等個人相關網絡方面的文章。這裏只講與本問有關的應用部分。

    假如我是一個網管,公司內部使用C類地址,如今我要把公司網絡劃分紅5個子網,網絡號爲192.168.1.0的前三段,那麼子掩碼怎麼填呢?

    我如今先告訴你子網的子網掩碼分別怎麼填:192.168.1.224。(固然這裏還有其餘答案,我取的是在子網擴充不超過8個的狀況下的每一個子網所容納主機最多的最佳方案)。

    這個怎麼來的呢?ip自己是個二進制的東東,爲了方便人們設置,咱們採用了點分十進制的轉換,把32位的ip地址轉換成了4個字節的十進制萊表示。好比 192.168.1.213 這個ip地址的二進制表示爲:11000000 10101000 00000001 11010101 。對於C類地址默認的前三個字節表示網絡號,那麼這個網絡號就是:11000000 10101000 00000001   ,最後一個字節11010101表示主機號,能夠知道這個網絡能夠容納的最多主機數爲2^8-2,爲何減2本身去查。如今要劃分子網,那麼咱們就要從表示主機的那個字節也就是8個位裏面拿出幾個位來表示子網號, 幾位比較合適呢?這就要看你須要劃分多少個子網咯。好比咱們如今要劃分5個子網,(5)10 = (101)2 ,那麼至少就須要3位了,並且最多能夠劃分2^3 = 8個子網。如今你把224換成二進制看看吧(224)10 = (11100000)2  ,明白了吧,咱們能夠推斷出子網掩碼幹了什麼勾當?不錯子網掩碼與ip地址作了按位與運算,他的做用就是屏蔽了主機號獲取網絡號與子網號。若是你明白了這點,你就知道本身在192.168.1.64子網的ip該怎麼填了,不會錯誤滴填成192.168.1.10了。 

    居然扯到一邊去了,講了半天才講了一個與運算的應用。

 

例2. 防止int型變量溢出

    int x = 32760;int y = 32762; 要求求x、y的平均值,要求空間複雜度位O(0)。

    你能用常規方法去解決嗎?能夠。我不會講,這裏只講位運算的 方法。

int ave(int x, int y)   //返回X、Y的平均值

{   

     return (x & y) + ( (x^y)>>1 );

}

知識點:>>n 至關於除於2^n ,<<n 至關於乘於2^n .

                 x,y對應位均爲1,相加後再除以2仍是原來的數,如兩個00001000相加後除以2仍得00001000,那麼咱們把x與y分別分紅兩個部分來看,二者相同的位分別拿出來 則 :

x = (111111111111000)2 =  (111111111111000)2 +  (000000000000000)2

y =  (111111111111010)2 =  (111111111111000)2 +  (000000000000010)2

相同部分咱們叫作x1,y1,不一樣部分咱們叫作x2,y2.那麼如今(x+y)/2 =(x1+y1)/2 +(x2 + y2)/2 ,由於x1 == y1 ,因此(x1+y1)/2 ==x1 ==y1,

相同部分咱們用與運算求出來 x1 = x&y ,不一樣部分的和咱們用^求出來,而後除於2是否是咱們想要的結果了呢?言至於此,無需再言!            

這個例子有點難於理解.可是通過個人分解應該還算好理解了,弄懂這個例子相信你的位運算已經登入大門了。

 

例3.《有關集合算法的實現一些學習筆記》中的"算法2"

 

算法2. 將整數index的元素插入集合(閱讀此例請先閱讀該文)

int insert(BitSet* s,int index){

    if(index >=0 && index>>3 < s->size)

        {s->array[index>>3] |= (1<< (index & 7) );return 1}

    return 0;

}

代碼詳解:index>=0不解釋,(index>>3 )< s->size 這個是保證  index  < n 的。由於index<=n-1,因此 index/8 <=(n-1)/8,又由於 index < n+7 ==(n-1) +8,因此index/8 < (n-1)/8 +8/8 == s->size。由於array的下標是0到size-1,index>>3也就是index/8取整也就是index下標所在的字節,index&7  等價於  index & 0000000 00000111 ,就是取index二進制編碼的低三位也就是至關於index>>3所剩下的餘數,餘數對應的十進制就是index所在字節的序號( 這個序號也是從0開始,而且從右至左),因此把1左移相應的位數就是index在n中對應bit了,再把s->array[index>>3]也就是index所在的字節與(1<<(index&7))也就是除了index所在的位之外均爲0或運算,這樣不管index所對應位原先是什麼狀態,以後都被置1。這個可能比上一個例子難度大多了,這個須要掌握位向量的相關知識,若是你不能看懂就跳過吧。

 

   以上是我本身的一些學習心得。下面將貼上一些網絡上的例子。

 應用舉例
(1) 判斷int型變量a是奇數仍是偶數           
a&1   = 0 偶數
       a&1 =   1 奇數
(2) 取int型變量a的第k位 (k=0,1,2……sizeof(int)),即a>>k&1   (先右移再與1)

(3) 將int型變量a的第k位清0,即a=a&~(1<<k)    (10000 取反後爲00001 )

(4) 將int型變量a的第k位置1,即a=a|(1<<k)     

(5) int型變量循環左移k次,即a=a<<k|a>>16-k   (設sizeof(int)=16)
(6) int型變量a循環右移k次,即a=a>>k|a<<16-k   (設sizeof(int)=16)

(7)對於一個數 x >= 0,判斷是否是2的冪。

boolean power2(int x)
{
    return ( (x&(x-1))==0) && (x!=0);
}

(8)不用temp交換兩個整數

void swap(int x , int y)
{
 x ^= y;
 y ^= x;
 x ^= y;
}

(9)計算絕對值

int abs( int x )
{
 int y ;
 y = x >> 31 ;
 return (x^y)-y ;        //or: (x+y)^y
}

(10)取模運算轉化成位運算 (在不產生溢出的狀況下)
         a % (2^n) 等價於 a & (2^n - 1)
(11)乘法運算轉化成位運算 (在不產生溢出的狀況下)
         a * (2^n) 等價於 a<< n
(12)除法運算轉化成位運算 (在不產生溢出的狀況下)
         a / (2^n) 等價於 a>> n
        例: 12/8 == 12>>3
(13) a % 2 等價於 a & 1       
(14) if (x == a)

                  x= b;
   else      x= a;
        等價於 x= a ^ b ^ x;
(15) x 的 相反數 表示爲 (~x+1)
(16)輸入2的n次方:1 << 19
(17)乘除2的倍數:千萬不要用乘除法,很是拖效率。只要知道左移1位就是乘以2,右移1位就是除以2就好了。好比要算25 * 4,用25 << 2就好啦

 

實例      功能              |          示例            |    位運算  ----------------------+---------------------------+--------------------  去掉最後一位          | (101101->10110)          | x >> 1  在最後加一個0        | (101101->1011010)        | x < < 1  在最後加一個1        | (101101->1011011)        | x < < 1+1  把最後一位變成1      | (101100->101101)          | x | 1  把最後一位變成0      | (101101->101100)          | x | 1-1  最後一位取反          | (101101->101100)          | x ^ 1  把右數第k位變成1      | (101001->101101,k=3)      | x | (1 < < (k-1))  把右數第k位變成0      | (101101->101001,k=3)      | x & ~ (1 < < (k-1))  右數第k位取反        | (101001->101101,k=3)      | x ^ (1 < < (k-1))  取末三位              | (1101101->101)            | x & 7  取末k位              | (1101101->1101,k=5)      | x & ((1 < < k)-1)  取右數第k位          | (1101101->1,k=4)          | x >> (k-1) & 1  把末k位變成1          | (101001->101111,k=4)      | x | (1 < < k-1)  末k位取反            | (101001->100110,k=4)      | x ^ (1 < < k-1)  把右邊連續的1變成0    | (100101111->100100000)    | x & (x+1)  把右起第一個0變成1    | (100101111->100111111)    | x | (x+1)  把右邊連續的0變成1    | (11011000->11011111)      | x | (x-1)  取右邊連續的1        | (100101111->1111)        | (x ^ (x+1)) >> 1  去掉右起第一個1的左邊 | (100101000->1000)        | x & (x ^ (x-1))  判斷奇數       (x&1)==1  判斷偶數        (x&1)==0        

相關文章
相關標籤/搜索