contributed by < 56han
>
1
在每次迴圈更新 、 來計算 進而推得
透過閱讀數學算式,進而得到幾個重點:
z
即是 ,而 m
即是 ,b
即是 ,x
則是代表前一輪的差值,也就是 。m
往右位移 2 位,是在表示上面公式中的 。b = z + m;
表示 。if
條件是判斷:當 比 大時,則更新其值。__ffs()
函數的目的是找到參數 (unsigned long) 中最低位的 1 所在的位置(從 0 開始計算)。__ffs()
函數改寫成下面的 fls()
函數的目的是找到參數 (unsigned long) 中最高位的 1 所在的位置(注意:從 1 開始計算)。__builtin_clz()
為 GCC 的內建函式,功能為找到 x 最高位數前面有幾個 0 , x 為 0 時,未定義。所以將 BITS_PER_LONG - fls(x)
即可得到 __builtin_clz(x)
。
因此可以將 i_sqrt()
改寫成以下
2
3
考慮 ilog2()
可計算以 2 為底的對數,且其輸入和輸出皆為整數。以下是其一可能的實作: (版本一)
透過一次又一次的右移(除以 2 ),直到 i=0
,去計算 i 以 2 為底時有多少位數。
改寫為以下程式碼: (版本二)
透過右移的方式去計算 i 以 2 為底時有多少位數。
我認為這裡的 while 可以改成 if,因為 i 透過右移會越來越小,因此不會進入相同迴圈中,只會往下走,進入別的判斷式中。
亦可運用 GNU extension __builtin_clz
來改寫,注意輸入若是 0 則無定義: (版本三)
透過 31-__builtin_clz()
找到最高位數,即是取 log 以後的值。
__builtin_clz()
為 GCC 的內建函式,功能為找到 x 最高位數前面有幾個 0 , x 為 0 時,未定義。
4
5
延伸測驗 3,已知輸入必為大於 0 的數值 (即 x > 0),以下程式碼可計算 ,也就是 ceil 和 log2 的組合並轉型為整數:
函數首先 x - 1
,是為了對於 2 的冪的數值也能夠正確計算對數。
shift = (x > 0xFF) << 3;
表示,若 x > 15 則為 1 << 2
,否則為 0 << 2
。
大部分和測驗 3 類似,當我們看到以下片段
首先判斷 x > 0xFF
,並使用 shift 記錄要移動多少。
x >>= shift;
即是測驗 3 的 i >>= 16;
,r |= shift;
即是 r = r + shift;
之意。
最後 return 可拆成3個部份看
r | shift
即是 r = r + shift;
之意shift = (x > 0x3) << 1;
會忽略 x = 0x2
的情況。1
此題是 LeetCode 477. Total Hamming Distance ,透過兩個for 迴圈可以逐一比較兩個數字之間的 Hamming Distance,最後要除以2 (右移1位),因為重複計算ab和ba。
GCC 提供對應的內建函式:
__builtin_popcount(x)
: 計算 x 的二進位表示中,總共有幾個 1
從 popcount 角度出發,時間複雜度就被限制在 。
以下嘗試從位元展現的樣貌,觀察 Total Hamming Distance 的規則。
n'th bit | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|
input 7 | 0 | 0 | 1 | 1 | 1 |
input 5 | 0 | 0 | 1 | 0 | 1 |
input 10 | 0 | 1 | 0 | 0 | 1 |
input 17 | 1 | 0 | 0 | 0 | 1 |
以 bit 找規則:
所有 Number 中從第 0 個 bit 開始計算全部該 bit 的 Hamming Distance ,在全部累加起來就是 Total Hamming Distance。
可以改寫成以下程式碼,其中:
(nums[i] >> bit) & 1
判斷 nums[i]
該 bit 是否為 1bitCount
統計出所有數字的該 bit 為 1 有幾個bitCount * (numsSize - bitCount)
統計該 bit 的 Hamming Distance2
我理解 n = popcount(n & 0x55555555) - popcount(n & 0xAAAAAAAA)
可以寫為 n = popcount(n ^ 0xAAAAAAAA) - 16
,其中也可以理解「我們要將它加上一個足夠大的 3 的倍數」,但為何是選擇加上39?使其為 n = popcount(n ^ 0xAAAAAAAA) + 23;
3