# 2021q1 Homework1 (quiz1) contributed by < `Edwardz43` > > [第一周測驗題](https://hackmd.io/@sysprog/linux2021-quiz1) ## LLL = ? ```c= static inline void list_concat(node_t **left, node_t *right) { while (*left) LLL; *left = right; } ``` ## AAA = ? BBB = ? ```c= ... node_t *pivot = *list; int value = pivot->value; node_t *p = pivot->next; pivot->next = NULL; node_t *left = NULL, *right = NULL; while (p) { node_t *n = p; p = p->next; list_add_node_t(n->value > value ? AAA : BBB, n); } quicksort(&left); quicksort(&right); ... ``` ## CCC = ? ```c= void quicksort(node_t **list) { if (!*list) return; node_t *pivot = *list; int value = pivot->value; node_t *p = pivot->next; pivot->next = NULL; ... node_t *result = NULL; list_concat(&result, left); CCC; *list = result; } ``` --- ## 原理 ```graphviz digraph g { nodesep=.5 rankdir=LR; node [shape = record,width=.5,height=.1]; store0 [label=" {<n>4 | <p>}"]; store1 [label=" {<n>2 | <p>}"]; store2 [label=" {<n>5 | <p>}"]; store3 [label=" {<n>3 | <p>}"]; store4 [label=" {<n>1 | <p>}"]; store5 [label=" {<n>7 | <p>}"]; store6 [label=" {<n>6 | <p>}"]; store0:p -> store1:n; store1:p -> store2:n; store2:p -> store3:n; store3:p -> store4:n; store4:p -> store5:n; store5:p -> store6:n; } ``` --- ```graphviz digraph g { nodesep=.5 rankdir=LR; node [shape = record,width=.5,height=.1]; pivot [label=" pivot | {<n> | <p>}"]; store0 [label=" {<n>4 | <p>}"]; store1 [label=" {<n>2 | <p>}"]; store2 [label=" {<n>5 | <p>}"]; store3 [label=" {<n>3 | <p>}"]; store4 [label=" {<n>1 | <p>}"]; store5 [label=" {<n>7 | <p>}"]; store6 [label=" {<n>6 | <p>}"]; store0:n -> pivot:n; store0:p -> store1:n [style=dotted]; store1:p -> store2:n; store2:p -> store3:n; store3:p -> store4:n; store4:p -> store5:n; store5:p -> store6:n; } ``` * 先以第一個 `node` 作為 `pivot`,將 list 拆成兩個部份 --- ```graphviz digraph g { nodesep=.5 node [shape = record,width=.5,height=.1]; rankdir=LR; pivot [label=" pivot |{<n>4 | <p>}"]; // left left0 [label=" left | {<n> | <p>}"] store1:n -> left0:n [style=bold, color=blue]; store1 [label=" {<n>2 | <p>}"]; store2 [label=" {<n>5 | <p>}"]; store3 [label=" {<n>3 | <p>}"]; store4 [label=" {<n>1 | <p>}"]; store5 [label=" {<n>7 | <p>}"]; store6 [label=" {<n>6 | <p>}"]; store1:p -> store2:n [style=dotted]; store2:p -> store3:n; store3:p -> store4:n; store4:p -> store5:n; store5:p -> store6:n; store1:n -> pivot:n [color=red, dir=both,label="compare", fontcolor=darkgreen]; } ``` * 因為 2 < 4 , 所以將 2 放到 `left` --- ```graphviz digraph g { nodesep=.5 node [shape = record,width=.5,height=.1]; rankdir=LR; pivot [label=" pivot | {<n>4 | <p>}"]; // left left0 [label=" left | {<n>2 | <p>}"]; // right right0 [label=" right | {<n> | <p>}"]; store2:n -> right0:n [style=bold, color=blue]; store2 [label=" {<n>5 | <p>}"]; store3 [label=" {<n>3 | <p>}"]; store4 [label=" {<n>1 | <p>}"]; store5 [label=" {<n>7 | <p>}"]; store6 [label=" {<n>6 | <p>}"]; store2:p -> store3:n [style=dotted]; store3:p -> store4:n; store4:p -> store5:n; store5:p -> store6:n; store2:n -> pivot:n [color=red, dir=both, label="compare", fontcolor=darkgreen]; } ``` * 5 -> `right` --- ```graphviz digraph g { nodesep=.5 node [shape = record,width=.5,height=.1]; rankdir=LR; pivot [label=" pivot | {<n>4 | <p>}"]; // left left0 [label=" left | {<n>2 | <p>}"]; left1 [label=" {<n> | <p>}"]; left0:p -> left1:n; store3:n -> left1:n [style=bold, color=blue]; // right right0 [label=" right | {<n>5 | <p>}"]; store3 [label=" {<n>3 | <p>}"]; store4 [label=" {<n>1 | <p>}"]; store5 [label=" {<n>7 | <p>}"]; store6 [label=" {<n>6 | <p>}"]; store3:p -> store4:n[style=dotted]; store4:p -> store5:n; store5:p -> store6:n; store3:n -> pivot:n [color=red, dir=both, label="compare", fontcolor=darkgreen]; } ``` * 3 -> `left` --- ```graphviz digraph g { nodesep=.5 node [shape = record,width=.5,height=.1]; rankdir=LR; pivot [label=" pivot | {<n>4 | <p>}"]; store4 [label=" {<n>1 | <p>}"]; store5 [label=" {<n>7 | <p>}"]; store6 [label=" {<n>6 | <p>}"]; // left left0 [label=" left | {<n>2 | <p>}"]; left1 [label=" {<n>3 | <p>}"]; left2 [label=" {<n> | <p>}"]; left0:p -> left1:n; left1:p -> left2:n; store4:n -> left2:n [style=bold, color=blue]; // right right0 [label=" right | {<n>5 | <p>}"]; store4:p -> store5:n [style=dotted]; store5:p -> store6:n; store4:n -> pivot:n [color=red, dir=both, label="compare", fontcolor=darkgreen]; } ``` * 1 -> `left` --- ```graphviz digraph g { nodesep=.5 node [shape = record,width=.5,height=.1]; rankdir=LR; pivot [label=" pivot | {<n>4 | <p>}"]; store5 [label=" {<n>7 | <p>}"]; store6 [label=" {<n>6 | <p>}"]; // left left0 [label=" left | {<n>2 | <p>}"]; left1 [label=" {<n>3 | <p>}"]; left2 [label=" {<n>1 | <p>}"]; left0:p -> left1:n; left1:p -> left2:n; // right right0 [label=" right | {<n>5 | <p>}"]; right1 [label="{<n> | <p>}"]; right0:p -> right1:n; store5:n -> right1:n [style=bold, color=blue]; store5:p -> store6:n [style=dotted]; store5:n -> pivot:n [color=red, dir=both, label="compare", fontcolor=darkgreen]; // [arrowhead=vee, dir=both, tailclip=true, arrowsize=1] } ``` * 7 -> `right` --- ```graphviz digraph g { nodesep=.5 node [shape = record,width=.5,height=.1]; rankdir=LR; pivot [label=" pivot | {<n>4 | <p>}"]; store6 [label=" {<n>6 | <p>}"]; // left left0 [label=" left | {<n>2 | <p>}"]; left1 [label=" {<n>3 | <p>}"]; left2 [label=" {<n>1 | <p>}"]; left0:p -> left1:n; left1:p -> left2:n; // right right0 [label=" right | {<n>5 | <p>}"]; right1 [label=" {<n>7 | <p>}"]; right2 [label=" {<n> | <p>}"]; right0:p -> right1:n; right1:p -> right2:n; store6:n -> right2:n [style=bold, color=blue]; store6:n -> pivot:n [color=red, dir=both, label="compare", fontcolor=darkgreen]; } ``` * 6 -> `right` --- ```graphviz digraph g { nodesep=.5 node [shape = record,width=.5,height=.1]; rankdir=LR; subgraph cluster0 { style=filled; color=lightgrey; label = "right -> quicksort(right)"; // right right0 [label=" right | {<n>5 | <p>}"]; right1 [label=" {<n>7 | <p>}"]; right2 [label=" {<n>6 | <p>}"]; right0:p -> right1:n; right1:p -> right2:n; } pivot [label=" pivot | {<n>4 | <p>}"]; subgraph cluster1 { style=filled; color=lightgrey; label = "left -> quicksort(left)"; left0 [label=" left | {<n>2 | <p>}"]; left1 [label=" {<n>3 | <p>}"]; left2 [label=" {<n>1 | <p>}"]; left0:p -> left1:n; left1:p -> left2:n; } } ``` 之後會透過遞迴,將得到的 `left` 與 `right` 再去執行 `quicksort`,直到排序結束,最後再將 `left` 、 `pivot` 、 `right` 經由 `list_concat` 合併 ```graphviz digraph g { nodesep=.5 node [shape = record,width=.5,height=.1]; rankdir=LR; left0 [label=" left | {<n>1 | <p>}"]; left1 [label=" {<n>2 | <p>}"]; left2 [label=" {<n>3 | <p>}"]; pivot [label=" pivot | {<n>4 | <p>}"]; right0 [label=" right | {<n>5 | <p>}"]; right1 [label=" {<n>6 | <p>}"]; right2 [label=" {<n>7 | <p>}"]; left0:p -> left1:n; left1:p -> left2:n; left2:p -> pivot:n [style=bild, color=blue,label="concat"]; pivot:p -> right0:n [style=bild, color=blue,label="concat"]; right0:p -> right1:n; right1:p -> right2:n; } ``` 根據以上的流程, `list_add_node_t` 的功能就是將 `last element of left` 的 `next` 接到 `right` 的 `first element`,所以 `LLL` 的內容是將 pointer to pointer to `left` (==node_t ** left==) 指向到 last element ```c= ... while (*left) left = &((*left)->next); ... ``` 因為題目的要求為遞增排序,所以當 `n->value` > value of `pivot` 時,要把 n 放到 `right`(AAA) , 反之則是放到 `left`(BBB) ```c= ... while (p) { node_t *n = p; p = p->next; list_add_node_t(n->value > value ? &right : &left, n); } ... ``` 最後透過 `list_concat` 合併時, 依序是 `left` -> `pivot` -> `right` ```c= ... node_t *result = NULL; list_concat(&result, left); // CCC list_concat(&result, pivot); list_concat(&result, right); *list = result; ... ``` --- ## 修正隨機問題 根據 `man-pages` [rand(3)](https://man7.org/linux/man-pages/man3/rand.3.html) 的說明 >The rand() function returns a pseudo-random integer in the range 0 to RAND_MAX inclusive (i.e., the mathematical range[0, RAND_MAX]). The srand() function sets its argument as the seed for a new sequence of pseudo-random integers to be returned by rand(). These sequences are repeatable by calling srand() with the same seed value. If ==no seed== value is provided, the rand() function is ==automatically seeded with a value of 1==. 所以直接使用 rand() 會出現相同的結果 改進的辦法就是利用 `srand()` 餵給它不同的 `seed` ```c= ... seed = atoi(argv[1]); nloops = atoi(argv[2]); srand(seed); for (int j = 0; j < nloops; j++) { r = rand(); printf("%d\n", r); } ... ``` 但是這邊又衍生了另一個問題:如何確保給定的 `seed` 是~~隨機~~不同的?查了一下比較常見的作法是利用 `time()` function 回傳的 timestamp 當作 `seed` ```c= srand(time(NULL)); ``` 實際測試的結果,可以產出不同的隨機數列 ```c= srand(time(NULL)); for (int i = 0; i < 10; i++) { printf("%d ", rand()); } printf("\n"); ``` ```shell= $ ./rand 2077753944 2041748551 282386150 900842139 643100466 2047655698 1595456208 76206106 1886978341 1804387094 $ ./rand 732364144 1383447097 1435339213 469217589 658707237 563652290 333147698 981652687 2055214820 1302689626 ``` 不過這邊又發現了一個問題:因為 `time()` 回傳的 timestamp 單位是秒(second),所以如果在很短的時間內重複呼叫,會使得 `srand()` 吃到同樣的 seed 而導致產出一樣的隨機數列 比較簡易的解決方案就是提高 timestamp 的解析度:用 `microsecond` 取代 `second` ```c= long long current_timestamp() { struct timeval tv; // get current time gettimeofday(&tv, NULL); // calculate microseconds long long microseconds = tv.tv_sec * 1000000 + tv.tv_usec; return microseconds; } ``` ```c= struct timeval { __time_t tv_sec; /* Seconds. */ __suseconds_t tv_usec; /* Microseconds. */ }; ``` `timevl` 包含了兩個 member : 當下時間的秒 (tv_sec) 與 微秒(tv_usec),解析度可提高 $10^6$ > $1 second = 10^6 microseconds$ `gettimeofday()` 可以取得當下的 time value ,經過簡單的運算所取得的 timestamp,足以滿足目前的需求 ```c= int main(int argc, char **argv) { ... srand(current_timestamp()); while (count--) list = list_make_node_t(list, rand() % 1024); ... } ``` :::info keyword: Address Space Layout Randomization ::: ### References [Performance and Entropy of Various ASLR Implementations](http://pages.cs.wisc.edu/~riccardo/736finalpaper.pdf) --- ## Optimized QuickSort without recursion > TODO ###### tags: `week1` `sysprog2021q1`