contributed by < ShawnLu31
>
簡單地將 a
和 b
相加後除以 2 。但此作法可能導致 overflow,例如: uint32 的最大值相加。
避免直接相加導致 overflow ,其中一個數與兩數的差的平均相加,兩者的差不會超過該兩個數的數值,所以不會導致 overflow 。
a
和 b
各自除以 2 並相加。
因右移運算子 >>
移出的位元會被忽略,若 a
b
皆為奇數,右移除 2 的結果會差 1 ,必須加回 1 ,故使用 a & b & 1
判斷兩者是否皆為奇數。
參考加法器原理 a + b
等價於 ((a & b) << 1) + (a ^ b)
, (a & b) << 1
為進位, (a ^ b)
為和。
所以 (a + b) / 2
等價於 (((a & b) << 1) + (a ^ b)) >> 1
,再化簡
-> (((a & b) << 1) >> 1) + ((a ^ b) >> 1)
-> (a & b) + ((a ^ b) >> 1)
若 a
小於 b
,則 -(a < b) = -1
,否則等於 0
。
回傳值會有兩種情況: a ^ ((a ^ b) & -1)
或 a ^ ((a ^ b) & 0)
在 a
小於 b
時,-(a < b) = -1
, -1
在二補數表示時為 0xffffffff
,所以 AND -1
不會有改變,可以去掉化簡為 a ^ a ^ b
。兩個同樣的數做 XOR 會為 0
,將 a ^ a
先運算為 0
,再運算 0 ^ b
等於 b
。
在 a
大於等於 b
時, -(a < b) = 0
, AND 0
結果都是 0
,括號內 ((a ^ b) & 0)
等於 0
,最後a ^ 0
等於 a
。
變數用途:
(a < b)
做比較。a
回傳 a
的值。a
抵銷第一個 a
在需要回傳 b
時。b
回傳 b
的值。可分成三個區域:
if
區域對應 GCD 演算法規則第 1、2 條。因至少一個數為 0 ,使用 OR 運算子回傳另一個數的值。for
區域對應 GCD 演算法規則第 3 條。兩數 LSB 皆為 0 (偶數), 2 為公因數, shift 紀錄兩者最大公因數的 2 的冪。while
和 do while
區域對應 GCD 演算法規則第 4、5 條。while
與 do while
內部的 while
迴圈,使 u
v
移除 2 的因數,因為經過前述的 for
迴圈,已不再有 2 的因數。do while
迴圈為輾轉相除法:被除數(大數)除以除數(小數),再使餘數與除數成為下一輪的被除數與除數,重述此動作直到餘數為 0 ,除數便是最大公因數。v
為迴圈的條件,所以使 v
一直放餘數, u
一直放除數,當 v
為 0 時, u
便為最大公因數。v
大於 u
時,可直接 v -= u
;v
小於 u
時,此時 v
為除數 u
被除數,須將除數換成 u
,並把差值給 v
,才能維持 v
餘數 u
除數。最後回傳 u << shift
,公因數(不含因數 2)乘回因數 2 的冪,得到最大公因數。
外部迴圈走訪 bitmap 每個元素。
bitset 取出 bitmap 的一個元素,內部迴圈低位往高位尋找為 1
的位元,若該位原為 1
,此時 i
為第 i
個位元。
1
pos 會加一。 bitmap 位元若都是 1
pos 有最大值 bitmapsize * 64
。p + i = k * 64 + i
的意義?使用 __builtin_ctzll
改寫原本的迴圈,由低位往高位尋找第一個碰到的 1
, r
等價於原本程式的 i
。
且使用 XOR t
清除碰到的 1
(最低位元的 1
) ,使下一次的 __builtin_ctzll
不會成重複找到同個位元的 1
。 -bitset
為 bitmap
的二補數,兩者做 AND 後的 t
唯一的 1
會是 bitmap
最低位元的 1
。
此方法會直接找到需要的位元位置,不需像原本程式用迴圈一一檢查每個位元。