# 2020q3 Homework4 (quiz4) contributed by < ``yceugene`` > ###### tags: `linux2020` `sysprog2020` `quiz` [題目連結](https://hackmd.io/@sysprog/2020-quiz4) ## :memo: 目錄 [TOC] ## 測驗 1: 計算 Hamming Distance * 題意: 計算兩整數 x 與 y 的 Hamming distance (其二進位的每個位元的差)。 例如整數 1 的二進位為 0 0 0 1,而整數 4 的二進位為 0 1 0 0,則 1 與 4 的 Hamming distance 為 2。 * 利用 __builtin_popcount() 實作 Hamming Distance 程式碼如下: ``` c int hammingDistance(int x, int y) { return __builtin_popcount(x OP y); } ``` 根據 Hamming distance 的定義,就是在計算兩數二進位表示中相同位置但不同數值的位元有多少,因此要對兩數做 `XOR`,再利用 __builtin_popcount 算出 `x XOR y` 有多少個 1 即可,故 `OP = ^`。 ## 測驗 2: Kth Ancestor of a Tree Node * 題意: 回傳節點 node 的第 k 個祖先節點。若不存在這樣的祖先節點,回傳 -1。 (樹節點的第 k 個祖先節點是從該節點到根節點路徑上的第 k 個節點) * 條件: 樹上有 n 個節點,編號自 0 到 $n-1$。樹以父節點陣列的形式提供,其中 parent[i] 是節點 i 的父節點。樹的根節點是編號為 0 的節點。 ``` c typedef struct { AAA; int max_level; } TreeAncestor; ``` ``AAA = int **parent`` * 參考 C 程式實作: ``` c #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); } ``` * 程式原理: ``TreeAncestor *treeAncestorCreate()`` ``int treeAncestorGetKthAncestor()`` ``void treeAncestorFree(TreeAncestor *obj)`` * __builtin_clz: 返回左起第一個1之前0的個數 ## 3. FizzBuzz * 題意: FizzBuzz 題目 1. 從 1 數到 n,如果是 3的倍數,印出 “Fizz” 2. 如果是 5 的倍數,印出 “Buzz” 3. 如果是 15 的倍數,印出 “FizzBuzz” 4. 如果都不是,就印出數字本身 * 直覺的實作程式碼: ``` C #include <stdio.h> int main() { for (unsigned int i = 1; i < 100; i++) { if (i % 3 == 0) printf("Fizz"); if (i % 5 == 0) printf("Buzz"); if (i % 15 == 0) printf("FizzBuzz"); if ((i % 3) && (i % 5)) printf("%u", i); printf("\n"); } return 0; } ``` * 利用 bitwise 和上述技巧實作的 FizzBuzz 程式碼: ``` C #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; } ``` ## 4. F 整數 PAGE_QUEUES 的數值分佈: * (1 or 2 or 3 or 4) * $(5 or 6 or 7 or 8) × (2n)$, n from 1 to 14 原本的除法: ``` c #include <stdint.h> size_t blockidx = offset / PAGE_QUEUES; ``` 預先進行計算,從而避免整數除法指令的使用: ``` c #include <stdint.h> #define ffs32(x) ((__builtin_ffs(x)) - 1) size_t blockidx; size_t divident = PAGE_QUEUES; blockidx = offset >> ffs32(divident); divident >>= ffs32(divident); switch (divident) { case 2: blockidx /= 2; break; } ``` * 動作說明 : * __builtin_ffs(x)-1 是二進位數字中, 右方有幾個 '0'. 如: __builtin_ffs(0x80)-1 = 7 __builtin_ffs(0x08)-1 = 3 __builtin_ffs(0x04)-1 = 2 __buildin_ffs(0x01)-1 = 0 * x >> ffs32(n) 即 x / $2^n$ 因此只需考慮質數 3, 5, 7