Chongzhi314
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # 2025-05-14 會議問答記錄 ## Q1: [kxo](https://hackmd.io/@sysprog/linux2025-kxo/%2F%40sysprog%2Flinux2025-kxo-a) -> 為何 Linux 核心不傾向使用 FPU? 參考 [Use of floating point in the Linux kernel](https://stackoverflow.com/questions/13886338/use-of-floating-point-in-the-linux-kernel): > The real reason is that the kernel doesn't particularly *need* FPU ops and also needs to run on architectures without an FPU at all. Therefore, it simply avoids the complexity and runtime required to manage its own FPU context by not doing ops for which there are always other software solutions. > It's interesting to note how often the FPU state would have to be saved if the kernel wanted to use FP...every system call, every interrupt, every switch between kernel threads. Even if there was a need for occasional kernel FP, it would probably be faster to do it in software. 因為 FPU 暫存器較大(如:Intel x86 的 YMM 暫存器一個就有 256 bits 寬 [[Source](https://www.cs.uaf.edu/2012/fall/cs301/lecture/11_02_other_float.html)]),且作業系統又需經常進行 Context Switch (如:system call, interrupt, switch between kernel threads),因此若在 kernel 中使用 FPU 指令,每次都要反覆的「**手動**^[1]^」store 和 restore FPU 暫存器狀態,這會需要更多的 CPU cycle,導致系統效能下降,何況大多數核心程式碼幾乎不會使用浮點數指令,而且 Linux kernel 還得能在缺少 FPU 的硬體(如:較早期的嵌入式處理器)上運行。所以若為這些不常使用 FPU 的行程頻繁 store 和 restore FPU 狀態是非常浪費系統資源的。因此kernel 的浮點數運算改由軟體方法 (如:fixed point) 來取代,雖然軟體方法在浮點數運算的效率沒有直接用 FPU 來的好,但 kernel 少用,所以相比於支援 FPU 的 kernel 來說反而是更快。再者,由於前述的原因,為了減少不必要的 FPU 狀態保存,FPU 支援 lazy state switching 機制,借助此機制可以讓 kernel 在支援 FPU 指令的同時,當發生 Context Switch 時,以 Intel®64 為例,kernel 需將 CR0.TS bit 設為 1 ^[2]^,表示 FPU 暫存器的內容尚未保存,若接手的行程未執行到 FPU 指令,則 FPU 暫存器的內容仍維持上一個行程的 context 的狀態,反之,一但後來的行程一執行到 FPU 指令時,就會因為 CR0.TS bit 為 1 而觸發 trap(Device not Available exception 即 #NM)進入 kernel mode^[實驗]^,此時 #NM exception handler 就會去保存上一個行程的 context 狀態。不過在現今[動態執行](https://en.wikipedia.org/wiki/Out-of-order_execution)^[3]^的 CPU 中,可能沒辦法馬上偵測到 lazy restore mechanisms 中設定的 "FPU not available",導致在當前行程中,仍可以存取到上一個行程的 FPU 暫存器內容,進而達到攻擊的目的(FPU 暫存器可能是用來暫存加密相關資料)即 [CVE-2018-3665](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-3665) 漏洞。基於上述的種種因素,Linux 不傾向使用 FPU 指令。 ### 實驗: Linux kernel module 參考內容: > 教材: > - [LKMPG](https://sysprog21.github.io/lkmpg/) Chapter 1 ~ 4 > - [Linux 核心模組運作原理](https://hackmd.io/@sysprog/linux-kernel-module) 撰寫 Kernel module 查看在 userspace 執行 fpu 指令後的 CR0.TS bit 狀態: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/smp.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jimmy"); MODULE_DESCRIPTION("Check CR0 TS bit on all CPUs"); static void show_cr0_ts(void *info) { unsigned long cr0; asm volatile("mov %%cr0, %0" : "=r"(cr0)); pr_info("CPU %d: CR0 = 0x%lx, TS = %lu\n", smp_processor_id(), cr0, (cr0 >> 3) & 1); } static int __init ts_check_init(void) { pr_info("Checking CR0 TS bit on all CPUs...\n"); on_each_cpu(show_cr0_ts, NULL, 1); return 0; } static void __exit ts_check_exit(void) { pr_info("CR0 TS bit check module unloaded.\n"); } module_init(ts_check_init); module_exit(ts_check_exit); ``` 輸出結果: ```bash $ sudo dmesg | grep CPU [325258.902427] Checking CR0 TS bit on all CPUs... [325258.902435] CPU 5: CR0 = 0x80050033, TS = 0 [325258.902436] CPU 1: CR0 = 0x80050033, TS = 0 [325258.902436] CPU 2: CR0 = 0x80050033, TS = 0 [325258.902437] CPU 7: CR0 = 0x80050033, TS = 0 [325258.902437] CPU 6: CR0 = 0x80050033, TS = 0 [325258.902436] CPU 0: CR0 = 0x80050033, TS = 0 [325258.902437] CPU 4: CR0 = 0x80050033, TS = 0 [325258.902436] CPU 3: CR0 = 0x80050033, TS = 0 ``` :::info 經在 Ubuntu 24.04 實驗後,CR0.TS bit 皆為 0。我還不清楚是為什麼? ::: [1]: 根據 Robert Love 在 [Linux Kernel Development](https://www.amazon.com/Linux-Kernel-Development-Robert-Love/dp/0672329468/) 一書論及浮點運算:"Using a floating point inside the kernel requires manually saving and restoring the floating point registers." **以 kernel 的視角來看**,因為 kernel process 不能像 user process 那樣透過 trap 中斷來請自己(kernel)保存當前行程的狀態,因此若 kernel 需要用到 FPU 指令時,就必須自己(kernel)**手動**保存 kernel process 的 FPU 暫存器狀態。 \ [2]: 參考 [Intel®64 and IA-32 Architectures Software Developer’s Manuals Vol.3](https://cdrdv2.intel.com/v1/dl/getContent/671447) 15.4.1 (Vol. 3A 15-7): > The TS flag in control register CR0 is provided to allow the operating system to delay saving/restoring the x87 FPU and SSE state until an instruction that actually accesses this state is encountered in a new task. When the TS flag is set, the processor monitors the instruction stream for x87 FPU, MMX, SSE instructions. When the processor detects one of these instructions, it raises a device-not-available exception (#NM) prior to executing the instruction. The #NM exception handler can then be used to save the x87 FPU and SSE state for the previous task (using an FXSAVE, XSAVE, or XSAVEOPT instruction) and load the x87 FPU and SSE state for the current task (using an FXRSTOR or XRSOTR instruction). If the task never encounters an x87 FPU, MMX, or SSE instruction, the device-not-available exception will not be raised and a task state will not be saved/restored unnecessarily. \ [3]: 讓處理器能同時把多條指令丟進管線,做得了的就先做,以提高Througput,最後再按原本程式順序輸出結果,好像一切仍依序執行一樣。 :::info 待釐清: - 為什麼要將 FPU 和 SIMD 的 register 一起使用呢?是因為他們的暫存器都需要設計的很大嗎? - 手動的理解有沒有錯? ::: ### Q2: Assume x is a single-precision floating point (IEEE 754) and always >= 1, calculate square root. No FPU is allowed. - 利用 `牛頓求根法` - 說明: 先選擇一個接近函數 $\displaystyle f(x)$ 零點的點 $x_0$。找出穿過點 $(x_0, \displaystyle f(x_0))$ 且斜率為 $\displaystyle f'(x_0)$ 之切線與 $x$ 軸的焦點 $x_1$,透過點斜式得該切線方程式為 $y - \displaystyle f(x_0) = \displaystyle f'(x_0)(x - x_0)$,將點 $(x_1, 0)$ 帶入得 $0 - \displaystyle f(x_0) = \displaystyle f'(x_0)(x_1 - x_0)$,經過移項得:\begin{split} - \cfrac{\displaystyle f(x_0)}{\displaystyle f'(x_0)} = x_1 - x_0 \ \ \ \Rightarrow \ \ \ x_1 = x_0 - \cfrac{\displaystyle f(x_0)}{\displaystyle f'(x_0)}\end{split}依循以上的方法得疊代公式:$x_{n+1} = x_n - \cfrac{\displaystyle f(x_n)}{\displaystyle f'(x_n)}$ 即可逐步求得近似 $f(x)$ 零點的數。 - 求平方根: 因為要求 $N$ 的平方根,故令 $\displaystyle f(x) = x^2 - N$,則 $f(x)$ 的零點即為 $N$ 的平方根。帶入疊代公式 $x_{n+1} = x_n - \cfrac{\displaystyle f(x_n)}{\displaystyle f'(x_n)}$ 經化簡後得:\begin{split}x_{n+1} = x_n - \cfrac{({x_n}^2 - N)}{2x_n} = \cfrac{1}{2}(2x_n - \cfrac{{x_n}^2}{x_n} + \cfrac{N}{x_n}) = \cfrac{1}{2}(x_n + \cfrac{N}{x_n})\end{split} ```c float my_sqrtf(float x) { if (x == 0.0f) return 0.0f; float ans = x; float tolerance = 1e-5f; // 取到小數點後第五位 while ((ans * ans - x) > tolerance || (x - ans * ans) > tolerance) ans = 0.5f * (ans + x / ans); return ans; } ``` :::info 當輸入的值為 73 時,會發生無窮迴圈,只能透過增加誤差解決,但看起來是不能用這種方法😢。 > 參考:[math: add faster soft float sqrtf implementation, add rsqrtf #1013](https://github.com/picolibc/picolibc/pull/1013/files) ::: ### Q3: 為何有 accpet 系統呼叫?為何不能直接 read socket fd? 內容部分參考: > Linux manual page: [accept](https://man7.org/linux/man-pages/man2/accept.2.html), [connect](https://man7.org/linux/man-pages/man2/connect.2.html), [listen](https://man7.org/linux/man-pages/man2/listen.2.html), [read](https://man7.org/linux/man-pages/man2/read.2.html) > > CS:APP Chapter 11 重點參考:11.4 > > Stack overflow: > - [What things are exactly happening when server socket accept client sockets?](https://stackoverflow.com/questions/29331938/what-things-are-exactly-happening-when-server-socket-accept-client-sockets) > - [How does the socket API accept() function work?](https://stackoverflow.com/questions/489036/how-does-the-socket-api-accept-function-work) > - [Socket: How does connect/accept use the 3-way handshake, if they ever do?](https://stackoverflow.com/questions/62468593/socket-how-does-connect-accept-use-the-3-way-handshake-if-they-ever-do) > - [Does Accept event happen after the three way handshake?](https://stackoverflow.com/questions/18451462/does-accept-event-happen-after-the-three-way-handshake) > - [About listen(), accept() in network socket programming(3-way handshaking)](https://stackoverflow.com/questions/34676972/about-listen-accept-in-network-socket-programming3-way-handshaking) > > 其他參考資料: > - [TCP SYN Queue and Accept Queue Overflow Explained](https://www.alibabacloud.com/blog/tcp-syn-queue-and-accept-queue-overflow-explained_599203) > - [TCP Control Block Interdependence](https://www.rfc-editor.org/rfc/rfc9040.pdf) 首先,伺服器端使用 socket() 建立一個 socket,再透過 bind() 將其綁定到特定的 IP 地址與埠號,接著呼叫 listen() 將該 socket 設為監聽狀態,此 socket **僅負責等待客戶端的連線請求,並不會與任何客戶端建立連線**,因為伺服器要同時處理多個客戶端請求,所以要保留原本的 socket 來監聽多個客戶端的連線請求,當有客戶端發送連線請求(客戶端執行 connect())時,客戶端的 TCP/IP 堆疊就會要發送 SYN 封包給伺服器^[如下圖]^,在發送 SYN 之前,客戶端的 kernel 必須先配置一個臨時埠([ephemeral port](https://en.wikipedia.org/wiki/Ephemeral_port "Ephemeral port"))用來與伺服器連接,在客戶端發送 SYN 封包後,伺服器的 TCP/IP 堆疊就會在 SYN queue 中建立一個 entry,並回傳 SYN-ACK 封包給客戶端完成第二次握手,此時,客戶端的 connect() 就會回傳 0 表示成功,並再發送 ACK 封包給伺服器完成第三次握手,當伺服器收到 ACK 後,便會把剛剛建立在 SYN queue 中的 entry 移動到 Accept queue 中,伺服器就可以透過 accept() 從 Accept queue 中取出一個客戶端的 socket 資訊,並回傳一個全新的 socket(與負責監聽的 socket 不同),此 socket 負責與該客戶端做資料交換。因此,每當有新的客戶端完成連線時,accept() 就是負責從 Accept queue 中取出一份客戶端的連線資訊,並建立新的 socket,負責與客戶端做資料交換。 ![image](https://hackmd.io/_uploads/Byd282_-xe.png =50%x) 回答問題:為何有 accpet 系統呼叫?為何不能直接 read socket fd? 因為**負責監聽的 listen_fd,要接受很多客戶端的連線請求**,為了不讓傳輸的資料都混在一起,伺服器為每一個客戶端建立對應的 socket_fd,因此需要 accept() 去建立新的 socket_fd,以確保每一個 TCP 通訊都是獨立的。 :::info 待釐清: TCP/IP stack 運行三次握手的細節是? SYN/Accept queue 中的 entry 內容是什麼? ::: ### Q4: 比照 remove_list_node 撰寫 find_node / reverse_nodes,利用 indirect pointer (no external allocated memory. i.e., constant space complexity),如何驗證? ```c= bool find_node(List *list, Node *target) { Node **indirect = &list->head; while (*indirect != NULL) { if (*indirect == target) { return true; } indirect = &(*indirect)->next; } return false; } ``` ```c= void reverse_node(List *list) { if (!list || !list->head) { return; } Node *cur = list->head; Node *prev = NULL; Node *next = NULL; while (*cur) { next = cur->next; cur->next = prev; prev = cur; cur = next; } list->head = prev; } ``` ### 追蹤程式碼 - 追蹤 find_node - 假設 `target` 為 `node2` - 第 3 行 ![截圖 2025-05-18 上午11.20.38](https://hackmd.io/_uploads/HJZfMCIbgl.png =60%x) - 第 4 ~ 9 行 ![截圖 2025-05-18 上午11.23.12](https://hackmd.io/_uploads/HyssfRLZgg.png =60%x) - 假設 `target` 不在鏈結串列中 - 第 3 行 ![截圖 2025-05-18 上午11.25.41](https://hackmd.io/_uploads/BkxBQCIbel.png =60%x) - 第 4 ~ 9 行 ![截圖 2025-05-18 上午11.29.26](https://hackmd.io/_uploads/BJM7VR8bex.png =60%x) - 第 10 行:return FALSE - 追蹤 reverse_node - 第 6 ~ 8 行 ![截圖 2025-05-18 上午11.00.26](https://hackmd.io/_uploads/S1BUpaLbeg.png =60%x) - 第 10 ~ 15 行 ![截圖 2025-05-18 上午11.08.33](https://hackmd.io/_uploads/BkCNy08bel.png =60%x) - 第 17 行 ![截圖 2025-05-18 上午11.10.27](https://hackmd.io/_uploads/ryRsy08Zxg.png =60%x) ### 寫出完整程式碼來驗證 :::spoiler 完整程式碼 ```c #include <stdio.h> #include <stdbool.h> typedef struct Node { int data; struct Node* next; } Node_t; typedef struct { struct Node* head; } List_t; bool find_node(List_t *list, Node_t *target) { Node_t **indirect = &list->head; while (*indirect != NULL) { if (*indirect == target) { return true; } indirect = &(*indirect)->next; } return false; } void reverse_node(List_t *list) { if (!list || !list->head) { return; } Node_t *cur = list->head; Node_t *prev = NULL; Node_t *next = NULL; while (cur) { next = cur->next; cur->next = prev; prev = cur; cur = next; } list->head = prev; } void print_list(List_t *list) { Node_t *cur = list->head; while (cur != NULL) { printf("%d -> ", cur->data); cur = cur->next; } printf("NULL\n"); } int main() { Node_t node4 = {4, NULL}; Node_t node3 = {3, NULL}; Node_t node2 = {2, &node3}; Node_t node1 = {1, &node2}; List_t list = {.head = &node1}; printf("list: "); print_list(&list); printf("Find 2: %s\n", find_node(&list, &node2) ? "Found" : "Not Found"); printf("Find 4: %s\n", find_node(&list, &node4) ? "Found" : "Not Found"); reverse_node(&list); printf("Reverse list: "); print_list(&list); return 0; } ``` ::: - 驗證 find_node - 1. 建立單向鏈結串列 ```tex node1 -> node2 -> node3 -> NULL ``` - 2. 額外節點(未列入串列中) ```tex node4 ``` - 3. 驗證 | 執行方法 | 預期結果 | | ------------------------ | -------- | | find_node(&list, &node2) | TRUE | | find_node(&list, &node4) | FALSE | - 驗證 reverse_node - 呼叫 reverse_node(&list) - 預期結果: ```tex node3 -> node2 -> node1 -> NULL ``` - 輸出結果: ```bash jimmy@NEAT:~/linux2025$ ./test_list list: 1 -> 2 -> 3 -> NULL Find 2: Found Find 4: Not Found Reverse list: 3 -> 2 -> 1 -> NULL ``` ### Q5: 複雜度 大 $O$、小 $o$ 的定義? > [楓葉書](https://www.tenlong.com.tw/products/9780262046305?list_name=srh) Page. 47 - 為何有大 $O$ 和小 $o$ 的存在? - 大 $O$: - 用來描述函數增長速率的上界,也就是在最壞情況下的時間或空間複雜度。這種表示法讓我們能忽略常數因子,專注於演算法在輸入規模增長時的時間複雜度趨勢。透過這種方式可以更有效的比較不同演算法在處理大規模輸入時的效率表現。例如:$O(nlogn)$ 通常比 $O(n²)$ 更有效率。 - 小 $o$: - 用來描述一個函數相對於另一個函數的**嚴格上界**,意味著前者的增長速率遠小於後者(即在輸入規模無窮大時,前者對後者的比直趨向於 0)。例如:Dijkstra 演算法,圖有 $V$ 個節點和 $E$ 條邊,並將尚未拜訪的點存於 Binary Heap 中,時間複雜度為 $O(ElogV)$ ,不過在這個演算法中除了主要的 $O(ElogV)$ 開銷外,還有一些額外的預處理操作,例如: - 建立 Binary Heap:Buttom Up 建立需 $O(V)$。 - 節點標籤初始化:為每個節點設置初始標籤需 $O(V)$。 這些額外操作的時間複雜度 $O(V)$ 相對於 $O(ElogV)$ 是次要的。因此,總時間複雜度可以表示為:$O(ElogV) + o(ElogV)$,這裡的 $o(ElogV)$ 表示所有增長速率遠小於 $ElogV$ 的項,$O(V)$(初始化),因為 $V / (ElogV) → 0$(當 $V$ 和 $E$ 趨進無窮大時,考慮 $E ≥ V - 1$),明確指出 $O(V)$ 的開銷相對於 $O(ElogV)$ 是微不足道的。 - $Big-O$ - **定義**:設 $f,g:\mathbb N \to \mathbb R_{\ge 0}$ 若 $\exists c, n_0\in \mathbb R^+$,使得 $\forall n \ge n_0,\; 0\ \le f(n) \le cg(n)$,則稱 $f(n)=O(g(n))$,寫作 $f(n) \in O(g(n))$ - 例子:試證 $f(n) = 3n + 3$,則 $f(n) = O(n)$ 解:$\because \exists c = 4, n_0 = 3$ 使得 $\forall n \ge n_0 = 3,\; 0 \le 3n + 3 \le 4n = cg(n)$ $\therefore f(n) = O(n)$ 得證。 - 也就是說,當 $n$ 大於等於 $3$ 時,可以保證 $f(n)$ 一定會小於 $4\,g(n)$,如下圖,橫軸表示為輸入資料量 $n$,縱軸表示執行時間,只參考第一象限,因為執行時間與輸入資料量不可能為負 (Source: [GeoGebra](https://www.geogebra.org)): ![截圖 2025-05-17 晚上9.13.27](https://hackmd.io/_uploads/B1LFsb8Zex.png =60%x) - $Little-o$ - **定義**:設 $f,g:\mathbb N\to\mathbb R_{\ge 0},$ 若 $\forall c \in \mathbb R^+,\;\exists n_0\in\mathbb R^+$,使得 $\forall n \ge n_0,\;0\ \le f(n) \lt cg(n)$,則稱 $f(n)=o(g(n))$,寫作$f(n) \in o(g(n))$ 又或者是 $\lim_{n \to \infty} \cfrac{f(n)}{g(n)} = 0 \Leftrightarrow f(n) = o(g(n))$ - 例子:試證 $f(n) = 3n + 3$,則 $f(n) = o(n^2)$ - 解法一: $3n+3 < cn^2 \Rightarrow 3 < cn^2 -3n \Rightarrow 3c < c^2n^2 - 3cn \Rightarrow 3c + 2.25 < (cn - 1.5)^2$$\therefore \sqrt{3c + 2.25} + 1.5 \lt cn \Rightarrow \cfrac{\sqrt{3c + 2.25} + 1.5}{c} < n$,故當$n > \cfrac{\sqrt{3c + 2.25} + 1.5}{c}$ 時,$f(n) < cn^2$ 成立,得證。 - 解法二: $\lim_{n \to \infty}\cfrac{3n+3}{n^2} = \lim_{n \to \infty} (\cfrac{3n}{n^2} + \cfrac{3}{n^2}) = \lim_{n \to \infty} (\cfrac{3}{n} + 0) = 0$ 得證。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully