# 2020q3 Homework4 (quiz4)
contribute by <`hsiehong`>
###### tags: `進階電腦系統理論與實作`
outline
[TOC]
### [題目](https://hackmd.io/@sysprog/2020-quiz4)
## 測驗 1
```c=
int hammingDistance(int x, int y) {
return __builtin_popcount(x OP y);
}
```
OP = `^`
### 原理
要計算兩數 `x`, `y` 的 [Hamming distance](https://en.wikipedia.org/wiki/Hamming_distance),先將兩數做 xor 運算,所得結果的數的二進制中 1 的個數即為所求
### LeetCode 練習
[477. Total Hamming Distance](https://leetcode.com/problems/total-hamming-distance/)
* 作法一
最直覺的做法,用雙迴圈將所有結果加總起來,但會 TLE
```c=
class Solution {
public:
int totalHammingDistance(vector<int>& nums) {
size_t numSize = nums.size();
int ham = 0;
for (int i = 0; i<numSize; ++i) {
for (int j = i + 1; j<numSize; ++j) {
ham += __builtin_popcount(nums[i] ^ nums[j]);
}
}
return ham;
}
};
```
* 作法二
參考[解說](https://leetcode.com/problems/total-hamming-distance/discuss/720409/Python-Bit-Manipulation-Explanation-with-Diagram)
```c=
class Solution {
public:
int totalHammingDistance(vector<int>& nums) {
size_t numSize = nums.size();
int bitCnt[32] = {0};
for (int i = 0; i < numSize; i++) {
for (int j = 0; j < 32; j++) {
if (nums[i] & (1 << j)) {
bitCnt[j]++;
}
}
}
int sum = 0;
for (int i = 0; i < 32; i++) {
sum += (numSize - bitCnt[i]) * bitCnt[i];
}
return sum;
}
};
```
主要是利用 bit operation **每個 bit 都是各自獨立的**特性,記錄每個位置 1 跟 0 出現的次數,每個 1 跟每個 0 會貢獻一次 Hamming distance,將兩數相乘就是那個 bit 總共會貢獻的 Hamming distance
## 測驗 2
```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);
}
```
AAA = `int **parent`, BBB = `(-1)`, CCC = `1 << i`
* 在 func `treeAncestorCreate()` 內部,可以看到 line 11 先宣告一個一維空間給 parent ,在 line 15 在對每個單位空間宣告 max_level 大小的空間,可得知 `parent` 是一個二維陣列,所以 AAA = `int **parent`
* line 24 的 for loop 是在對每個點建立祖先,假設要建立第 j 個祖先,要先檢查第 j-1 的祖先是否存在,有的話才將第 $2^{j-1}$ 個 parent 的第 $2^{j-1}$ parent 加入 (binary tree) 寫入第 $2^j$ 個祖先,所以 `BBB = -1`
## 測驗 3
FizzBuzz !!!
從 1 數到 n,如果是 3的倍數,印出 “Fizz”,如果是 5 的倍數,印出 “Buzz”,如果是 15 的倍數,印出 “FizzBuzz”,如果都不是,就印出數字本身,觀察下列 offset,可以透過 offset 來控制期望的輸出
```c
string literal: "fizzbuzz%u"
offset: 0 4 8
```
```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;
}
```
KK1 = `div5`, KK2 = `div3`, KK3 = `2`
* `div3`, `div5` 分別代表 n 是否可以被 3, 5 整除,並決定好要複製的起點和長度。觀察 `length`
* 若只可被 3 整除 (2 << 1 << 0) = 4
* 只可被 5 整除 (2 << 0 << 1) = 4
* 若同時可被 3 跟 5 整除 (2 << 1 << 1) = 8
* 否則其他狀況 = 2
* 觀察每個狀態的起始位置
```c
string literal: "fizzbuzz%u"
offset: 0 4 8
```
| div3 | div5 | start(MSG_LEN) |
|:----:|:----:|:--------------:|
| 1 | 0 | 0 |
| 0 | 1 | 4 |
| 1 | 1 | 0 |
| 0 | 0 | 8 |
可以推得 `(MSG_LEN >> KK1) >> (KK2 << KK3)`,其中 KK1 = `div5`, KK2 = `div3`, KK3 = `2`
## 測驗 4
TODO