位运算n=(n-1)快速统计二进制1的个数
看到一篇博客,發現n&=(n-1)竟然能夠快速統計二進制1的個數,經過博主同意特此拿來分享一下。
首先,分析一下該式子,先可以簡化為
n=n&(n-1);我們先做一個實例,
| n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 十進制 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 二進制 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 |
我們先試著求7中二進制1的個數k
? ? ?0111
& ?0110
-------------
? ? 0110(6)
然后對其結果繼續進行上述操作
? ? 0110
& 0101
-------------
? ? 0100(4) ? ?
繼續上述操作
? ? 0100
& ?0011
-------------
? ? 0000
結束操作。
至此,我們可以發現,每次&操作后,0111(7)的二進制數中最右邊的1都被消除1個,
第一次&操作后,結果為0110
第二次&操作后,結果為0100
第三次&操作后,結果為0000
而k恰好與&操作的次數是相同的。那么我們可以猜測這兩者必然存在直接關聯關系。
不過,當二進制數中1的位置不連續,中間有若干個0這個公式也能“跳過”0,直接統計1的個數嗎?
下面我們在舉個實例37(0010 0101)這個比較有代表性
| n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 十進制 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 二進制 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 |
? ? ?100101
& ? 100100(36)
----------------------
? ? ?100100(36) ? ?
繼續重復上述操作
? ? ?100100
& ? 100011
--------------------
? ? ?100000(32)?
繼續重復上述操作
? ? ?100000
& ? 011111
--------------------
? ? ?000000
結束。
到這里,我們會得出一個結論,
1的位置無非就三種情況,頭和中間、尾部
a.在尾部的情況
? ? 當末尾是1的時候(奇數),與前一個數(偶數)進行&操作后,結果必為0,末尾的1消除
b.在頭部的情況
? ? 當1只在頭部的時候,其余位上都為0.類似8(1000),4(0100),與前一個數(全1,7(0111),3(0011))進行&操作時,結果必為0.
c.在中間的情況
? ? ?無非也是上述兩種情況的遞歸。保證末尾位為0,因為之前已經處理過尾部的情況了。
? ? ?比如,尾部是*0010結尾,*0100結尾,*1010結尾,*11010結尾。他們對應的前一個數分別是:*0001,*0011,*1001,*11001。(*代表左邊還有若干個0和1)
? ? ?0010
& ? 0001
-------------
? ? 0000
結束。
? ? ?0100
& ? 0011
-------------
? ? ?0000
結束。
? ? ?1010
& ? 1001
--------------
? ? ?1000(消除最右邊的1,下一步進行第二種情況的處理)
結束。
? ? ? 11010
& ? ?11001
---------------
? ? ? 11000(消除最右邊的1,下一步進行第一種情況的處理)
結束。
--------------------------------分割線-------------------------------------
到這里,我們就可以看出,每次在最右的1設置一個flag的話,
當它(i)與它前一位(i-1)進行&操作時,對flag左邊的1是沒有影響的,每次得到的結果,就會將flag位置及右邊所有的數置為0.
例如:11010&11001 ==11000(24)
那么,結束條件是什么呢?
? ? ? 那就是當&操作后的結果為0,循環結束。
好啦,分析就到這里。下面附上源代碼供看官們欣賞哈~
總結
以上是生活随笔為你收集整理的位运算n=(n-1)快速统计二进制1的个数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringBoot项目中静态资源加载失
- 下一篇: JAVA字符运算