位運算可以計算兩個整數的平均值,代碼如下:
public int average(int a, int b){
return (a&b)+((a^b)>>1);
}
那究竟爲什麼呢?
爲什麼a和b的平均值就可以表示成這樣呢,即
(a+b)/2 = (a&b)+((a^b)>>1)
下面我們一步一步進行解釋
從上代碼可知求平均數分爲兩部分
- a&b
- (a^b)>>1
第一步算的是兩個數二進制對應位上當兩個值相同的情況下的平均值
第二步算的是兩個數二進制對應位上當兩個值不同的情況下的平均值
最後將兩個結果相加即爲這兩個整數的平均值
上面的有點抽象,這裏做一個更加形象的解釋:
兩個整數a和b分別爲45和75
a = 45;
b = 75;
---------------------
二進制表示爲
a = 00101101
b = 01001011
---------------------
將a和b分解成兩個數的相加,分解依據爲:
a和b二進制對應位上當兩個值都爲1時的位分解成左邊的數,
a和b二進制對應位上當兩個值都只有一個爲1時的位分解成右邊的數
(這裏的分解依據不是憑空想象來的,而是根據相加需要進位的1抽取在左邊,相加不需要進位的1抽取在右邊)
左 右
a = 00101101 = 00001001 + 00100100
b = 01001011 = 00001001 + 01000010
---------------------
與操作 | 異或操作
00001001 | 00100100
& | ^
00001001 | 01000010
---------------------
| 再右移1位
00001001 | 01100110
| >> 1
---------------------
左邊結果 | 右邊結果
00001001 | 00110011
---------------------
現在我們會發現一個現象
左邊的兩個數的平均數等於左邊兩個數的與操作的結果,即
(00001001 + 00001001)/2 = 00001001 & 00001001 = 00001001
右邊的兩個數的平均數等於右邊兩個數的異或操作後再右移1位的結果,即
(00100100 + 01000010)/2 = (00100100 ^ 01000010) >> 1 = 00110011
---------------------
再往細看,我們會看到另一個現象
左邊的兩個數的與操作的結果與a和b本身與操作的結果相同
a & b = 00101101 & 01001011 = 00001001 & 00001001 = 00001001
右邊的兩個數的異或操作的結果與a和b本身異或操作的結果相同
a ^ b = 00101101 ^ 01001011 = 00100100 ^ 01000010 = 00110011
這樣的結果不是偶然的,是跟與操作、異或操作本身的邏輯分不開的,也和我們分解的依據分不開。
---------------------
好的,現在讓我們把上面的步驟串起來
(a+b)/2
= (00101101+01001011)/2
= ((00001001 + 00100100)+(00001001 + 01000010))/2
= ((00001001 + 00001001)+(00100100 + 01000010))/2
= (00001001 + 00001001)/2 + (00100100 + 01000010)/2
= (00001001 & 00001001) + ((00100100 ^ 01000010) >> 1)
= (a & b) + ((a ^ b) >> 1)
所以,證明結論正確:
(a+b)/2 = (a & b) + ((a ^ b) >> 1)
最終總結:
兩個整數的平均值 = 進位部分的平均值 + 不進位部分的平均值