sysprog2020
homework
contributed by < JKChun
>
1
解釋上述程式碼運作原理,並舉出不使用 gcc extension 的 C99 實作程式碼;
以下節錄自 Wikipedia 的 Hamming Distance
the Hamming distance between two strings of equal length is the number of positions at which the corresponding symbols are different. In other words, it measures the minimum number of substitutions required to change one string into the other, or the minimum number of errors that could have transformed one string into the other.
The symbols may be letters, bits, or decimal digits, among other possibilities.
所以二進位數的 Hamming Distance 就是指兩個二進位數有幾個 bit 不一樣。
Example:
直覺版:
不使用 gcc extension 的話,就自己實作 __builtin_popcount(),想法就是在 while loop 裡檢查兩數在 XOR 運算後的結果的 LSB 是否為1,接著往右 shift 一個位元,直到每個位元都檢查一遍。
應該可以用 bitwise operation,不需要用 while loop 去實作 __builtin_popcount()
練習 LeetCode 477. Total Hamming Distance,你應當提交 (submit) 你的實作 (C 語言實作),並確保執行結果正確且不超時
先以直覺的想法,兩兩比對 array 中的所有元素,以 input 為 2、4、6、10、14 舉例:
Combination | HammingDistance |
---|---|
(2,4) | 2 |
(2,6) | 1 |
(2,10) | 1 |
(2,14) | 2 |
(4,6) | 1 |
(4,10) | 3 |
(4,14) | 2 |
(6,10) | 2 |
(6,14) | 1 |
(10,14) | 1 |
total hamming distance | 16 |
假設有 n 個元素,則比對的次數為: 次,時間複雜度為,在此例 n = 5,要比對 10 次,total hamming distance 為 16,有沒有辦法降低時間複雜度?
這次把數字的二進位列出來看:
input | 4th bit | 3rd bit | 2nd bit | 1st bit |
---|---|---|---|---|
2 | 0 | 0 | 1 | 0 |
4 | 0 | 1 | 0 | 0 |
6 | 0 | 1 | 1 | 0 |
10 | 1 | 0 | 1 | 0 |
14 | 1 | 1 | 1 | 0 |
hamming distance | 6 | 6 | 4 | 0 |
可以發現所有數字在同一位置的 bit 的 hamming distance 全部累加起來即為 total hamming distace
以及特定位置的 bit 的 hamming distance 就是 0 的總數乘上 1 的總數
。
例:1st bit 有 5 個 0、0 個 1,所以全部數字之間彼此在 1st bit 的 Hamming Distance 為 5x0 = 0。
n'th bit | 0的數量 | 1的數量 | hamming distance |
---|---|---|---|
1st | 5 | 0 | 5x0 = 0 |
2nd | 1 | 4 | 1x4 = 4 |
3rd | 2 | 3 | 2x3 = 6 |
4th | 3 | 2 | 3x2 = 6 |
counter_for_one_bit
去記錄每個位置的 bit 有幾個 1,以 input 為 2、4、6、10、14 舉例:
input | 4th bit | 3rd bit | 2nd bit | 1st bit |
---|---|---|---|---|
2 | 0 | 0 | 1 | 0 |
4 | 0 | 1 | 0 | 0 |
6 | 0 | 1 | 1 | 0 |
10 | 1 | 0 | 1 | 0 |
14 | 1 | 1 | 1 | 0 |
one_bit[3] | one_bit[2] | one_bit[1] | one_bit[0] | |
2 | 3 | 4 | 0 |
nums
一次,每次用 while 迴圈去檢查 input 有幾個 1__builtin_ctz(number)
才能找出下一個 1 的位置,所以要製作一個 0010 的 mask,由於我們要削除的 1 一定是從 LSB 數過來的第一個 1,所以取 number 的 2 補數可以把第一個 1 後面的 bit 全部作 NOT 運算(從 1010 變成 0110),利用 A.=0 的特性,number & -number
就可以得出 mask 0010
,再讓 number 和 mask 做 XOR 運算就可以削除 1。iteration | 1 | 2 |
---|---|---|
number | 1010 | 1000 |
__builtin_ctz(number) | 1 | 3 |
-number | 0110 | 1000 |
number & -number | 0010 | 1000 |
number ^ (number & -number) | 1000 | 0000 |
研讀錯誤更正碼簡介及抽象代數的實務應用,摘錄其中 Hamming Distance 和 Hamming Weight 的描述並重新闡述,舉出實際的 C 程式碼來解說;
搭配閱讀 Hamming codes, Part I 及 Hamming codes, Part II,你可適度摘錄影片中的素材,但應當清楚標註來源
研讀 Reed–Solomon error correction,再閱讀 Linux 核心原始程式碼的 lib/reed_solomon 目錄,抽離後者程式碼為單獨可執行的程式,作為 ECC 的示範。
2
給你一棵樹,樹上有 n 個節點,編號自 0 到 。樹以父節點陣列的形式提供,其中
parent[i]
是節點 i 的父節點。樹的根節點是編號為 0 的節點。請設計treeAncestorGetKthAncestor(TreeAncestor *obj, int node, int k)
函式,回傳節點 node 的第 k 個祖先節點。若不存在這樣的祖先節點,回傳-1
。樹節點的第 k 個祖先節點是從該節點到根節點路徑上的第 k 個節點
範例 input :
範例 output :
依照範例 input 建立出來的 tree:
[7,[-1,0,0,1,1,2,2]]
中,7
為treeAncestorCreate()
的 parameter n
,n
代表樹的節點數量,[-1,0,0,1,1,2,2]
為treeAncestorCreate()
的 parameter parent
,其中-1
代表節點 0 為 root 沒有父節點,兩個0
代表節點 1 和 2 的父節點為節點 0,剩下的兩個1
和2
分別代表節點 3 和 4 的父節點為節點 1 及節點 5 和 6 的父節點為節點 2。解釋上述程式碼運作原理,指出可改進的地方,並實作對應的程式碼;
在
treeAncestorCreate
函式內部,若干記憶體被浪費,請撰寫完整測試程式碼,並透過工具分析;
在 LeetCode 1483. Kth Ancestor of a Tree Node 提交你的實作,你應該要在 C 語言項目中,執行時間領先 75% 的提交者;
3
解釋上述程式運作原理並評估
naive.c
和bitwise.c
效能落差
- 避免
stream I/O
帶來的影響,可將printf
更換為sprintf
分析 Faster remainders when the divisor is a constant: beating compilers and libdivide 一文的想法 (可參照同一篇網誌下方的評論),並設計另一種 bitmask,如「可被 3 整除則末位設為 1」「可被 5 整除則倒數第二位設定為 1」,然後再改寫 bitwise.c 程式碼,試圖運用更少的指令來實作出 branchless;
4
解釋程式運算原理,可搭配 Microsoft Research 的專案 snmalloc 來解說;
- 對應的原始程式碼 src/mem/sizeclass.h
練習 LeetCode 51. N-Queens,應選擇 C 語言並善用上述的
__builtin_ffs
,大幅降低記憶體開銷;