# 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`