# 2020q3 Homework4 (quiz4)
contributed by < `shauming1020` >
###### tags: `homework`
## 測驗 1
```c=
int hammingDistance(int x, int y) {
return __builtin_popcount(x OP y);
}
```
> population count 簡稱 popcount 或叫 sideways sum,是計算數值的二進位表示中,有多少位元是 1
![](https://i.imgur.com/w0eZmdO.png)
###
- Hamming Distance 計算兩數之間相異 bit 個數,$x \oplus y$ 先將相異的 bit 位置設為 1 後再透過 __builtin_popcount 求出 1 的個數,即為 Hamming Distance
- 不使用 gcc extension 的 C99 實作程式碼
```c=
int hammingDistance(int x, int y) {
x ^= y;
y = 0;
while (x) {
y += (x & 1);
x >>= 1;
}
return y;
}
```
![](https://i.imgur.com/OfHFnSv.png)
### LeetCode 477. Total Hamming Distance
- 暴力法
```c=
int totalHammingDistance(int* nums, int numsSize){
int sum = 0;
for(int i = 0; i < numsSize - 1; i++) {
for (int j = i + 1; j < numsSize; j++) {
sum += __builtin_popcount(nums[i] ^ nums[j]);
}
}
return sum;
}
```
先從較直覺的暴力法著手,numsSize 不斷增長的情況下,時間複雜度為 $O(n^2)$
- 觀察
| nums[i] | bit 3 | bit 2 | bit 1 | bit 0 |
| ------- | ------| ----- | ------| ----- |
| 4 | 0 | 0 | 0 | 1 |
| 14 | 1 | 1 | 1 | 0 |
| 2 | 0 | 0 | 1 | 0 |
| 1s | 1 | 2 | 2 | 0 |
| 0s | 2 | 1 | 1 | 3 |
| sum | 2 | 2 | 2 | 0 |
可以推得總和為所有資料中相對 bit 的 1 總數 * 0 總數,因此 $total sum = 1 \times 2 + 2 \times 1 + 2 \times 1 + 0 \times 3 = 6$
```c=
int* to_bit_array(int num) {
int *bit_array = (int *) malloc(sizeof(int) * 32);
memset(bit_array, 0, sizeof(int) * 32);
for (int i = 0; i < 32; i++, num >>= 1)
bit_array[i] += (num & 1);
return bit_array;
}
void add_bit_array(int *a, int *b) {
for (int i = 0; i < 32; i++)
a[i] += b[i];
}
int totalHammingDistance(int* nums, int numsSize) {
int sum = 0;
int *bit_array = to_bit_array(0);
for (int i = 0; i < numsSize; i++) {
int *tmp = to_bit_array(nums[i]);
add_bit_array(bit_array, tmp);
}
for (int i = 0; i < 32; i++)
sum += bit_array[i] * (numsSize - bit_array[i]);
return sum;
}
```
### 研讀錯誤更正碼簡介及抽象代數的實務應用
> 研讀錯誤更正碼簡介及抽象代數的實務應用,摘錄其中 Hamming Distance 和 Hamming Weight 的描述並重新闡述,舉出實際的 C 程式碼來解說;
搭配閱讀 Hamming codes, Part I 及 Hamming codes, Part II,你可適度摘錄影片中的素材,但應當清楚標註來源
**Hamming Distance & Hamming Weight**
- 定義
- Hamming Distance : 兩向量有幾個相異的 bit
> e.g. $x = (0001011), x' = (1101010), d_H(x,x') = 3$
- Hamming Weight : 一個向量不是 0 的的地方有幾個
> e.g. $w_H(x) = 3, w_H(x') = 4$
- x 和 x' 的 Hamming Distance 只是 (x + x') 的 Hamming Weight
- minimum distance : 在這個碼中,任兩不同 codewords 的 Hamming distance,其當中最小值
- minimum weight : 所有不為 0 的 codewords,其 Hamming weight 的最小值
- 特性
- 由於兩個 codewords 的 Hamming distance 會等於相加後的 Hamming weight,所以對線性碼來說,$d_{min(C)} = w_{min(C)}$,也就是 minimum distance 會等於 minimum weight
- 要改 t 個錯,其 minimum distance 至少要 $2t + 1$
![](https://i.imgur.com/yiAspS9.png)
> 1. 球心 (x, x') 為一個 codeword,球包含所有跟此 codeword 的 Hamming distance <= t 的 binary vectors
> 2. 由於要改 t 個錯,兩球不能交疊在一起,否則交疊的 vectors 不知道要改成 x 還是 x'
> 3. 因此兩球距離至少為 1,即任兩 codewords 距離至少要 $2t + 1$
### 研讀 Reed–Solomon error correction
---
## 測驗 2
>給你一棵樹,樹上有 n 個節點,編號自 0 到 $n − 1$ 。樹以父節點陣列的形式提供,其中 parent[i] 是節點 i 的父節點。樹的根節點是編號為 0 的節點。
>請設計 treeAncestorGetKthAncestor(TreeAncestor *obj, int node, int k) 函式,回傳節點 node 的第 k 個祖先節點。若不存在這樣的祖先節點,回傳 -1。樹節點的第 k 個祖先節點是從該節點到根節點路徑上的第 k 個節點
![](https://i.imgur.com/mRCmU2j.png)
```
Input:
["TreeAncestor","getKthAncestor","getKthAncestor","getKthAncestor"]
[[7,[-1,0,0,1,1,2,2]],[3,1],[5,2],[6,3]]
Output:
[null,1,0,-1]
```
```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);
}
```
###
```graphviz
digraph treeAncestor {
node[shape=record]
node15 [label="{{<value> 15 | {<parent> 7 | 3 | 0 | -1 | -1 | -1}}}"]
0 -> {1, 2}
1 -> {3, 4}
2 -> {5, 6}
3 -> {7, 8}
4 -> {9, 10}
5 -> {11, 12}
6 -> {13, 14}
7 ->{node15, 16}
8 -> {17, 18}
9 -> {19, 20}
10 -> {21, 22}
11 -> {23, 24}
12 -> {25, 26}
13 -> {27, 28}
14 -> {29, 30}
}
```
treeAncestorCreate
- ```#11 obj->parent = malloc(n * sizeof(int *)); ``` 從這行可以知道結構體成員 parent 為 a pointer to a pointer,故 AAA 為 int ** parent
- ```#13 int max_level = 32 - __builtin_clz(n) + 1;``` 計算最大樹高 (skewed)
- ```
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;
}
```
初始化 obj ,index i 表示節點編號,index j 存取**上 $2^{j}$ 代祖先**,因此 ```obj->parent[i][j] = -1;``` 先將每個節點的**每 $2^{j}$ 代祖先**設為 -1 (沒有祖先)
- ```
for (int i = 0; i < parentSize; i++)
obj->parent[i][0] = parent[i];
```
每個節點的上一代 ( j = 0 ) 祖先根據給定的 parent 序列逐一指派,例如 parent 給定 [-1,0,0,1,1,2,2],則第 0 個節點的上一代祖先將被指派為 -1
- ```
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;
}
```
接著建立**上 $2^{j}$ 代祖先**,j 從 1 開始,當存在**上 $2^{j-1}$ 代祖先**時,才會有**上 $2^{j}$ 代祖先**,因此 BBB = -1
treeAncestorGetKthAncestor
- 搜尋**上 k 代祖先**,如果**上 $2^i$ 代祖先存在**,就從上 $2^i$ 代祖先繼續往上搜尋
- 例如 k = 5 (0b101),則 i 在 0、2 時可能會有上 $2^i$ 代祖先存在,如果上 $2^0$ 代祖先存在,則從上一代祖先繼續往上搜尋上 $2^2$ 祖先
###
---
## 測驗 3
```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;
}
```
> 從 1 數到 n,
如果是 3的倍數,印出 “Fizz”
如果是 5 的倍數,印出 “Buzz”
如果是 15 的倍數,印出 “FizzBuzz”
如果都不是,就印出數字本身
###
- 從 ```strncpy(fmt, &"FizzBuzz%u"[(MSG_LEN >> KK1) >> (KK2 << KK3)], length);``` 可以回推程式想執行的事情
- 從 "FizzBuzz%u" 中取出特定長度的字串出來放到變數 fmt,再將其打印出來
- 而要取多長則取決於變數 length,從哪裡開始取則取決於 (MSG_LEN >> KK1) >> (KK2 << KK3) ,其中 MSG_LEN 8
- ```unsigned int length = (2 << div3) << div5;```
- 從 1 數到 n ,如果可以被 3 整除則 div3 將被設成 1,否則為 0 ,同理 div 5
- 因此我們會得到下列四種不同的 length
- 3 的倍數,(2 << 1) << 0 得 4
- 5 的倍數,(2 << 0) << 1 得 4
- 15 的倍數,(2 << 1) << 1 得 8
- 都不是,(2 << 0) << 0 得 2
- ```(MSG_LEN >> KK1) >> (KK2 << KK3)``` 根據以上不同的情境將未知的 KK 填入
- 先考慮 3 和 15 的倍數,要印出 “Fizz” 和 “FizzBuzz” 都得從字串的 0 位開始取,先從選項中取固定常數出來嘗試
- KK1 = 1、KK2 = 2、KK3 = 2,可得 0
- 接著考慮 5 的倍數,要印出 “Buzz” 得從字串的 4 位開始取
- 將 KK2 改成 div3 可得 4
- 而最後考慮都不是的情況,要取得字串的輸出控制符 %u 得從字串的 8 位開始取
- 最後將 KK1 改成 div5 可得 8
---
## 測驗 4
> 考慮到整數 PAGE_QUEUES 可能有以下數值:
> (1 or 2 or 3 or 4)
> (5 or 6 or 7 or 8) × ($2^n$), n from 1 to 14
```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) {
CASES
}
```
其中 CASES 可能形如:
```c
case 2: blockidx /= 2;
break;
```
用最少的 case-break 敘述做出同等 ```size_t blockidx = offset / PAGE_QUEUES;``` 效果的程式碼
- ```__builtin_ffs(x))``` 表示 x 二進位中最後一個 1 的位置
> int __builtin_ffs (unsigned int x)
Returns one plus the index of the least significant 1-bit of x, or if x is zero, returns zero.
- ``` ffs32(x) ((__builtin_ffs(x)) - 1)``` 等同於 ctz ,表示尾端有幾個 0,即該數是否含有 $2^n$
- ```blockidx = offset >> ffs32(divident);``` $offset /= 2^n$,先從 offset 提出 $2^n$
- ```divident >>= ffs32(divident);``` $divident /= 2^n$
- 將 $2^n$ 提出後且 PAGE_QUEUES 最多為 $8 \times 2^n$,故只需判斷 ```blockidx /= y```,y = 2, 3, 5, 7 質數即可
---