2020q3 Homework4 (quiz4)
contributed by < RusselCK
>
指標 & 數值系統 & Bitwise 練習題
- The Hamming distance between two integers is the number of positions at which the corresponding bits are different.
使用 __builtin_popcount
實作
x ^ y
可以將 x
y
兩者對應位置不同的 bits 顯示出來,其 1 的個數即為 Hamming distance
Q1. 進階題
不使用 gcc extension 實作
- find the total Hamming distance between all pairs of the given numbers.
範例
Input: 4, 14, 2
Output: 6
the 4 is 0100, 14 is 1110, and 2 is 0010
So the answer will be:
HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6.
超時版
- 兩兩一組求 Hamming Distance ,最後再加總
- 共需要算 次
不超時版
04 = ( 0100 )
14 = ( 1110 )
02 = ( 0010 )
- 每個 bit 的 #兩兩不同 = ( #1 ) * ( #0 ) = ( #1 ) * ( #nums - #1 )
- 最後加總 32 個 bit 的 #兩兩不同
- 效能評估:
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
Hamming Distance 和 Hamming Weight
錯誤更正碼( Error–Correcting Codes )
由來
- 1948年以前, 大家在做通訊系統的時候, 都只想到要如何使訊號接收得比較好, 比如把天線加大, 或者把訊號傳遞的功率加大, 但這些都是在物理上做改進
- 1948年 Claude Shannon 寫了一篇論文叫做 A mathematical theory of communications, 他說事實上我們不必這樣做, 很多情形只要在資料上做手腳, 只要傳資料的速率小於每個通道特有的量( 通道容量 channel capacity ), 就一定有辦法讓資料錯誤的機率很小, 要多小都可以
- 因為他提出了這件事, 後來造成了整個領域的發展, 這個領域叫做訊息理論 (information theory)
Single–Error Correcting(SEC) Codes / (7, 4) Hamming code
- 只能改一個錯
- (7, 4) : 全部長度是7, 只有4個 bits是原來要傳的資料
- Hamming 在 1950 年所發明的

Hamming codes 的數學結構
- 定義 modulo 2 運算
- 先 加起來 或 乘起來 後,再 除以 2 取 餘數
- 由 (7, 4)Hamming code 規則可以得到:
- 為了與文章一致, ( d4 d3 d2 d1 p3 p2 p1 ) 對應到 ( x1 x2 x3 x4 x5 x6 x7)
線性方程式寫成矩陣
- 的
- 只要給定其中 4 個 bits ,另外 3個 bits 會對應而生
- 這 4 個 bits 共有 種可能 ( codewords )
Syndrome ( 徵狀 )
- 計算 syndrome
- 代表 syndrome 只跟錯誤向量有關, 跟傳哪個 codeword 無關
- 舉例: $y = x + e $ = ( 1101000 ) = ( 1101010 ) + ( 0000010 )
- 根據錯誤的 bit 不同, 的結果也會隨之改變,整理如下:
錯誤的 bit |
x1 |
x2 |
x3 |
x4 |
x5 |
x6 |
x7 |
|
(1000000) |
(0100000) |
(0010000) |
(0001000) |
(0000100) |
(0000010) |
(0000001) |
|
|
|
|
|
|
|
|
- 每一個 syndrome 就對應到一種錯的情況
- 我們只要知道它的徵狀是什麼, 就可以知道錯在那裡, 然後把它改正過來
Hamming Distance & Hamming Weight
- Hamming distance = 兩個向量不一樣的地方有幾個
- Hamming weight = 一個向量不是 0 的的地方有幾個
- x + y 的 Hamming weight = x 和 y 的 Hamming distance
- modulo 2 加法
- 加起來之後不為0 的 bits 的個數 = 它們之間不同的 bits 的個數
- minimum distance
- 任何在 中的兩個不同的 codewords 的 Hamming distance, 它們之中最小的值
- minimum weight
- 所有不為0的 codewords, 它的 Hamming weight 的最小值
- also, minimum distance = minimum weight, i.e.
- minimum distance 是多少?
- 1 ?
- 2 ?
- 3 ?
- Yes! 取第 1、2、7 個 column 相加,可以得到
- 如果要改 個錯, 它的 minimum distance 至少要
證明

- 兩球的中心分別為 codeword and
球內包含 所有與中心的 Hamming distance 小於等於 的 binary vector
- 因為有 個錯要改,所以兩個球不能碰在一起,因此,兩球距離最小值為 1 ( 交集為空集合 )
- 若兩球碰在一起,會有一點和 及 的距離都 小於 ,
此時便不知道要修正為 還是 ,導致不能改 個錯
x 和 x' 的距離 至少要
任兩個 codewords 的距離 至少要
閱讀 Linux 核心原始程式碼的 lib/reed_solomon 目錄,抽離程式碼為單獨可執行的程式,作為 ECC 的示範
- You are given a tree with
n
nodes numbered from 0
to n-1
in the form of a parent array where parent[i]
is the parent of node i
.
The root of the tree is node 0
.
- Implement the function
getKthAncestor(int node, int k)
to return the k
-th ancestor of the given node.
If there is no such ancestor, return -1
.
- The k-th ancestor of a tree node is the
k
-th node in the path from that node to the root.
範例

-
Binary lifting :
- the 7th ancestor = the 4th ancestor of the 2nd ancestor of the 1st ancestor
- the -th ancestor = the -th ancestor of the -th ancestor
the 8th ancestor = the 4th ancestor of the 4th ancestor
- 可改良
int treeAncestorGetKthAncestor()
-
程式碼解析:
TreeAncestor *treeAncestorCreate(int n, int *parent, int parentSize)
- 由
#11
可以知道,AAA 的名字為 parent
,而且是型態為 (int *)
的指標
- 由
#15
、#17
可以知道,obj->parent
是 二維陣列
- 因此
AAA = int **parent
- By Binary Lifting, for each node, 記錄 -th ancestor,
- 因此,我們共需要
n
* 的二維陣列
以 8 bits 為例, n
= 7 = ( 0000 0111 )2
= 3 = 8 - __builtin_clz(7)
- 將
obj->parent
做成 n
* max_level
的 二維陣列:obj->parent[n][max_level]
- 所有初始值 設為
-1
( i.e. no ancestor )
-
範例
parent[i]
存放 nodei 的父親 ( i.e. The 1-th ancestor ) 的位置
obj->parent[i][0]
存放 nodei 的 1-th ancestor
for
迴圈 的終止式 用 parentSize
來判斷而非 n
- 因為 input 提供有關 ancestor 的資料只有
parent[]
- 若 且以
n
來判斷 , 會出現 parent[]
沒充分使用 或 資料不足 的情況
-
範例
- 設置
obj->parent[i][1:max_level]
的資料
- For each node, the -th ancestor = the -th ancestor of the -th ancestor
obj->parent[i][j] = obj->parent[obj->parent[i][j - 1]][j - 1]
- 前提:
obj->parent[i][j - 1]
不為 -1
- 因此, BBB =
(-1)
範例
#23
、#30
:若 quit == 1
,跳出 for
迴圈
#28
:只要當下檢查的欄位 不為 -1
,代表還有欄位可能需要修改,不能跳出 for
迴圈,設置 quit = 0
- Why
max_level
又要 - 1 ?
- 代表先前預想的
max_level
沒有錯
- 可能是
#22
~ #31
有改進空間
善用 cpp=行號
的標注方式,避免逐行輸入行號,後者對之後的編撰無益。
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
jserv
int treeAncestorGetKthAncestor(TreeAncestor *obj, int node, int k)
k = 7 = ( 0000 0111 )2
7th = ( 1th 的 2th ) 的 4th
- by Binary Lifting, CCC =
1 << i
Q2. 進階題
指出可改進的部份
- 在
void treeAncestorFree(TreeAncestor *obj)
中有出現 obj->n
代表 TreeAncestor
struct 等地方需要修改:
- 修改
for
迴圈中止條件
- 因為沒有中止條件,會導致程式多進一次
for
迴圈
- 改進方式:
#14
改為 int max_level = 32 - __builtin_clz(n);
- 設中止條件為
j < max_level
#33
改為 obj->max_level = max_level;
- 效能評估:
- 在 C 語言項目中,執行時間領先 78.29% 的提交者
- 從 1 數到 n
- 如果是 3的倍數,印出 “Fizz”
- 如果是 5 的倍數,印出 “Buzz”
- 如果是 15 的倍數,印出 “FizzBuzz”
- 如果都不是,就印出數字本身
直覺的實作: (naive.c
)
bitwise 觀察
我們若能精準從給定輸入 i
的規律去控制 start
及 length
,即可符合 FizzBuzz 題意:
bitwise 的實作: (bitwise.c
)
div3
、div5
分別可知道 i
是否為 3 或 5 的倍數
- 除法判別可參考 quiz2 Q3 及 Q4
div3 |
div5 |
length |
(( 1000 ) >> KK1) >> (KK2 << KK3) |
1 |
0 |
4 |
0 = ( 0000 ) = 8 >> ( 3 or more ) |
0 |
1 |
4 |
4 = ( 0100 ) = 8 >> 1 |
1 |
1 |
8 |
0 = ( 0000 ) = 8 >> ( 3 or more ) |
0 |
0 |
0 |
8 = ( 1000 ) |
- 若 div5 = 1 ,則 MSG_LEM >> 1
- 若 div3 = 1 ,則 MSG_LEM >> ( 3 or more )
- 綜合上述,KK1 = div5, KK2 = div3, KK3 >= 2
Q4. 除數為 PAGE_QUEUES
的除法
- 整數
PAGE_QUEUES
可能有以下數值:
- (1 or 2 or 3 or 4)
- (5 or 6 or 7 or 8) × (), n from 1 to 14
原本的運算:
預先進行計算,避免整數除法指令的使用:
- (假設在 x86_64 硬體執行 GNU/Linux 並透過 gcc/clang 編譯)
其中, CASES
可能形如
這樣的組合,請用最少的 case-break 敘述做出同等 size_t blockidx = offset / PAGE_QUEUES;
效果的程式碼。
__builtin_ffs(x)
- Returns one plus the index of the least significant 1-bit of x
( 返回右起第一個 1 的位置 )
- if x is zero, returns zero.
-
觀察 PAGE_QUEUES
的範圍
- (1 or 2 or 3 or 4) = ( or or or )
- (5 or 6 or 7 or 8) × (), n from 1 to 14
= ( or or or ) × (), n from 1 to 14
-
PAGE_QUEUES
可以歸納出另一種形式:
- 1 × (), n = 0, 1, 2 and 4 ~ 17
- 3 × (), n = 0 and 2 ~ 15
- ( 5 or 7 ) × (), n = 1 ~ 14
-
觀察上述形式,可以發現
- 除以
PAGE_QUEUES
= 除以 (1 or 3 or 5 or 7) × ()
- 因此,可以先處理 除以 () 部份,再處理 除以 (3 or 5 or 7)
-
程式碼解析:
#2
: fs32(x)
= 中的
__builtin_ffs(x) - 1
= x 最右的 1 後面有幾個 0
#5
: 被除數除以 ( i.e. 右移 )
#6
: 除數除以 ( i.e. 右移 )
Q4. 進階題
善用 __builtin_ffs
,大幅降低記憶體開銷
- The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.
範例
-
觀察上圖的成立解 :
- 每一列 ( 行 ) 必定要有一個 Q
- 放 Q 時
(i, j)
要考慮
- 左上方
( i-k, j-k )
- 正上方
( i-k, j )
- 右上方
( i-k, j+k )
- ~
-
程式碼解析:
ways[]
: 記錄 N-queens 的 互不相同解個數
*returnSize
: *res
的 行數 ( i.e. 解的數量 ways[n]
)
**returnColumnSizes
: *res[w]
的 columns 數 ( i.e. n
)
QueensPos
: 各列 Q
所在的行數
res
: a pointer to pointers to pointers to charater arrays
- 從
row
= 0 開始進行遞迴 放置 Queen
#39
~ #42
:
- 找出合法位置擺放 Queen
- 若
row
= (N-1)
,代表成功擺到最後一列 儲存該解 ( i.e. PushAnswer()
)
若否,進行下一列的遞迴 ( i.e. NQueens(row+1)
)
#35
: 若 w
= ways[N]
,代表已紀錄完所有解,可停止遞迴
- 檢查想擺放的位置是否合法
QueensPos
紀錄各列 Q 所在的行數
- 若
QueensPos[i]
= col
,則 ( i, j )
的 上方 有 Q
- 若
QueensPos[i]
= j-k
or j+k
,則 ( i, j )
的 左上 或 右上 有 Q
k
= abs( QueensPos[i] - j )
k = i-(i-k)
j-k = j-(i-(i-k)) = j - i + (i-k) = (col - row + i)
j+k = j+(i-(i-k)) = j + i - (i-k) = (col + row - i)