---
# System prepended metadata

title: 2020q3 Homework4 (quiz4)
tags: [sysprog2020]

---

# 2020q3 Homework4 (quiz4)
contributed by \<joe-U16>
[測驗題](https://hackmd.io/@sysprog/2020-quiz4)
## 測驗 `1`

```cpp
int hammingDistance(int x, int y) {
    return __builtin_popcount(x OP y);
}
// OP = ^
```
**程式碼運作原理:**
* 做 `x ^ y` 後，會比對 `x` `y` 的每個 bit 的值，如果該 bit 的值不一樣，該 bit 會 output `1`
    * e.g. `0b0011 ^ 0b0001` => `0b0010`
* 而Hamming Distance 就是比對有幾個 `bit` 不一樣，所以利用 `__builtin_popcount` 把 `xor` 完後，output 為 1 的 value 加總即為所求

**不使用 gcc extension 的方法:**
實作出計算 `1` 個數的程式碼
```cpp=
int hammingDistance(int x, int y) {
    int n = x ^ y;
    unsigned int count = 0;
    while (n) {
        if (n & 1) {count++; }
        n >>= 1;
    }
    return count;
}
```
**練習 LeetCode 477. Total Hamming Distance**
如果用兩個 `for` 迴圈，裡面 call 計算差異個數，會得到 `TLE`

換一個方法：
```cpp
int totalHammingDistance(int* nums, int numsSize){
    int count = 0;
    for (int i = 0; i < 32; i++) {
        unsigned int k = 0;
        for (int j = 0; j < numsSize; j++) {
            if ((nums[j] >> i) & 1) {
                k++;
            }
        }
        count += k * (numsSize - k);
        }
    return count;
    }
```
* 一個 `int` 有 32 個 bit ，所以做 `32` 次
* 把是 `1` 的 bit 的個數可能是 k 個，是 0 的個數有 `(numSize - k)` 這兩個數相乘就會是該 `bit` 要改變的次數
* Time Complexity: `O(n)`










## 測驗 `2`
* struct TreeAncestor 有一個 max_level
    * `max_level = 32 - __builtin_clz(n) + 1;` 
    * 可以得到這棵 tree 最大的 level
* 在最開始幫 obj 配置一段 TreeAncestor 大小的記憶體空間
* 再來幫 obj->parent 配置 n 倍的 pointer to integer
    * `malloc(n * sizeof(int *));`
    * 從這裡可以看出 AAA = `(b)int **parent`
* 幫每個 `obj->pointer[i]` 配置最大可能祖先數的記憶體大小，這邊的 `i` 代表第幾個 node
* 每個 node i 的第0個 `obj->pointer[i][0]` 都是存自己的parent
* 下面程式碼
```
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];
```
判斷如果 node i 是有 parent 的話，就會在自己的 `obj->parent[i][j]` 的 j 格上紀錄自己的 ancessor ，而這個 ancestor 是 2 的指數倍的祖先
e.g. 
`obj->parent[i][0]` 紀錄 node i 第$2^0$輩的祖先，即為父親
`obj->parent[i][1]` 紀錄 node i 第$2^1$輩的祖先，即為祖父
`obj->parent[i][2]` 紀錄 node i 第$2^2$輩的祖先，為曾曾祖父，依此類推

BBB = `(-1)` ，在 treeAncestorCreate 建立時，也建立自己的 2 的指數祖先表。

* `treeAncestorGetKthAncestor`
    * `k & CCC`
    * 在判斷 有哪些 2 的次方，
    * e.g. `101` 即為有 $2^0$ 以及 $2^2$
        * 會先找到 第 $2^0$ 輩祖先
        * 找第 $2^2$ 輩祖先
    * 可知 CCC = `(f) 1<<i`







## 測驗 `3`
* 考慮貌似簡單卻蘊含實作深度的 [FizzBuzz](https://en.wikipedia.org/wiki/Fizz_buzz) 題目:
    * 從 1 數到 n，如果是 3的倍數，印出 “Fizz”
    * 如果是 5 的倍數，印出 “Buzz”
    * 如果是 15 的倍數，印出 “FizzBuzz”
    * 如果都不是，就印出數字本身



以下是利用 bitwise 和上述技巧實作的 FizzBuzz 程式碼: (bitwise.c)

```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;
}
// KK1 = div5
// KK2 = div3
// KK3 = 2
```

**程式碼運作原理:**
* `uint8_t div3 = is_divisible(i, M3);`
    * 可判斷 `i` 是否可被 `3` 整除，同理 div5
* `strncpy(fmt, &"FizzBuzz%u"[(MSG_LEN >> KK1) >> (KK2 << KK3)], length);`
    * 若 `div3=1, div5=0` 則需到字串第0位元的位址 `(MSG_LEN >> KK1) >> (KK2 << KK3)` 需為 `0` ，`KK2=div3` `KK3=2` 為 `8 >> 4` 得 `0`
     * 若 `div3=0, div5=1` 則需到字串第4個字元的位址 ， `KK1=div5`，`8 >> 1` 得 `4`
     *  若 `div3=1, div5=1`  則需到字串第0字元的位址
     *  若 `div3=0, div5=0` 則需到字串地8個字元的位址


## 測驗 `4`
考慮到整數 PAGE_QUEUES 可能有以下數值:
* (1 2 3 4)
* (5 6 7 8) X ($2^n$), n from 1 to 14

給定 size_t offset，試著將原本運算:
```cpp=
#include <stdio.h>
size_t blockidx = offset / PAGE_QUEUES
```
透過下述程式碼，取逮除法運算:
```cpp=
#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) {
    CASES
}
```
考慮到整數 PAGE_QUEUES 可能有以下數值:

> 摘自 [Built-in Functions Provided by GCC](https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html):
Built-in Function: int __builtin_ffs (int x)
Returns one plus the index of the least significant 1-bit of x, or if x is zero, returns zero.
`__builtin_ffs(x) - 1` 可得 x 的 index

**程式碼運作原理:**
* `blockidx = offset >> ffs32(divident);`
    * 如果要除的整數作因式分解後含有 `2` ，皆可透過右移運算消除此整數裡的 `2` 得到 `1 3 5 7` 等數，之後再對 `1 3 5 7` 做處理
    * 而 `1` 除 offset 即為 offset 不需處理
    * 答案為 (b)3 、 (d)5 、 (f)7



###### tags: `sysprog2020`