今天在Matrix67大牛的博客上看到了很多位運算的優化技巧,頓感位運算的神奇,現在來我總結一下位運算的神奇用法。
下面是常見的位運算:
a & b a和b都是1的位取1,其他都取0。和&&有區別,&&是邏輯運算,返回true或者false
a | b a和b都是0的位取0,其餘情況都取1。和||有區別,||是邏輯運算,返回true或者false
a ^ b a和b相同的位取0,不同的位取1.
~a a按位取反 一個無符號整形取反等於表示上限減去這個數, 比如12 假設是16位無符號整型,~12就是65535-12=65523
a << b a左移b位
a >> b a右移b位
下面是位運算的幾種常見的用法:
把a的末k位變成1 00110001 k=3 => 0110111 a | (1<<k - 1)
把a的末k位取反 00110001 k=3 => 0110110 a ^ (1<<k -1)
把a右邊連續的1變成0 00110111 => 00110000 (a +1)&a
把右邊連續的0變成1 00101111 => 00100000 (a - 1)^a
取右邊連續的1 00101111 => 1111 ((a+1) | a) >> 1
去掉右起第一個1的左邊 00101000 => x & (-x) 或者 x & (x ^ (x-1)) 這個在樹狀數組中會用到
上面這些只是位運算的簡單應用(已經非常強大了),還有一些更加強大的用處,第一個例子是,求一個32bit整數,求它的1的位數,代碼如下:
x := (x and $55555555) + ((x shr 1) and $55555555);
x := (x and $33333333) + ((x shr 2) and $33333333);
x := (x and $0F0F0F0F) + ((x shr 4) and $0F0F0F0F);
x := (x and $00FF00FF) + ((x shr 8) and $00FF00FF);
x := (x and $0000FFFF) + ((x shr 16) and $0000FFFF);
想一下怎麼回事,比如x爲21, 二進制爲10101,把這個數代入代碼中看下,
第一步 : 10101 & 0101010101010101 + (1010)&0101010101 = 10101
第二步 :10101&001100110011 + (101)&001100110011 = 10001+1 = 10010
第三步: 10010&00001111 + 1&00001111 = 10+1= 11
第四步: 11&11111111 + 0 = 11
第五步: 11& 11111111 + 0 = 11
上面五步得到了最終結果,答案爲3,到這裏,看出什麼了嗎?這段代碼其實是用了分治的思想,我看了好久,這幾個數字比較令人眼花。 下面讓我來解釋一下。比如這樣一個數字,x32x31x30...x4x3x2x1 , 第一步之後,變成了 x32x31兩位表示x32和x31的和,x30x29表示x30和x29的和...以此類推。第二步之後,x32x31x30x29四位數字表示x32~x29的和,x28x27x26x25表示x28~x25四位數字的和,以此類推。第五步結束後x32~x1表示x32~x1這32位數字的和。如果你還是不清楚,請看下面這個圖:
如何只用位運算來求一個整數的絕對值呢? 一個正整數x和它的相反數-x的關係是,-x是x的補碼,即-x=(~x)+1.x的絕對值代碼爲 x ^ (~(x >> 31) + 1) + x >> 31,當x爲正數時,x>>31爲0,取反變成-1,再加1變成0,0異或任何數不變;當x爲負數時候,x>>31=1,1取反後加一變成0xFFFFFFFF,異或x之後,x每位取反,取反後再加一剛好是相反數。
先寫到這裏,用到了再加