# 2021q1 Homework1 (quiz1) contributed by < `qwe661234` > ###### tags: `linux2021` ## 程式運作原理 ### `node_t` structure ```cpp typedef struct __node { int value; struct __node *next; } node_t; ``` ### list_add_node_t ```cpp static inline void list_add_node_t(node_t **list, node_t *node_t) { node_t->next = *list; *list = node_t; } ``` ```graphviz digraph graphname{ node[shape=box] ptr[shape=plaintext,fontcolor=red] rankdir=LR { A[label=A] B[label=B] C[label=C] D[label=new_node] NULL1[label=NULL,shape=plaintext] NULL2[label=NULL,shape=plaintext] } { rank="same"; list[shape=plaintext, fontcolor=blue] list->ptr->A } A->B->C->NULL1 D->NULL2 } ``` ```graphviz digraph graphname{ node[shape=box] ptr[shape=plaintext,fontcolor=red] rankdir=LR { A[label=A] B[label=B] C[label=C] D[label=new_node] NULL1[label=NULL,shape=plaintext] } { rank="same"; list[shape=plaintext, fontcolor=blue] list->ptr->D } D->A->B->C->NULL1 } ``` ### list_concat 在 while loop 中找到 list left 的尾端,並將另一個 list right 接在 list left 尾端 ``` cpp static inline void list_concat(node_t **left, node_t *right) { while (*left) left = &((*left)->next); *left = right; } ``` ```graphviz digraph graphname{ node[shape=box] left[shape=plaintext,fontcolor=red] right[shape=plaintext,fontcolor=blue] ptr1[shape=plaintext] rankdir=LR { A[label=left1] B[label=left2] C[label=left3] NULL1[label=NULL,shape=plaintext] } { D[label=right1] E[label=right2] F[label=right3] NULL2[label=NULL,shape=plaintext] } left->ptr1 { rank="same"; ptr1->A } { rank="same"; right->D } A->B->C->NULL1 D->E->F->NULL2 } ``` ```cpp while (*left) left = &((*left)->next); ``` ```graphviz digraph graphname{ node[shape=box] left[shape=plaintext,fontcolor=red] right[shape=plaintext,fontcolor=blue] ptr1[shape=plaintext] rankdir=LR { A[label=left1] B[label=left2] C[label=left3] NULL1[label=NULL,shape=plaintext] } { D[label=right1] E[label=right2] F[label=right3] NULL2[label=NULL,shape=plaintext] } left->ptr1 { rank="same"; ptr1->NULL1 } { rank="same"; right->D } A->B->C->NULL1 D->E->F->NULL2 } ``` ```cpp *left = right; ``` ```graphviz digraph graphname{ node[shape=box] left[shape=plaintext,fontcolor=red] right[shape=plaintext,label="right\n(original addr of left3's next)", fontcolor=blue] rankdir=LR { A[label=left1] B[label=left2] C[label=left3] D[label=right1] E[label=right2] F[label=right3] NULL1[label=NULL,shape=plaintext] } left->right { rank="same"; right->D } A->B->C->D->E->F->NULL1 } ``` ### quicksort ```cpp 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 *left = NULL, *right = NULL; while (p) { node_t *n = p; p = p->next; list_add_node_t(n->value > value ? &right : &left, n); } quicksort(&left); quicksort(&right); node_t *result = NULL; list_concat(&result, left); list_concat(&result, pivot); list_concat(&result, right); *list = result; } ``` 選定 list_head 作為 pivot,將 value 比 pivot 大的值接在 list right,將 value 比 pivot 小的值接在 list left,再分別對 list left 和 list right 繼續遞迴做 quicksort,當分出來的 list left 和 list right 都 return 後,依序將 list left, pivot, right 接上 ```graphviz digraph graphname{ node[shape=box] pivot[shape=plaintext] left[shape=plaintext,fontcolor=red] right[shape=plaintext,fontcolor=blue] rankdir=LR { A[label=3] B[label=4] C[label=5] D[label=1] E[label=2] NULL1[label=NULL,shape=plaintext] } A->B->C->D->E->NULL1 } ``` 選定 list_head 作為 pivot ```graphviz digraph graphname{ node[shape=box] pivot[shape=plaintext] left[shape=plaintext,fontcolor=red] right[shape=plaintext,fontcolor=blue] rankdir=LR { A[label=3] B[label=4] C[label=5] D[label=1] E[label=2] NULL1[label=NULL,shape=plaintext] NULL2[label=NULL,shape=plaintext] } { rank="same" pivot->A } A->NULL2 B->C->D->E->NULL1 } ``` 將 value 比 pivot 大的值接在 list right,將 value 比 pivot 小的值接在 list left ```graphviz digraph graphname{ node[shape=box] pivot[shape=plaintext] left[shape=plaintext,fontcolor=red] right[shape=plaintext,fontcolor=blue] rankdir=LR { A[label=3] B[label=4] C[label=5] D[label=1] E[label=2] NULL1[label=NULL,shape=plaintext] NULL2[label=NULL,shape=plaintext] NULL3[label=NULL,shape=plaintext] } { rank="same" right->C } { rank="same" left->E } { rank="same" pivot->A } A->NULL2 C->B->NULL3 E->D->NULL1 } ``` 對 list left 和 list right 繼續遞迴做 quicksort,最後會得到排序好的 list left 和 list right ```graphviz digraph graphname{ node[shape=box] pivot[shape=plaintext] left[shape=plaintext,fontcolor=red] right[shape=plaintext,fontcolor=blue] rankdir=LR { A[label=3] B[label=4] C[label=5] D[label=1] E[label=2] NULL1[label=NULL,shape=plaintext] NULL2[label=NULL,shape=plaintext] NULL3[label=NULL,shape=plaintext] } { rank="same" right->B } { rank="same" left->D } { rank="same" pivot->A } A->NULL2 B->C->NULL3 D->E->NULL1 } ``` 依序將 list left, pivot, right 接上 ```graphviz digraph graphname{ node[shape=box] result[shape=plaintext] rankdir=LR { A[label=3] B[label=4] C[label=5] D[label=1] E[label=2] NULL1[label=NULL,shape=plaintext] } { rank="same" result->D } D->E->A->B->C->NULL1 } ``` ## 延伸問題 ### 1.測試程式使用到 random,多執行幾次可發現輸出結果相仿,請修正,並引入其他 Pseudorandom number generator ### 修正多執行幾次輸出結果相仿: 引入亂數種 >所謂亂數種子可以視為是產生亂數的一種規律,如果不加以事先設定,則每次執行程式時產生的亂數規律都會一模一樣;為了避免亂數產生順序被使用者完全掌控,因此我們常藉助系統時間的變動性來作為亂數種子,這樣每次執行程式時會因為時間不同,其產生的亂數規律與結果也不一樣 ```cpp srandom(time(0)); node_t *list = NULL; while (count--) { list = list_make_node_t(list, random() % 1024); } ``` ### 引入其他 Pseudorandom number generator 這次使用的是 **Lagged Fibonacci generator** **Fibonacci 數列:** $Sn = Sn-1 + Sn-2$ 而**Lagged Fibonacci generator** 則是以 $Sn≡Sn−j⋆Sn−k(modm),0<j<k$ 來生成亂數,其中的 $Sn$、$Sn-j$、$Sn-k$,皆為 Fibonacci 數列中的值,而 operator$⋆$ 則可以是 addition ($+$), subtraction ($−$), multiplication ($×$) or XOR (^). 此做法可大大增加亂數週期,使使用者更難猜測其規律。 :::danger 用合法的 LaTeX 語法來表達上述符號和數學式 :notes: jserv ::: ### generator 實作 ```cpp int f[100], op, j, k, out; f[0] = 1; f[1] = 1; for(int i = 2; i < 100; i++){ f[i] = f[i - 1] + f[i - 2]; } size_t count = 20; srandom(time(0)); node_t *list = NULL; while (count--) { op = random() % 4; j = random() % 100; k = random() % 100; while(!(j < k)){ j = random() % 100; k = random() % 100; } switch(op){ case 0: out = f[99 - j] + f[99 -k]; break; case 1: out = f[99 - j] - f[99 -k]; break; case 2: out = f[99 - j] * f[99 -k]; break; case 3: out = f[99 - j]^f[99 -k]; break; } list = list_make_node_t(list, out % 1024); } ``` ### 2.參考 Optimized QuickSort — C Implementation (Non-Recursive) 並重寫上述 quick sort 程式碼,避免使用遞迴呼叫 ### nonrecursive quicksort 程式碼 ```c = void quicksort_nonrecursive(node_t **list, int size) { if (!*list) return; int beg[MAX_LEVELS], end[MAX_LEVELS], i=0, L, R, piv_pos; node_t *piv, *temp; beg[0] = 0; end[0] = size; while (i >= 0) { L = beg[i]; R = end[i]-1; if (L < R) { piv = find_ele(*list, L); if (i == MAX_LEVELS-1) return; while (L < R) { while (find_ele(*list, R)->value >= piv->value && L < R) { R--; } if (L < R) { move_node(list, R, L); L++; } while (find_ele(*list, L)->value <= piv->value && L < R) { L++; } if (L < R) { move_node(list, L, R); R--; } } if (find_ele(*list, L)->value > piv->value) { R--; L--; } move_node(list, find_ele_byVal(*list, piv->value), L); beg[i + 1] = L + 1; end[i + 1] = end[i]; end[i++] = L; } else { i--; } } } ``` 輔助用途函式的實作 ```cpp static inline node_t *find_ele(node_t *head, int pos){ node_t *ptr = head; for(int i = 0; i < pos; i++){ ptr = ptr->next; } return ptr; } static inline int find_ele_byVal(node_t *head, int target){ node_t *ptr = head; int pos = 0; while (ptr->value != target) { pos ++; ptr = ptr->next; } return pos; } static inline void move_node(node_t **list, int pos, int des){ if(pos == des){ return; } node_t *ptr = *list; for(int i = 0; i < pos; i++){ ptr = ptr->next; } node_t *temp = ptr; if (ptr == *list) { *list = (*list)->next; } else { ptr = *list; for(int i = 0; i < pos - 1; i++){ ptr = ptr->next; } ptr->next = temp->next; temp->next = NULL; } if (des == 0) { temp->next = *list; *list = temp; } else { ptr = *list; for(int i = 0; i < des - 1; i++){ ptr = ptr->next; } temp->next = ptr->next; ptr->next = temp; } } ``` :::danger 上述程式碼較為冗長,請思索更精簡的實作 :notes: jserv :::