# 2025q1 Homework2 (quiz1+2)
contributed by < `weiso131` >
{%hackmd NrmQUGbRQWemgwPfhzXj6g %}
# 第一週測驗 `1`
## 填空
- `AAAA` : `&l`
- `BBBB` : `before`
- `CCCC` : `&(*p)->next`
- `DDDD` : `item->next`
## 程式運作原理
### list_insert_before 運作原理
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled, fillcolor=lightgray];
head_ptr [label="{l | <head >head}"];
indirect [label="p", shape=note, fillcolor=lightyellow];
node1 [label="{ data | <next >next }"];
node2 [label="{ before | next }"];
node3 [label="{ data | next }"];
null [label="NULL", shape=plaintext];
node4 [label="{ item | next }"];
// head -> node1
head_ptr -> node1 [label="head"];
// node1 -> node2 -> node3 -> NULL
node1 -> node2 [label="next"];
node2 -> node3 [label="next"];
node3 -> null [label="next"];
// indirect 指向 head_ptr(間接指標)
indirect -> head_ptr: next [style=dashed, color=blue];
}
```
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled, fillcolor=lightgray];
head_ptr [label="{l | <head >head}"];
indirect [label="p", shape=note, fillcolor=lightyellow];
node1 [label="{ data | <next >next }"];
node2 [label="{ before | next }"];
node3 [label="{ data | next }"];
null [label="NULL", shape=plaintext];
node4 [label="{ item | next }"];
// head -> node1
head_ptr -> node1 [label="head"];
// node1 -> node2 -> node3 -> NULL
node1 -> node2 [label="next"];
node2 -> node3 [label="next"];
node3 -> null [label="next"];
// indirect 指向 head_ptr(間接指標)
indirect -> node1: next [style=dashed, color=blue];
}
```
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled, fillcolor=lightgray];
head_ptr [label="{l | <head >head}"];
indirect [label="p", shape=note, fillcolor=lightyellow];
node1 [label="{ data | <next >next }"];
node2 [label="{ before | next }"];
node3 [label="{ data | next }"];
null [label="NULL", shape=plaintext];
node4 [label="{ item | next }"];
// head -> node1
head_ptr -> node1 [label="head"];
// node1 -> node2 -> node3 -> NULL
node1 -> node4 [label="next"];
node2 -> node3 [label="next"];
node3 -> null [label="next"];
// indirect 指向 head_ptr(間接指標)
indirect -> node1: next [style=dashed, color=blue];
}
```
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled, fillcolor=lightgray];
head_ptr [label="{l | <head >head}"];
indirect [label="p", shape=note, fillcolor=lightyellow];
node1 [label="{ data | <next >next }"];
node2 [label="{ before | next }"];
node3 [label="{ data | next }"];
null [label="NULL", shape=plaintext];
node4 [label="{ item | next }"];
// head -> node1
head_ptr -> node1 [label="head"];
// node1 -> node2 -> node3 -> NULL
node1 -> node4 [label="next"];
node2 -> node3 [label="next"];
node3 -> null [label="next"];
node4 -> node2 [label="next"];
// indirect 指向 head_ptr(間接指標)
indirect -> node1: next [style=dashed, color=blue];
}
```
### merge_sort 實作
```c
list_item_t *merge(list_item_t *l, list_item_t *r, int descend)
{
list_item_t *head = NULL, **indir = &head;
while (1) {
if (l->value > r->value == descend) {
*indir = l;
indir = &l->next;
l = l->next;
if (!l) {
*indir = r;
break;
}
}
else {
*indir = r;
indir = &r->next;
r = r->next;
if (!r) {
*indir = l;
break;
}
}
}
return head;
}
list_item_t *merge_sort(list_item_t *head, int descend)
{
if (head->next == NULL)
return head;
list_item_t *l = head, *last_r = head, *r = head->next, *fast = head->next;
while (fast != NULL && fast->next != NULL) {
last_r = r;
r = r->next;
fast = fast->next->next;
}
last_r->next = NULL;
l = merge_sort(l, descend), r = merge_sort(r, descend);
return merge(l, r, descend);
}
```
# 第一週測驗 `2`
## 填空
- `EEEE`: `(*pred_ptr)->r`
- `FFFF`: `&(*pred_ptr)->r`
## `remove_free_tree` 解讀
### `l == r == NULL`
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
//pred_ptr [label="pred_ptr", shape=note, fillcolor=lightyellow];
// 節點
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> 2 | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n1 [label="{ <n1node> 1 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null1 [label="NULL", shape=plaintext];
null2 [label="NULL", shape=plaintext];
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n2:n2node;
n4:r -> n6:n6node;
n2:l -> n1:n1node;
n2:r -> n3:n3node;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n1:l -> null1;
n1:r -> null2;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> n2: l [style=dashed, color=blue];
//pred_ptr -> n2: n2node [style=dashed, color=blue];
}
```
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
//pred_ptr [label="pred_ptr", shape=note, fillcolor=lightyellow];
// 節點
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> 2 | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n1 [label="{ <n1node> 1 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null1 [label="NULL", shape=plaintext];
null2 [label="NULL", shape=plaintext];
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
null9 [label="NULL", shape=plaintext];
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n2:n2node;
n4:r -> n6:n6node;
n2:l -> null9;
n2:r -> n3:n3node;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n1:l -> null1;
n1:r -> null2;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> n2: l [style=dashed, color=blue];
//pred_ptr -> n2: n2node [style=dashed, color=blue];
}
```
### `l != NULL || r != NULL`
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
//pred_ptr [label="pred_ptr", shape=note, fillcolor=lightyellow];
// 節點
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> 2 | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
null9 [label="NULL", shape=plaintext];
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n2:n2node;
n4:r -> n6:n6node;
n2:l -> null9;
n2:r -> n3:n3node;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> n4: l [style=dashed, color=blue];
//pred_ptr -> n2: n2node [style=dashed, color=blue];
}
```
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
//pred_ptr [label="pred_ptr", shape=note, fillcolor=lightyellow];
// 節點
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> 2 | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
null9 [label="NULL", shape=plaintext];
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n3:n3node;
n4:r -> n6:n6node;
n2:l -> null9;
n2:r -> n3:n3node;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> n4: l [style=dashed, color=blue];
//pred_ptr -> n2: n2node [style=dashed, color=blue];
}
```
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
//pred_ptr [label="pred_ptr", shape=note, fillcolor=lightyellow];
// 節點
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> 2 | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
null9 [label="NULL", shape=plaintext];
null1 [label="NULL", shape=plaintext];
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n3:n3node;
n4:r -> n6:n6node;
n2:l -> null9;
n2:r -> null1;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> n4: l [style=dashed, color=blue];
//pred_ptr -> n2: n2node [style=dashed, color=blue];
}
```
### `l != NULL && r != NULL`
#### `*pred_ptr != (*node_ptr)->l`
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
pred_ptr [label="pred_ptr", shape=note, fillcolor=lightyellow];
// 節點
root [label="{root}"];
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> 2 | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n1 [label="{ <n1node> 1 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null1 [label="NULL", shape=plaintext];
null2 [label="NULL", shape=plaintext];
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
root -> n4;
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n2:n2node;
n4:r -> n6:n6node;
n2:l -> n1:n1node;
n2:r -> n3:n3node;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n1:l -> null1;
n1:r -> null2;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> root;
pred_ptr -> n2 : r [style=dashed, color=blue];
}
```
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
pred_ptr [label="pred_ptr", shape=note, fillcolor=lightyellow];
// 節點
root [label="{root}"];
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> 2 | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n1 [label="{ <n1node> 1 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null1 [label="NULL", shape=plaintext];
null2 [label="NULL", shape=plaintext];
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
null9 [label="NULL", shape=plaintext];
root -> n4;
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n2:n2node;
n4:r -> n6:n6node;
n2:l -> n1:n1node;
n2:r -> null9;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n1:l -> null1;
n1:r -> null2;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> root;
pred_ptr -> n2 : r [style=dashed, color=blue];
}
```
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
pred_ptr [label="pred_ptr", shape=note, fillcolor=lightyellow];
// 節點
root [label="{root}"];
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> 2 | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n1 [label="{ <n1node> 1 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null1 [label="NULL", shape=plaintext];
null2 [label="NULL", shape=plaintext];
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
null9 [label="NULL", shape=plaintext];
root -> n3;
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> null3;
n4:r -> null4;
n2:l -> n1:n1node;
n2:r -> null9;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n1:l -> null1;
n1:r -> null2;
n3:l -> n2;
n3:r -> n6;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> root;
pred_ptr -> n2 : r [style=dashed, color=blue];
}
```
#### `*pred_ptr == (*node_ptr)->l`
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
pred_ptr [label="pred_ptr", shape=note, fillcolor=lightyellow];
// 節點
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> 2 | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n1 [label="{ <n1node> 1 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null1 [label="NULL", shape=plaintext];
null2 [label="NULL", shape=plaintext];
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n2:n2node;
n4:r -> n6:n6node;
n2:l -> n1:n1node;
n2:r -> n3:n3node;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n1:l -> null1;
n1:r -> null2;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> n4 : l;
pred_ptr -> n2 : l [style=dashed, color=blue];
}
```
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
pred_ptr [label="pred_ptr", shape=note, fillcolor=lightyellow];
// 節點
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> 2 | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n1 [label="{ <n1node> 1 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null1 [label="NULL", shape=plaintext];
null2 [label="NULL", shape=plaintext];
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n2:n2node;
n4:r -> n6:n6node;
n2:l -> n1:n1node;
n2:r -> n3:n3node;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n1:l -> null1;
n1:r -> null2;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> n4:l;
pred_ptr -> n2 : l [style=dashed, color=blue];
}
```
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
pred_ptr [label="pred_ptr", shape=note, fillcolor=lightyellow];
// 節點
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> 2 | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n1 [label="{ <n1node> 1 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null1 [label="NULL", shape=plaintext];
null2 [label="NULL", shape=plaintext];
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
null9 [label="NULL", shape=plaintext];
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n1:n1node;
n4:r -> n6:n6node;
n2:l -> null9;
n2:r -> null2;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n1:l -> null1;
n1:r -> n3;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> n4:l;
pred_ptr -> n2 : l [style=dashed, color=blue];
}
```
## 補完程式碼
### find_free_tree
```c
block_t **find_free_tree(block_t **root, block_t *target)
{
block_t **node_ptr = root;
while (*node_ptr != target && *node_ptr != NULL) {
if (target->size > (*node_ptr)->size)
node_ptr = &(*node_ptr)->r;
else
node_ptr = &(*node_ptr)->l;
}
return node_ptr;
}
```
#### 解說
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="node_ptr", shape=note, fillcolor=lightyellow];
// 節點
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> target | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n1 [label="{ <n1node> 1 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null1 [label="NULL", shape=plaintext];
null2 [label="NULL", shape=plaintext];
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
root -> n4;
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n2:n2node;
n4:r -> n6:n6node;
n2:l -> n1:n1node;
n2:r -> n3:n3node;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n1:l -> null1;
n1:r -> null2;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> root;
}
```
```graphviz
digraph BST {
rankdir=TB;
node [shape=record, style=filled, fillcolor=lightgray];
indirect [label="return: node_ptr", shape=note, fillcolor=lightyellow];
// 節點
n4 [label="{ <n4node> 4 | {<l> l | <r> r }}"];
n2 [label="{ <n2node> target | {<l> l | <r> r }}"];
n6 [label="{ <n6node> 6 | {<l> l | <r> r }}"];
n1 [label="{ <n1node> 1 | {<l> l | <r> r }}"];
n3 [label="{ <n3node> 3 | {<l> l | <r> r }}"];
n5 [label="{ <n5node> 5 | {<l> l | <r> r }}"];
n7 [label="{ <n7node> 7 | {<l> l | <r> r }}"];
// NULL 節點
null1 [label="NULL", shape=plaintext];
null2 [label="NULL", shape=plaintext];
null3 [label="NULL", shape=plaintext];
null4 [label="NULL", shape=plaintext];
null5 [label="NULL", shape=plaintext];
null6 [label="NULL", shape=plaintext];
null7 [label="NULL", shape=plaintext];
null8 [label="NULL", shape=plaintext];
root -> n4;
// 建立連線(從 l、r port 出發,接到子節點的 <xxxnode> port)
n4:l -> n2:n2node;
n4:r -> n6:n6node;
n2:l -> n1:n1node;
n2:r -> n3:n3node;
n6:l -> n5:n5node;
n6:r -> n7:n7node;
n1:l -> null1;
n1:r -> null2;
n3:l -> null3;
n3:r -> null4;
n5:l -> null5;
n5:r -> null6;
n7:l -> null7;
n7:r -> null8;
indirect -> n4:l;
}
```
### insert_free_tree
```c
void insert_free_tree(block_t **root, block_t *target)
{
block_t **node_ptr = find_free_tree(root, target);
*node_ptr = target;
}
```
### init_block
```c
block_t *init_block(size_t size)
{
block_t *block = malloc(sizeof(block_t));
block->size = size;
block->l = block->r = NULL;
return block;
}
```
### 測試程式
```c
void block_test()
{
for (int i = 0;i < N;i++) {
size_t size = rand() % 8192;
blocks[i] = init_block(size);
insert_free_tree(&root, blocks[i]);
}
for (int i = 0;i < N;i++) {
remove_free_tree(&root, blocks[i]);
block_t **find = find_free_tree(&root, blocks[i]);
assert(*find == NULL);
free(blocks[i]);
blocks[i] = NULL;
}
printf("All test complete!!!\n");
}
```
#### 解說
## 自訂記憶體配置器
[github](https://github.com/weiso131/Customize_allocator)
參考 [memory-allocators](https://github.com/mtrebi/memory-allocators) 的 free list allocator。
這種記憶體分配器屬於通用型分配器,利用一個資料結構(通常是鏈結串列或紅黑樹)紀錄可供使用的記憶體區塊,在使用者請求分配時,在這個資料結構尋找合適的區塊提供給使用者。
另外,若在釋放記憶體時,發現有相鄰的記憶體區塊是可用的,這種實作方法會將其合併。
要以時間複雜度 $O(1)$ 確認是否能合併,可以使用雙向鏈結串列維護其順序,以方便查詢。
文中提到可以用鍊結串列或是紅黑樹維護,我嘗試使用二元搜尋樹代替紅黑樹,實作這種記憶體分配器。在不發生樹嚴重傾斜的情況下,查詢的時間複雜度與紅黑樹相同,皆是 $O(log(n))$。 而在測驗題中也有提供相關實作,可直接使用。
至於雙向鏈結串列,在實作過程中發現,若是環狀的鏈結串列,在插入節點時就沒有判斷下一項是否為空指標的問題,因此我使用 list.h 的資料結構與 API 來維護這個雙向鏈結串列。
### 功能簡述
這些是這個分配器提供給使用者的主要功能
定義在 [includes/free_list_alloc.h](https://github.com/weiso131/Customize_allocator/blob/main/includes/free_list_alloc.h)
- `void init_fl(size_t size);`
根據使用者提供的size,設定這個記憶體分配器的總分配空間大小上限
- `void *fl_alloc(size_t size)`
分配使用者需要的記憶體空間,分配成功則回傳該記憶體的位址
分配失敗或是 size 為 0 則回傳 NULL
- `void fl_free(void *ptr)`
將使用者傳入的指標對應的空間釋放
若傳入空指標則不做任何事情
若傳入已經釋放過,或是不屬於 init_fl 初始化的記憶體,則屬於 undefine behavior
與 C 語言原生的 free 類似(參考: C standard (ISO/IEC 9899:2011 §7.20.3.2))
### `init_fl`
#### 步驟
1. 利用 init_block 建立一個初始 block (root_block)。
2. 把這個 block 設定為 tree_root(BST 根節點)。
3. 建立一個 root_head,為雙向鏈結串列的頭節點。
4. 將 root_block 加入雙向鏈結串列(使用 list_add)。
#### 說明
1. 這個新的 block 是一個全新可用的區塊,所以直接加入二元搜尋樹
2. 先使用 root_block 儲存這個分配器全部的可用區塊,再將 tree_root 設定成 root_block ,可以在最後方便使用者一次釋放所有空間,不會因為二元搜尋樹根節點的改變而無法一次釋放
### `fl_alloc`
#### 步驟
1. 使用 find_block_by_size() 找一個合適的 block
2. 從 BST 移除該 block , 代表這個 block 不再是未使用空間
3. 分割多餘空間:
- 若剩餘空間足夠放一個 block 結構和額外資料 ➜ 建立新 block (unuse)。
- 重新設定兩個區塊所持有的空間
- 將 unuse 插入鏈結串列(在原本的 block 後方),並加入二元搜尋樹。
4. 標記分配的 block 為已使用
#### 說明
1. 若剩餘空間小於等於 block 結構的大小,則無法正常成為一個可使用的 block,此時儘管超出使用者要求的記憶體一些,仍會一次將這些記憶體都提供給使用者
#### `find_block_by_size`
- 找到最小大於等於使用者要求大小的區塊
- 在迴圈中,若最後找不到相同大小的區塊時, find 會變成 NULL
- 為了確保找到的區塊絕對大於使用者要求大小,利用 last_bigger 儲存最後一個往左,也就是最後一個發現比目標大的區塊的指標
>[dded92d](https://github.com/weiso131/Customize_allocator/commit/dded92dd1f87b868ff0ab5a5bffe884028fa630b)
- find 為 NULL 的時候會將其設為 last_bigger , 若 size 大於所有可用區塊, 就會因為尋找過程中一直向右, last_bigger 仍為 NULL , 符合需求
### `fl_free`
#### 步驟
1. 檢查時否未使用
2. 標記 block 為未使用
3. 插入二元搜尋樹
4. 呼叫 try_merge() 嘗試合併周圍未使用區塊
#### `try_merge`
1. 檢查 block 的 list.next 和 list.prev 是否是未使用。
2. 若是 ➜ 呼叫 merge_b_to_a()
#### `merge_b_to_a`
1. 將 b 從 BST 和 linked list 移除。
2. 將 a 從 BST 移除。
3. 計算合併後新大小 ➜ a->size = a->size + b->size + sizeof(block_t)。
4. 將合併後的 a 插回 BST
##### 說明
- 因為合併大小時,可能會改變二元搜尋樹的性質,所以要先將 a 從樹上移除
>[5c0da11](https://github.com/weiso131/Customize_allocator/commit/5c0da119bb05f29e360428c54bc30cfb6b5f7b8e)
### 效能測試
[測試程式](https://github.com/weiso131/Customize_allocator/blob/main/time_test.c)

- 圖為隨機執行 alloc / free 1000000 次所花費的時間累積圖
- 如圖,目前的分配器效能明顯比 malloc 差
使用 perf 尋找效能瓶頸

- 可以發現 find_free_tree 佔用了非常大量的時間
- 查看組合語言可以看到 cmp 前最後一個 dereference 非常花時間,這可能對應到原始碼的 `(*node_ptr)->size`
```
0.07 │25: mov -0x20(%rbp),%rax ▒
0.71 │ mov (%rax),%rax ▒
0.13 │ mov -0x8(%rbp),%rdx ▒
53.47 │ mov (%rdx),%rdx
```
- 似乎與太多的 cache miss 發生有關

### 嘗試減少 `find_free_tree` 使用
- 參考 [`rota1001` 開發紀錄](https://hackmd.io/@rota1001/linux2025-homework2#%E6%B8%AC%E9%A9%97-2)

效能或許有些微提昇,使用 t 檢定驗證
```python=
def calculate_cohens_d(x1, x2):
# Pooled standard deviation
nx = len(x1)
ny = len(x2)
pooled_std = np.sqrt(((nx - 1)*np.var(x1, ddof=1) + (ny - 1)*np.var(x2, ddof=1)) / (nx + ny - 2))
d = (np.mean(x1) - np.mean(x2)) / pooled_std
return d
def compare_groups(free_list, new_free_list):
# Welch's t-test(自動考慮變異數不等)
t_value, p_value = ttest_ind(free_list, new_free_list, equal_var=False)
# 自由度由 ttest_ind 自動處理(Welch 方法)
cohen_d = calculate_cohens_d(free_list, new_free_list)
print(f"✅ Welch's t-test 結果:")
print(f"t 值:{t_value:.4f}")
print(f"p 值:{p_value:.4f} {'(顯著)' if p_value < 0.05 else '(不顯著)'}")
print(f"Cohen's d 效果量:{cohen_d:.4f}")
print(f"效能提升倍率:{free_list.sum() / new_free_list.sum():.4f}")
compare_groups(free_list, new_free_list)
```
輸出:
```
✅ Welch's t-test 結果:
t 值:54.8427
p 值:0.0000 (顯著)
Cohen's d 效果量:0.0776
效能提升倍率:1.0997
```
>[8e833ec](https://github.com/weiso131/Customize_allocator/commit/8e833ec4e9bdd3247775fe1f5c0945c2887144a5)
# 第一週測驗 `3`
## 填空
- `GGGG`: `head->prev=prev`
- `HHHH`: `list_entry(pivot,node_t,list)->value`
- `IIII`: `list_entry(n,node_t,list)->value`
- `JJJJ`: `pivot`
- `KKKK`: `right`
## `rebuild_list_link` 解說
node 不斷往 next
prev 緊跟在 node 後面
當 node 為 NULL 時,代表鍊結串列的最後一個節點找到了
此時 prev 就是最後一個節點
## `quick_sort` 解說
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled];
list [label="list | {<prev>perv | <next>next } "];
begin [label="begin: | {4}"];
node1 [label="{{4 | {<head> list_head | <next >next}}}"];
node2 [label="{{8 | {<head> list_head | <next >next}}}"];
node3 [label="{{7 | {<head> list_head | <next >next}}}"];
node4 [label="{{6 | {<head> list_head | <next >next}}}"];
node5 [label="{{3 | {<head> list_head | <next >next}}}"];
node1: next -> node2: head;
node2:next -> node3:head;
node3:next -> node4:head;
node4:next -> node5:head;
node5:next -> list: prev;
list:next -> node1:head;
}
```
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled];
pivot [label="pivot", style=filled, fillcolor=lightyellow]
p [label="p", style=filled, fillcolor=lightyellow]
right [label="right", style=filled, fillcolor=lightyellow]
left [label="left", style=filled, fillcolor=lightyellow]
null1 [label="NULL"]
node1 [label="{{4 | {<head> list_head | <next >next}}}"];
node2 [label="{{8 | {<head> list_head | <next >next}}}"];
node3 [label="{{7 | {<head> list_head | <next >next}}}"];
node4 [label="{{6 | {<head> list_head | <next >next}}}"];
node5 [label="{{3 | {<head> list_head | <next >next}}}"];
node1: next -> node2: head;
node2:next -> node3:head;
node3:next -> node4:head;
node4:next -> node5:head;
node5:next -> null1:head;
pivot -> node1: head;
p -> node2: head;
}
```
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled];
pivot [label="pivot", style=filled, fillcolor=lightyellow]
p [label="p", style=filled, fillcolor=lightyellow]
right [label="right", style=filled, fillcolor=lightyellow]
left [label="left", style=filled, fillcolor=lightyellow]
null1 [label="NULL"]
null2 [label="NULL"]
node1 [label="{{4 | {<head> list_head | <next >next}}}"];
node2 [label="{{8 | {<head> list_head | <next >next}}}"];
node3 [label="{{7 | {<head> list_head | <next >next}}}"];
node4 [label="{{6 | {<head> list_head | <next >next}}}"];
node5 [label="{{3 | {<head> list_head | <next >next}}}"];
node1: next -> null2;
node2:next -> node3:head;
node3:next -> node4:head;
node4:next -> node5:head;
node5:next -> null1:head;
pivot -> node1: head;
p -> node3: head;
right -> node2;
}
```
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled];
pivot [label="pivot", style=filled, fillcolor=lightyellow]
p [label="p", style=filled, fillcolor=lightyellow]
right [label="right", style=filled, fillcolor=lightyellow]
left [label="left", style=filled, fillcolor=lightyellow]
null1 [label="NULL"]
null2 [label="NULL"]
node1 [label="{{4 | {<head> list_head | <next >next}}}"];
node2 [label="{{8 | {<head> list_head | <next >next}}}"];
node3 [label="{{7 | {<head> list_head | <next >next}}}"];
node4 [label="{{6 | {<head> list_head | <next >next}}}"];
node5 [label="{{3 | {<head> list_head | <next >next}}}"];
node1: next -> null2;
node2:next -> node3:head;
node3:next -> node4:head;
node4:next -> node5:head;
node5:next -> null1:head;
pivot -> node1: head;
p -> null1;
right -> node2;
left -> node5;
}
```
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled];
pivot [label="pivot", style=filled, fillcolor=lightyellow]
right [label="right", style=filled, fillcolor=lightyellow]
left [label="left", style=filled, fillcolor=lightyellow]
null1 [label="NULL"]
null2 [label="NULL"]
null3 [label="NULL"]
node1 [label="{{4 | {<head> list_head | <next >next}}}"];
node2 [label="{{8 | {<head> list_head | <next >next}}}"];
node3 [label="{{7 | {<head> list_head | <next >next}}}"];
node4 [label="{{6 | {<head> list_head | <next >next}}}"];
node5 [label="{{3 | {<head> list_head | <next >next}}}"];
node1: next -> null2;
node2:next -> node3:head;
node3:next -> node4:head;
node4:next -> null3;
node5:next -> null1;
pivot -> node1: head;
right -> node2;
left -> node5;
begin [label="begin: | 3 | 4| 8"];
}
```
稍微跳過幾個步驟
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled];
result [label="result", style=filled, fillcolor=lightyellow]
right [label="right", style=filled, fillcolor=lightyellow]
left [label="left", style=filled, fillcolor=lightyellow]
null1 [label="NULL"]
null2 [label="NULL"]
null3 [label="NULL"]
null4 [label="NULL"]
null5 [label="NULL"]
node1 [label="{{4 | {<head> list_head | <next >next}}}"];
node2 [label="{{8 | {<head> list_head | <next >next}}}"];
node3 [label="{{7 | {<head> list_head | <next >next}}}"];
node4 [label="{{6 | {<head> list_head | <next >next}}}"];
node5 [label="{{3 | {<head> list_head | <next >next}}}"];
node1: next -> null2;
node2:next -> null4;
node3:next -> null5;
node4:next -> null3;
node5:next -> null1;
right -> node3: head;
left -> node3: head;
begin [label="begin: | 3 | 4| 6 | 7"];
result -> node2: head;
}
```
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled];
result [label="result", style=filled, fillcolor=lightyellow]
right [label="right", style=filled, fillcolor=lightyellow]
left [label="left", style=filled, fillcolor=lightyellow]
null4 [label="NULL"]
node1 [label="{{4 | {<head> list_head | <next >next}}}"];
node2 [label="{{8 | {<head> list_head | <next >next}}}"];
node3 [label="{{7 | {<head> list_head | <next >next}}}"];
node4 [label="{{6 | {<head> list_head | <next >next}}}"];
node5 [label="{{3 | {<head> list_head | <next >next}}}"];
node1: next -> node4: head;
node2:next -> null4;
node3:next -> node2: head;
node4:next -> node3: head;
node5:next -> node1: head;
right -> node5: head;
left -> node5: head;
begin [label="begin: | 3"];
result -> node5: head;
}
```
```graphviz
digraph LinkedList {
rankdir=LR;
node [shape=record, style=filled];
list [label="list | {<prev>perv | <next>next } "];
result [label="result", style=filled, fillcolor=lightyellow]
node1 [label="{{4 | {<head> list_head | <next >next}}}"];
node2 [label="{{8 | {<head> list_head | <next >next}}}"];
node3 [label="{{7 | {<head> list_head | <next >next}}}"];
node4 [label="{{6 | {<head> list_head | <next >next}}}"];
node5 [label="{{3 | {<head> list_head | <next >next}}}"];
node1: next -> node4: head;
node3:next -> node2: head;
node4:next -> node3: head;
node5:next -> node1: head;
result -> node5: head;
list: next -> node5: head;
list: prev -> node2: head;
node2: next -> list: head;
}
```
# 第二週 測驗 1
## 填空
- `AAAA`: `list_first_entry`
- `BBBB`: `list_del`
- `CCCC`: `list_move_tail`
- `DDDD`: `list_add`
- `EEEE`: `list_splice`
- `FFFF`: `list_splice_tail`
## `list_quicksort` 解釋
1. 將 list 的第一項作為 pivot , 並移出環狀鍊結串列
2. 比 pivot 小的節點加入 list_less , 比 pivot 大的節點加入 list_greater , 加入時使用 list_move_tail 維持環狀鍊結串列的特性, 同時滿足 stable sorting
3. list_less 與 list_greater 各自進行 list_quicksort
4. 將 pivot, list_less, list_greater 合併
## 修正 `random_shuffle_array`
### 測試程式
使用 4 個元素的陣列
執行 shuffle 1e6 次
紀錄每種排列的出現次數
再計算卡方值
```c
static int index_form[4][4][4];
#define SHUFFLE_TIMES 1000000
#define FACTORIAL_4 24
void build_lookup_table()
{
int index = 0;
uint16_t perm[4] = {1, 2, 3, 4};
// Generate all 4! permutations using stdlib's next_permutation
for (int a = 1; a <= 4; ++a)
for (int b = 1; b <= 4; ++b)
if (b != a)
for (int c = 1; c <= 4; ++c)
if (c != a && c != b)
for (int d = 1; d <= 4; ++d)
if (d != a && d != b && d != c)
index_form[a - 1][b - 1][c - 1] = index++;
}
int main()
{
uint16_t array[4] = {0, 1, 2, 3};
uint32_t count[FACTORIAL_4] = {0};
build_lookup_table();
for (int i = 0; i < SHUFFLE_TIMES; ++i) {
random_shuffle_array(array, 4);
int idx = index_form[(int)array[0]][(int)array[1]][(int)array[2]];
count[idx]++;
}
double expected = (double)SHUFFLE_TIMES / FACTORIAL_4;
double chi_squared = 0.0;
for (int i = 0; i < FACTORIAL_4; ++i) {
double diff = count[i] - expected;
chi_squared += (diff * diff) / expected;
}
printf("Chi-squared value: %.4f\n", chi_squared);
return 0;
}
```
### 原本的 `random_shuffle_array`
輸出:
```
Chi-squared value: 122688.7850
```
卡方值遠高於 $\chi^2_{23,0.05}=35.1725$
### 使用 `fisher_yates_shuffle`
參考 lab0 提供的 shuffle 演算法
```c
static inline void fisher_yates_shuffle(uint16_t *operations, uint16_t len)
{
for (uint16_t i = 0;i < len;i++) {
uint16_t new = len - i - 1;
uint16_t old = get_unsigned16() % (new + 1);
uint16_t save = operations[new];
operations[new] = operations[old];
operations[old] = save;
}
}
```
輸出:
```
Chi-squared value: 21748.5402
```
雖然有減少,但其卡方值仍遠高於 $\chi^2_{23,0.05}=35.1725$
### 使用 `rand` 代替 `get_unsigned16`
```diff
uint16_t new = len - i - 1;
- uint16_t old = get_unsigned16() % (new + 1);
+ uint16_t old = rand() % (new + 1);
uint16_t save = operations[new];
```
輸出:
```
Chi-squared value: 10.5795
```
卡方值小於 $\chi^2_{23,0.05}=35.1725$
無證據說明這個 shuffle 是不公平的
## `list_move_tail` 換為 `list_move` 無法滿足 stable sorting
# 第二週 測驗 2
## 填空
- `GGGG`: `14`
- `HHHH`: `2`
- `IIII`: `0`
- `JJJJ`: `3`
- `KKKK`: `2`
- `LLLL`: `1`
- `MMMM`: `~1`
- `NNNN`: `1`
- `PPPP`: `2`
## `clz32`
### 解說
1. 先將一個數字藉由 bit mask 切分成 `upper` 與 `lower`
2. 檢查 `upper` 是否為 0 , 若不是 0 ,代表 leading zero 在 `upper` 裡面,對 `upper` 執行 clz32。反之, leading zero 在 lower , 此時加上 `(16 >> c)` 正是前方的位元數量。
3. 重複執行直到 `c == 3` , 此時 `upper` 與 `lower` 都只剩兩個 bits 的長度,若 `upper` 不為 0 , 將 `upper` 帶入 `magic` 可做最後的計算。反之, `+2` 然後對 `lower` 最類似的事情。
#### mask 補充
最後只剩 2 bits , 代表會有四種可能
- 00: 前方累計位元數 +2 即是 leading zero 的位置
- 01: 前方累計位元數 +1 即是 leading zero 的位置
- 10: 前方累計位元數即是 leading zero 的位置
- 11: 前方累計位元數即是 leading zero 的位置
## `sqrti`
### 解說
題目中的 `sqrti` 其實就是 [Digit-by-digit Method](https://hackmd.io/@sysprog/sqrt#Digit-by-digit-Method) 的實作,但直接從原本的函式實作解釋它有些困難,我將會提供從最直觀的實作,一步一步優化到現在狀態的過程。
#### 最直觀實作
```c
int shift = (total_bits - 1 - clz64(x)) & ~1;
while (shift >= 0) {
m = 1ULL << shift;
uint64_t m_2 = 1ULL << (shift >> 1);
uint64_t b = ((y * m_2) << 1) + m;
if (x >= b) {
x -= b;
y += m_2;
}
shift -= 2;
}
```
這個實作方法基本上是照著演算法的描述一步一步實現,可以看到他會存在一個乘法運算
#### 移除乘法運算
```c
int shift = (total_bits - 1 - clz64(x)) & ~1;
uint64_t last = 0;
while (shift >= 0) {
m = 1ULL << shift;
uint64_t m_2 = 1ULL << (shift >> 1);
uint64_t b = last + m;
last >>= 1;
if (x >= b) {
x -= b;
y += m_2;
last += m;
}
shift -= 2;
}
```
這個方法由一個 `last` 儲存每一次 `y` 更新時的 `m`
並在進入判斷式前右移一格
這個調整仍是有效的原因,是因為所有 `m` 都是 2 的次方
以此改寫公式
$$
(2^{i+k}+...+2^i+2^{i-j})^2=(2^i)^2+(2(2^{i+k}+...+2^i)+2^{i-j})\cdot 2^{i-j}
$$
再展開
$$
(2^i)^2+(2^{i-j})^2+2^{2i+k-j+1}+...+2^{2i-j+1}
$$
觀察到 $2^{2i-j+1}$ 可以發現只要對 $2^{2i}$ 右移 $j-1$ ,就能得到現在做計算所需的值
這正是 `last` 在做的事情
#### 合併 `last` 與 `y`
```c
int shift = (total_bits - 1 - clz64(x)) & ~1;
while (shift >= 0) {
m = 1ULL << shift;
uint64_t b = y + m;
y >>= 1;
if (x >= b) {
x -= b;
y += m;
}
shift -= 2;
}
```
在 `shift` 為 $2i$ 加上一個 `m`: $2^{2i}$
由於到迴圈結束時會被右移 $i$ 格,對最後的 `last` 而言相當於每次都加上 $2^i$
這與 `y` 的功能完全相同
到了這步基本上已經跟原本的 `sqrti` 相同,只差把每次重新計算的 `m` 放在迴圈外, 每次 `m` 往右移兩格的調整
### 向上取整
`x` 在計算過程會被減去 `y` 的平方
若最後 `x` 不是 0 ,就代表 `x` 原本不是完全平方數,需要向上取整,也就是額外 `+1`
因此,可以對最後的 `return` 做修改
```c
return y + (x && 1);
```
即可在只使用加減法與位元操作的情況下,完成向上取整的平方根
# 第二週 測驗 3
## 填空
- `AAAA`: `map->bits`
- `BBBB`: `map->bits`
- `CCCC`: `first->pprev`
- `DDDD`: `n->pprev`
- `EEEE`: `n->pprev`
## 程式碼解釋