contributed by <`eleanorLYJ`> ## [第 3 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz3) ### 測驗 1 平方根 除了測驗題上的敘述,也閱讀[Digit-by-digit calculation: Binary numeral system (base 2)](https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2)) > 本筆記的變數的下標與作業說明順序相反 把 N 看為 某 bit pattern 的平方, 其中 $a_i$ 可能是 $a^i$ 或 0。我將 $2_i$ 想成 2 的冪的係數 $$N^2 = (a_0+a_1+a_2+...+a_n)^2$$ $$ N =\left[\begin{matrix} a_0a_0 & a_0a_1 & a_0a_2 &... & a_0a_{n}\\ a_{1}a_{0} & a_{1}a_{1} & a_{1}a_{2}&... & a_{1}a_{n}\\ a_{2}a_{0} & a_{2}a_{1} & a_{2}a_{2}&... & a_{2}a_{n}\\ ⋮ & ⋮ & ⋮ & ⋮ & ⋮\\ a_{n}a_{0} & a_{n}a_{1} & a_{n}a_{2} &... &a_{n}a_{n} \\ \end{matrix}\right] $$ $$ N^2 = a_0a_0 + ( a_0a_1+a_1a_0+ a_1a_1) +(a_0a_2 +a_{1}a_{2} +a_{2}a_{2}+a_{2}a_{1}+a_{2}a_{0}) +...+ \sum_{i=0}^{n-1} \sum_{j = n-1}^{0} a_ia_j ={a_0}^2+ (a_{1}*(a_0+a_1+a_0)) + (a_{2}*(a_0+a_1+a_2+a_1+a_0)) +... ={a_0}^2+ (a_{1}*(2a_0+a_1)) + (a_{2}*(2a_0+2a_1+a_2)) + ...+ a_n*[2 \sum_{i=0}^{n-1}a_i+a_n] $$ 設$P_k = a_0+ a_1 +a_2+..+a_k = \sum_{i=0}^{k}a_i$ 故 $N^2 = a_0 + (a_1*(2P_1+a_1))+ (a_2*(2P_2+a_2)) +...+ (a_n*(P_{n-1}+a_n))$ 。 $P_m$ 會有兩種情況 $$ \begin{aligned} P_m &= \begin{cases} P_{m-1} +2^m \text{, if } {P_m}^2 \leq N^2 \\ P_m, \text{ otherwise} \end{cases} \end{aligned} $$ 這算 n~0 逐一算 $N^2 - {P_m}^2$,得知 m 等於多少時,使 $P_m$ 值最接近 $\sqrt{N}$ 為了避免不要不斷的去算出${P_m} ^2$,所以改成僅存每一輪的差異, $X_{m} = N^2 - {P_m}^2$。 $X_m$ 的更新為: $X_m= X_{m-1}-Y_{m}$ 而 Y 的定義為上一輪的 P 與此輪的 P 的差異: $$Y_m = {P_m}^2 - {P_{m-1}}^2 = 2a_m{P_{m-1}} + a_m^2$$ 這裡的推導 $$ \begin{aligned} {P_m}^2 &= {a_0} ^2 + {a_1}^2 + ... {a_{m-1}}^2+ {a_m}^2 + 2a_0a_1 + ...+2a_0a_{m-1}+2a_{0}a_{m}+2a_1a_{m-1}+2a_1a_{m} \\ {P_{m-1}}^2 &= {a_0}^2 + {a_1}^2 + ... {a_{m-1}}^2 + 2a_0a_1 + ...+2a_0a_{m-1}+2a_1a_{m-1} \\ {P_m}^2 - {P_{m-1}}^2 &= {a_m}^2 +2a_m(a_0+...+a_{m-1})\\ Y_m &= 2a_m{P_{m-1}} + {a_m}^2 \\ \end{aligned} $$ 接著在維基百科中說: 將 $Y_m$ 看成兩部分 $c_m$ 與 $d_m$ $$ \begin{aligned} c_m &= 2a_m{P_{m-1}} \\ d_m &= {a_m}^2 = (2^m)^2 = 2^{2m}\\ Y_m &= \begin{cases} c_m+d_m, & \text{if } a_m=2^m \\ 0, & \text{if } a_m=0 \\ \end{cases}\\ \end{aligned} $$ 接著看如何迭代 <!-- $Y_{m_1}$有相同嗎??Y_{m-1} &= 2 a_{m-1} P_{m-2} + {a_{m-1}}^2 \\ --> $$ \begin{aligned} P_m &= P_{m-1}+a_m\\ c_{m-1} &= 2a_{m-1} P_{m-2}= \frac{c_m}{2} +d_m \\ d_{m-1} &= {a_{m-1}}^2 = 2^{2(m-1)} = \frac{d_m}{4}\\ Y_{m-1} &= \begin{cases} c_m/2+d_m, & \text{if }a_m=2^m \\ c_m/2, & \text{if } a_m=0 \\ \end{cases}\\ \\ \end{aligned} $$ 以下程式的變數意義: :::info b = $y_m$ = $P_m$ - $P_{m-1}$ z = $c_m$ m = $d_m$ x = $X_m = N^2 - {P_m}^2$ ::: ```c int i_sqrt(int x) { if (x <= 1) /* Assume x is always positive */ return x; int z = 0; for (int m = 1UL << ((31 - __builtin_clz(x)) & ~1UL); m; m >>= 2) { int b = z + m; z >>= 1; if (x >= b) x -= b, z += m; } return z; } ``` ### 測驗 3 ilog2() 用二分搜尋法尋找 Most Significant Bit (MSB) ```c static size_t ilog2(size_t i) { size_t result = 0; while (i >= AAAA) { result += 16; i >>= 16; } while (i >= BBBB) { result += 8; i >>= 8; } while (i >= CCCC) { result += 4; i >>= 4; } while (i >= 2) { result += 1; i >>= 1; } return result; } ``` ### 測驗 5 ceil_log2 一開始的 `x` 減1 是為了確保結果向上進位到下一個整數。而 `r` 用於累加位移位元數。 該程式碼會迭代`x`多次,檢查特定的位元模式。它使用移位操作從中刪除已檢查的位元`x`。首先檢查最高位元 (MSB > 16),接著每次檢查的位元都除以2。 最終有考量最後 x 等於 2 或 3 的情況會進位,而最終 x 等於 1 時不會進位 ```c int ceil_ilog2(uint32_t x) { uint32_t r, shift; x--; r = (x > 0xFFFF) << 4; x >>= r; shift = (x > 0xFF) << 3; x >>= shift; r |= shift; shift = (x > 0xF) << 2; x >>= shift; r |= shift; shift = (x > 0x3) << 1; x >>= shift; return (r | shift | x > 1) + 1; } ```