# 2020q3 Homework4 (quiz4) contributed by < `StevenChen8759` > ###### tags: `linux2020` > 相關連結: > [2020秋季進階電腦系統理論與實作 quiz4](https://hackmd.io/@sysprog/2020-quiz4) > [2020秋季進階電腦系統理論與實作 quiz4 作業說明](https://hackmd.io/@sysprog/r1z0WPWLD) > [2020秋季進階電腦系統理論與實作 quiz4 作業繳交區](https://hackmd.io/@sysprog/2020-homework4) > [color=green] ## :large_orange_diamond: 測驗一 ```cpp int hammingDistance(int x, int y) { return __builtin_popcount(x OP y); } ``` ### :building_construction: 程式碼分析 * 參考附圖中的雙立方體,可發現二進制表示中任兩個數字之間的 Hamming Distance 與表示法中對應位元不同的數量有關。 * `0110` 與 `0100` 之 Hamming Distance 為 1 * `0011` 與 `1100` 之 Hamming Distance 為 4 * 因此,我們需透過 XOR 指出位元表示不同的位置為 1,再以 `__builtin_popcount()` 計算位元為 1 的數量,即為輸入兩數的 Hamming Distance。 * 故 `OP` 應填入 `^` ## :large_orange_diamond: 測驗二 ```cpp #include <stdlib.h> typedef struct { AAA; int max_level; } TreeAncestor; TreeAncestor *treeAncestorCreate(int n, int *parent, int parentSize) { TreeAncestor *obj = malloc(sizeof(TreeAncestor)); obj->parent = malloc(n * sizeof(int *)); int max_level = 32 - __builtin_clz(n) + 1; for (int i = 0; i < n; i++) { obj->parent[i] = malloc(max_level * sizeof(int)); for (int j = 0; j < max_level; j++) obj->parent[i][j] = -1; } for (int i = 0; i < parentSize; i++) obj->parent[i][0] = parent[i]; for (int j = 1;; j++) { int quit = 1; for (int i = 0; i < parentSize; i++) { obj->parent[i][j] = obj->parent[i][j + BBB] == -1 ? -1 : obj->parent[obj->parent[i][j - 1]][j - 1]; if (obj->parent[i][j] != -1) quit = 0; } if (quit) break; } obj->max_level = max_level - 1; return obj; } int treeAncestorGetKthAncestor(TreeAncestor *obj, int node, int k) { int max_level = obj->max_level; for (int i = 0; i < max_level && node != -1; ++i) if (k & (CCC)) node = obj->parent[node][i]; return node; } void treeAncestorFree(TreeAncestor *obj) { for (int i = 0; i < obj->n; i++) free(obj->parent[i]); free(obj->parent); free(obj); } ``` ### :building_construction: 程式碼分析 * 觀察 `treeAncestorCreate()` 函式內部對 obj 的操作,在 allocate `n` 個 `int *` 型別的空間給 `obj->parent` 之後,再分配 `max_level` 個 `int` 的空間,並分別將其位址儲存在先前宣告的 `int *` 型別的記憶體位址之中。由這樣子的操作,可以知道 parent 是一個二維陣列,故其宣告應為指標的指標。 * 由上可知,`AAA` 應填入 `int **parent`。 * 觀察 `treeAncestorCreate()`,在第二個 for loop 時,程式將所有 node 的 parent 指派到配置的二維陣列之中;第三個 for loop 則是找出指定的節點是第幾代的 Ancestor。 * 觀察 25 行針對陣列元素的操作,可知 `BBB` 為了防止陣列輸入項為負值而造成錯誤,因此應填入 `-1` ## :large_orange_diamond: 測驗三 ```cpp= #define MSG_LEN 8 static inline bool is_divisible(uint32_t n, uint64_t M) { return n * M <= M - 1; } static uint64_t M3 = UINT64_C(0xFFFFFFFFFFFFFFFF) / 3 + 1; static uint64_t M5 = UINT64_C(0xFFFFFFFFFFFFFFFF) / 5 + 1; int main(int argc, char *argv[]) { for (size_t i = 1; i <= 100; i++) { uint8_t div3 = is_divisible(i, M3); uint8_t div5 = is_divisible(i, M5); unsigned int length = (2 << div3) << div5; char fmt[MSG_LEN + 1]; strncpy(fmt, &"FizzBuzz%u"[(MSG_LEN >> KK1) >> (KK2 << KK3)], length); fmt[length] = '\0'; printf(fmt, i); printf("\n"); } return 0; } ``` ### :building_construction: 程式碼分析 * 根據 [quiz2](https://hackmd.io/@sysprog/2020-quiz2#%E6%B8%AC%E9%A9%97-4) 中測驗四所提,我們透過這個函式,得以較低的成本 (不需乘、除運算) 計算出一個數字 $n$ 是否可指定數字整除。 * 原理請參考 [quiz2 Homework 解說](https://hackmd.io/@StevenHHChen/linux2020q3_hw02_quiz2#-%E6%B8%AC%E9%A9%97%E5%9B%9B%E7%A8%8B%E5%BC%8F%E7%A2%BC%E8%88%87%E5%88%86%E6%9E%90) * 根據上述提示,若 i 可被 3 整除,則 `div3` 的值為 1;若 i 可被 5 整除,則 `div5` 的值為 1 * 根據題意,在下列不同的狀況時,應輸出的字串對應如下: * 1. 若 i 是 3 的倍數,但不是 5 的倍數,則印出 `Fizz` * 2. 若 i 是 5 的倍數,但不是 3 的倍數,則印出 `Buzz` * 3. 若 i 是 3 與 5 的倍數,則印出 `FizzBuzz` * 4. 若 i 不是 3 與 5 的倍數,則印出 i 之值 * 觀察 `strncpy()` 陳述句,我們推敲出對應上述情況的輸出值與對應範圍 * ----------------------> (FizzBuzz%U\0) * 1. start=0, length=4 (Fizz\0------) * 2. start=4, length=4 (----Buzz\0--) * 3. start=0, length=8 (FizzBuzz\0--) * 4. start=8, length=2 (---------%u\0) * 我們可以發現在第 4 種狀況時,字串仍然輸出並保留了可讓 `printf()` 可辨識的字元,這樣的設計可讓使用者輸出數字。 * length 之值已在第 14 行的程式碼計算出來,而接著需要探討的就是 start 值的問題了,我們根據 `(MSG_LEN >> KK1) >> (KK2 << KK3)` 之 start 值與上述狀況加以分析 (已知 `MSG_LEN` 為 8) * Case 1. 8 -> 0, Right Shift 4 * Case 2. 8 -> 4, Right Shift 1 * Case 3. 8 -> 0, Right Shift 4 * Case 4. 8 -> 8, Right Shift 0 * 根據上述的觀察,僅有在整除 5 時需右移 1 位元;而當一數可整除 3 時 (包含可同時整除 5),則須右移 4 位元;並且在 3 跟 5 都不可整除時,右移位元數須為 0。 * 綜合以上的分析,`KK1` 應為 `div5`,`KK2` 應為 `div3`,`KK3` 應為 `2`,這樣的組合才可滿足要求。