看了一道leetcode上面的題 461 ,Hamming Distancejava
計算兩個整數有多少不一樣的位。其實很簡單,取兩個整數異或的值,而後計算出裏面二進制有多少個1就好了。代碼以下:code
public int hammingDistance(int x, int y) { return Integer.bitCount(x ^ y); }
爲何要用bitCount來統計含1的位了?爲何不直接使用循環統計每一個bit位了?leetcode
跳轉到bitCount的源碼中,以下:rem
public static int bitCount(int i) { // HD, Figure 5-2 i = i - ((i >>> 1) & 0x55555555); i = (i & 0x33333333) + ((i >>> 2) & 0x33333333); i = (i + (i >>> 4)) & 0x0f0f0f0f; i = i + (i >>> 8); i = i + (i >>> 16); return i & 0x3f; }
原來是 先 兩個兩個一組,求二進制1的個數,而且用兩位二進制存儲在原處,而後四個四個一組,求二進制位1的個數,再把它存儲以4位二進制到原處。以此類推直到計算完成。get
src | store | remark |
---|---|---|
00 | 00 | 這兩位沒有,那就用0存儲 |
01 | 01 | 這兩位只有一個1,就用1存儲 |
10 | 01 | 這兩位也只有一個1,也用1存儲 |
11 | 10 | 這兩位有兩個1,用10存儲 |
那麼就一對一對的數,已知 src列 求出 store列?
列式計算:源碼
那麼 x 又如何經過i獲得呢?it
咱們手無寸鐵,對CPU來講也只有加法和移位的手段。假如發明者列出這種算式,敏感的他一會兒
很容易看出來:
x=i>>>1
就這麼簡單
那麼獲得:
λλ = i - (i>>>1)table
那麼i不止兩位怎麼處理?若是這個是最後的兩位,那麼移位以後後面一位二進制能夠抹掉
而前面的移位會影響後面的最高位,那麼把移出去的那一位消除:
i>>>1 & 01;
即爲:01010101 01010101 01010101 01010101
λλ = i - (i>>>1 & 0x55555555)class
問題解決。
那麼 計算了兩位的如何計算4位的二進制位呢?
枚舉第一步計算完成的全部的狀況:循環
src | target | remark | ref |
---|---|---|---|
0000 | 0000 | = 0000 & 0011 | |
0001 | 0001 | = 0001 & 0011 | 01 = 01 & 11 |
0010 | 0010 | = 0010 & 0011 | 10 = 10 & 11 |
0100 | 0001 | = 01 + 00 | |
0101 | 0010 | = 01 + 01 | |
0110 | 0011 | = 01 + 10 | |
1000 | 0010 | = 10 + 00 | |
1001 | 0011 | = 10 + 01 | |
1010 | 0100 | = 10 + 10 |
後面兩組能夠參照第一組的結果,那麼能夠推算
四位中低兩位 bb = aabb & 0011,主要要計算與高兩位的和:
已知能夠用1100& aabb =aa00獲得左邊的值,可是多了兩個00,那麼要計算aa + bb:
能夠 aabb>>>2 = 00aa(bb)只看這兩位,移位多出去的被00消除,不影響後面的計算。
即:
λλ =( i & 0x0011) + (i>>>2 & 0x0011)
也就是:
λλ =( i & 0x33333333) + (i>>>2 & 0x33333333)
同理求8位裏面的兩邊4位之和:
λλ =( i + i>>>4) & 0x0F0F0F0F
求16位的兩邊之和:
λλ = i + (i >>> 8);
因爲二等分是8位,而8位一共有4份。
A B C D
(C>>>8) + D D處8位的結果最大爲 0001 0000不會進位到C。
(B>>>8) + C C處8位的結果最大爲 0001 0000不會進位到B。
(A>>>8) + B B處8位的結果最大爲 0001 0000不會進位到B。
A + 0 A處最大結果爲 0000 1000
獲得
A A+B B+C C+D
最後是求32位所有的內容也就是求(A+B)+(C+D)
A A+B B+C C+D
+
0 0 A A+B
也就是
λλ= i + (i >>> 16)
A A+B A+B+C A+B+C+D A+B+C+D最大也就32個: 0000 0000 0000 0000 0000 0000 0010 0000 0000 0000 0000 0000 0000 0000 0011 1111 = 0x3F 之因此要return i&0x3F,就是把前面抹乾淨。