# 2024 Homework4 (quiz3+4) contributed by < `dockyu` > ## 第 3 週測驗題 [第 3 週測驗題 連結](https://hackmd.io/@sysprog/linux2024-quiz3) ### 測驗一 --- ## 第 4 週測驗題 [第 4 週測驗題 連結](https://hackmd.io/@sysprog/linux2024-quiz4) ### 測驗一 ```c unsigned popcount_naive(unsigned v) { unsigned n = 0; while (v) v &= (v - 1), n = -(~n); return n; } ``` 有幾個位元是1就必須加幾次 `n = -(~n)` 等於 `n++` 是一種很酷的寫法,但是為什麼編譯器在做最佳化的時候應該也可以將 `n++` 轉成 `n = -(~n)` ,這樣程式碼可讀性比較高,為什麼不這樣做呢? --- 如果用下面的方法來做要減31次 \begin{align} popcount(x) = x - \left \lfloor{{\dfrac{x}{2}}}\right \rfloor - \left \lfloor{{\dfrac{x}{4}}}\right \rfloor - ... - \left \lfloor{{\dfrac{x}{2^{{31}}}}}\right \rfloor \end{align} 但是如果分區塊(四位元)來進行每個區塊只要減3次,而且可以**同時進行**,在透過位移和乘法將每個區塊加總 我覺得這個分區塊的想法是最精妙的部份,有點類似平行處理,跟下面的測驗4的棋盤表示法在概念上有點像 ### 測驗二 已經知道 $x \mod 3$ 其實就是奇數與偶數 bit 的1的數量的差 並且可以推導出下面的公式 \begin{align} popcount(x \And \overline{m}) - popcount(x \And m) = popcount(x \bigoplus m) - popcount(m) \end{align} ```c= int mod3(unsigned n) { n = popcount(n ^ 0xAAAAAAAA) + 23; n = popcount(n ^ 0x2A) - 3; return n + ((n >> 31) & 3); } ``` 第3行其實就等於`奇偶的差+39`,因為只有32 bit 所以奇偶的差的範圍是 `-16 ~ 16` ,第一行的 n 的範圍是 `23 ~ 55`,所以要再做一次但是因為最大才55所以可以用 0x2A 當作 m ,做完第二行後 n 的範圍是 `-3 ~ 3`,如果是負的後面 `((n >> 31) & 3)` 會是3,把 n 補回正的 ```bash (gdb) p /t i $2 = 11111111111111111111111111111101 (gdb) set variable i = i >> 1 (gdb) p /t i $3 = 1111111111111111111111111111110 ``` 提醒: 負數位移會補1 ```c= int mod3(unsigned n) { static char table[33] = {2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1 }; n = popcount(n ^ 0xAAAAAAAA); return table[n]; } ``` 第3行明顯跟上面推導的公式不一樣少了 `-16` ,所以 n 的範圍是 `0 ~ 32` ,而 $n=0$ 實際上 $\mod 3$ 的結果是-16,對照下表,因為範圍是 `0 ~ 32` 而且我們已經知道每一個數的答案,那麼直接用一個 lookup table 比較快 |$n$|$\mod 3$|正確答案| |:-:|:-:|:-:| |0|-16|2| |1|-15|0| |2|-14|1| |3|-13|2| |4|-12|0| |$\vdots$|$\vdots$|$\vdots$| |31|15|0| |32|16|1| 我覺得使用 lookup table 是一種很優美的方式,我之前根本沒有想過,他的流程是先用數學方法將問題的範圍限縮在一個可接受的範圍內,再用查表法來加快速度 其實兩種 mod3 函式都有用 popcount 來縮小範圍但是前者必須做兩次,因為範圍還沒精確到可以直接得出答案 參考 [vax-r 的筆記](https://hackmd.io/@vax-r/linux2024-homework4) 定義8條線的三個點是一個4位元的後3位元,這樣下每一格都不會互相影響