方法1:分別判斷各個位









方法2:循環(huán)中直接計算1的數(shù)量
如何只數(shù)'1'的個數(shù)?如果一個數(shù)字至少包含一個'1'位,那么這個數(shù)字減1將從最低位開始依次向高位借位,直到遇到第一個不為'0'的位。依次借位使得經(jīng)過的位由原來的'0'變?yōu)?1',而第一個遇到的那個‘1’位則被借位變?yōu)?0'。
36 d = 100100 b
36-1 d = 100011 b
如果最低位本來就是'1',那么沒有發(fā)生借位。
現(xiàn)在把這2個數(shù)字做按位與:n & n-1的結(jié)果是什么?
2個數(shù)字在原先最低為'1'的位以下(包括這個位)的部分都不同,所以結(jié)果是保留了其他的'1'位。
36 & 36-1 d = 100000 b
這個結(jié)果剛好去掉了最低的一個'1'位









由于直接跳過'0'位,這個方法比上面的要略微快一些。
方法3:并行計算的













POW是計算2的冪
MASK很奇怪,一個全1的無符號數(shù)字除以2的冪的冪加1?
好在打印出來還能看得懂:





這些mask分別把32位數(shù)字劃分為幾個部分。每個部分的前一半和后一半分別是全'0'和全'1'。
MASK(0)分為16個部分,MASK(1)分為8個部分...
ROUND中對n的處理:(n & MASK) + (n >> POW & MASK)
POW的值剛好是MASK中連續(xù)'0'(或者連續(xù)'1')的長度。也就是說ROUND把由MASK分開的n的各個部分中的高POW位和低POW位相加。
為了便于說明,取一個簡單的部分:MASK(1)的0011
假設(shè)n的值為1001,那么ROUND后的結(jié)果就是10 + 01 = 11 b,把這個結(jié)果賦值給n,這時n的含義由原來的二進制位串變?yōu)?1'位的數(shù)量。特別的,當ROUND(n, 0)時,把n當作一個32個部分各自'1'位的數(shù)量。('0'表示沒有'1',而'1'則表示有1個'1')
計算完n = ROUND(n, 0)后,n是一個16個部分各自'1'位數(shù)量的“數(shù)組”,這個“數(shù)組”每個元素只有2個二進制位。最大值為2,足夠由2個二進制位來表示。
接下來,計算完n=ROUND(n,1)后,n是一個8個部分各自'1'位數(shù)量的“數(shù)組”,這個“數(shù)組”的每個元素只有4個二進制位。最大值為4,足夠由4個二進制位來表示。(實際只需要3個二進制位)
...
最后一步,計算n=ROUND(n,4)后,n是一個1個部分各自'1'位數(shù)量的“數(shù)組”,這個“數(shù)組”的每個元素有32個二進制位。最大值為32,足夠由32個二進制位來表示。(實際只需要6個二進制位)
這個代表32位內(nèi)'1'位數(shù)量的32位二進制數(shù)也就是我們要求的結(jié)果。
是不是說得有點羅嗦了- -
這個方法的好處是只需要5步(log n (n=32)),并且沒有循環(huán)(或分支),流水線不會被打破。
PS:
這種方法是計算二進制形式1的個數(shù),如果用來判斷一個數(shù)是否是2的整數(shù)次冪,可以用這種方法,但是對此還有更好的方法,就是 A xor (A-1) == 0。
這個題目是從《程序員面試寶典》看到的。微軟面試的東西很廣泛,不過都還算基礎(chǔ)。但是我沒有過筆試……