# Intro. to Algorithm HW2 ###### tags: `Intro. to Algorithm` - [name=曾文鼎 0716023] ## 1 ### a 因為 $f(n)=O\left(h(n)\right)$ ,所以 $\exists n_1>0,c_1>0$ 使得 $$\forall n>n_1, f(n)\leq c_1h(n)$$ 同理 $\exists n_2>0,c_2>0$ 使得 $$\forall n>n_2, g(n)\leq c_2k(n)$$ 令 $n_0=\max(n_1,n_2),c_0=c_1c_2$ 則必有 $\forall n>n_0$ 使得 $$f(n)g(n) \leq c_1c_2h(n)k(n)=c_0h(n)k(n)$$ 故 $f(n)g(n)=O\left(h(n)k(n)\right)$ 。 ### b 以下用羅畢達證明 $\log^8n=o(n)=O(n)$ ,並且將 $\log$ 視為 $\ln$。 $$\begin{align}\lim_{n\to\infty}\frac{\log^8 n}{n} &= \lim_{n\to\infty}\frac{8\cdot\log^7 n}{n} \\&= \lim_{n\to\infty}\frac{8\cdot7\cdot\log^6 n}{n} \\&= \lim_{n\to\infty}\frac{8\cdot7\cdot6\cdot\log^5 n}{n} \\&= \cdots \\&= \lim_{n\to\infty}\frac{8!}{n} \\&=0 \end{align}$$ 故 $f(n)+\log^8n=O(n)+O(n)=O(n)$ 。 ## 2 以下給出 pseudo code 。 ```javascript function judge(A) { let n = A.length let dp3 = new Array[n+1][n+1] let dp5 = new Array[n+1][n+1] fill dp3 with 0 fill dp5 with infinity for (s=1; s<=2*n; s++) { for (x=1; x<=n; x++) { let y = s - x if (y < 1 or y > n) continue; dp3[x][y] = max(dp3[x-1][y], dp3[x][y-1]) dp5[x][y] = min(dp5[x-1][y], dp5[x][y-1]) if (A[x][y] % 3 == 0) dp3[x][y]++ if (A[x][y] % 5 == 0) dp5[x][y]++ } } return dp3[n][n] > dp5[n][n] } ``` * 宣告並初始化陣列 `dp3` 和 `dp5` 的複雜度是 $O(n)$ 。 * 兩個 `for` 迴圈的複雜度各是 $O(n)$ ,合計起來是 $O(n)\times O(n)=O\left(n^2\right)$ 。 * 其餘操作都是 $O(1)$ 。 故整體複雜度是 $O\left(n^2\right)$ 。 ## 3 直接使用 LIS 可以做出 "LBS" (Longest Bitonic Subsequence) ,複雜度為 $O(n\log n)$ 。作法是 1. 對於所有 $1\leq i\leq n$ ,將 input 陣列的前 $i$ 項 subarray 計算 LIS 長度。複雜度 $O(n\log n)$ 。 2. 反轉 input 陣列。然後模仿步驟 1. 。複雜度 $O(n\log n)$ 。 3. 合併 1. 和 3. 的結果,得出 LBS 長度。複雜度 $O(n)$ 。 以下給出 pseudo code 。 ```javascript // Queires the number of elements, which are not greater than t, in the // given sorted_array. The complexity is O(log(n)). function lower_bound(sorted_array, t) { let l = 0, r = sorted_array.length - 1 while (l <= r) { let m = floor((l + r) / 2) if (sorted_array[m] <= t) l = m + 1 else r = m - 1 } return l } // Returns an array, index i of which indicates length of longest // increasing subsequence of subarray within given array from index 0 // to i. E.g. if input array is [0,7,1,6,0,2,3] then [1,2,2,3,3,3,4] // is returned. The complexity is O(n*log(n)). function lis(array) { let ret = [] // empty array let dp = [] for (i=0; i<array.length; i++) { // O(n) let val = array[i] let l = lower_bound(dp, val) // O(log(n)) if (l == dp.length) dp.append(val) else dp[l] = min(dp[l], val) ret.append(dp.length) } return ret } // Queries length of longest bitonic subsequence within array A. The // complexity is O(n*log(n)). function lbs(A) { let p = lis(A) // O(n*log(n)). let r = reverse(lis(reverse(A))) // O(n*log(n)). let ret = 0; for (i=0; i<A.length; i++) // O(n) ret = max(ret, p[i] + r[i] - 1) return ret } ``` ## 4 以下給出 pseudo code 。顯然他是 $O(n)=O\left(n^2\right)$ 。當 `for` 迴圈跑到 $i=k$ 時, $\mathrm{dp}[j]$ 表示當 set 中元素的最大值被限制為 $k$ 的條件下, $j$ 的 integer partition set 的數量。 ```javascript // Queries count of distinct integer partision set. function judge(n) { let dp = new Array[n+1] fill dp with 0 dp[0] = 1 for (i=1; i<=n; i++) for (j=1; j<=n; j++) dp[j] += dp[j-i] return dp[n] } ``` ## 5 以下給出 $O(n)$ 的演算法。 ### 準備作業 * 如果 $B$ 是一個籃子,則定義 $|B|$ 為該籃子中石頭的數量。 * 準備一個籃子 $S$ ,然後把所有石頭放到 $S$ 裡。 * 定義各石頭的 CP 值為 $v_i/w_i$ 。 * 準備一個空背包,這個背包裡的石頭將是答案。其餘重定義為 $m$ 減去其已放入的石頭的總重。 * 準備一個垃圾桶,收集被棄置的石頭。 ### 演算法流程 1. 若 $|S|=0$ 或背包餘重不足 $3$ ,跳到步驟 9. 。 2. 用 Quick select 找出 $S$ 裡石頭的 CP 值的中位數,令其為 $c$ 。此步驟複雜度 $O(|S|)$ 。 3. 準備四個籃子 $G$、$E_1$、$E_3$、$L$ ,依照下列規則將石頭移至這些籃子當中的其中一個。此步驟複雜度為 $O(|S|)$ 。 * CP 值大於 $c$ 的石頭移至 $G$ * CP 值等於 $c$ 且重量為 $1$、$3$ 的石頭分別移至 $E_1$、$E_3$ * CP 值小於 $c$ 的石頭移至 $L$ 。 4. 若 $G$ 的總重量為大於背包餘重,則將 $G$ 中的石頭全部移至 $S$ ,並且把其他他三個籃子裡的石頭丟進垃圾桶,然後回到步驟 1.。此步驟複雜度為 $O\left(|S|\right)$ 。 5. 將 $G$ 中的石頭全部移至背包。此步驟複雜度為 $O(|S|)$ 。 6. 在不超重的前提下,盡量將 $E_3$ 中的石頭移至背包。因為 $E_3$ 中的石頭都一樣,所以不分放入順序。此步驟複雜度為 $O(|S|)$ 。 7. 在不超重的前提下,盡量將 $E_1$ 中的石頭移至背包。因為 $E_1$ 中的石頭都一樣,所以不分放入順序。此步驟複雜度為 $O(|S|)$ 。 8. 將 $L$、$E_3$ 和 $E_1$ 中的石頭全部移至 $S$ ,然後回到步驟 1.。此步驟複雜度為 $O(|S|)$ 。 9. 若背包仍有餘重,則盡量將重量為 $1$ 且價值最大的石頭移至背包,直到背包沒有餘重、或找不到重量為 $1$ 的石頭為止。注意,這裡要搜尋的石頭不限於 $S$ 中的石頭,也包含垃圾桶裡的石頭。 10. 演算法結束。 ### 複雜度計算 注意以下事實: * 因為在步驟 4. 中,必有 $|G|\leq|S|/2$ 且 $|L|\leq|S|/2$ ,且其他步驟的複雜度皆限於 $O(|S|)$ 。所以無論後來是 $S=G$ 還是 $S=L$ ,都遵守以下遞迴關係式,其中 $f(n)$ 為本演算法的複雜度。 $$f(n)=f\left(\frac n 2\right) + O(n)$$ 根據 Master Theorem ,可以知道 $f(n)=O(n)$ 。 * 到步驟 8. 時,背包餘重至多為 $2$ ,故此步驟最多移動兩個石頭,其複雜度為 $$O(2)\cdot O(n)=O(n)$$ 綜合以上,本演算法的複雜度為 $O(n)$ 。 ## 6 ### a 假設我有 $k=3$ 的演算法 $A$ 和陣列 $S$ ,並令 $n=|S|$ ,則我可以用以下方法判斷 element uniqueness problem 。 1. 檢查 $S_1$ 和 $S_2$ 是否相同。若是,則答案為假。此步驟複雜度 $O(1)$ 。 2. 將一個 $S_1$ 和一個 $S_2$ 放入到 $S$ 尾端。此步驟複雜度 $O(1)$ 。 3. 對 $S$ 使用演算法 $A$ 。若其有輸出值,令其為 $h$ ;若其輸出 `not found.` ,則答案為真。此步驟複雜度待定。 4. 若 $h$ 在 $S$ 中只出現一次,則答案為真;否則為假。此步驟複雜度 $O(n)$ 。 因為既成事實是 element uniqueness problem 的複雜度至低為 $\Omega(n \log n)$ ,可以斷定 $k=3$ 的演算法 $A$ 的複雜度至低為 $\Omega(n \log n)$ 。 ### b 假設我有 $k=\lfloor \log n\rfloor$ 的演算法 $A$ 和陣列 $S$ ,並令 $n=|S|$ ,則我可以用以下方法判斷 element uniqueness problem 。 1. 檢查 $S$ 的首 $\lfloor \log n\rfloor -1$ 項元素是否出現重複。若是,則答案為假。就算用 naive 方法,此步驟的複雜度也頂多 $O\left(\log^2 n\right)=O(n)$ 。 3. 在 在 $S$ 尾端加入 $S$ 的首 $\lfloor \log n\rfloor -1$ 項元素各一個。此步驟時間複雜度為 $O(\log n)$ 。 5. 對 $S$ 使用演算法 $A$ 。若其有輸出值,令其為 $h$ ;若其輸出 `not found.` ,則答案為真。此步驟複雜度待定。 6. 若 $h$ 在 在 $S$ 中只出現一次,則答案為真;否則為假。此步驟複雜度 $O(n)$ 。 因為既成事實是 element uniqueness problem 的複雜度至低為 $\Omega(n \log n)$ ,可以斷定 $k=\lfloor \log n\rfloor$ 的演算法 $A$ 的複雜度至低為 $\Omega(n \log n)$ 。