# 物件導向HW5討論報告 ## 結果比較 ### 第一版 #### 測資3-1 過濾結果:5276 花費時間:3184ms #### 測資3-2 過濾結果:- 花費時間:超過10小時,但僅處理完不到一半測資 #### 測資3-3 過濾結果:24 花費時間:0ms ### 第二版 #### 測資3-1 過濾結果:5276 花費時間:468ms **較第一版快2.716秒** #### 測資3-2 過濾結果:687166 花費時間:5587171ms (約1.55小時) **較第一版快超過可能十來小時** #### 測資3-3 過濾結果:24 花費時間:430ms **較第一版慢0.430秒** ## 演算法說明 >已自行重新定義 Point 型別的 == 及 != 運算子 ### 第一版 此版使用單純的 "nested for loop" 以及 if/else 判斷式, 讓單點和後續所有點一一比對,並於兩點相同時移除該後續點,同時計數減一,僅保留第一點, 照此步驟持續至陣列結尾,完成所有點的比對。 由於範例程式給的是 Point 的一維陣列,但個人習慣是使用 Point* 的陣列, 原因是習慣用 nullptr 來做物件存在判斷。 考量到任何點皆有可能出現,並提升程式撰寫方便性, 此題我取陣列第一項 Point 作為移除點後 blank 的代表值, 這也是為何底下程式第9行的迴圈入口條件為 第一次迴圈 or 基準點尚未移除。 以下程式: ```cpp=1 // Use first point to represent blank after removing Point empty = point_array[0]; int left = nPoint; // left amount // Check each one with pushing forward starter for (int i = 0; i < nPoint - 1; ++i) { // Check if the point isn't removed yet if (i == 0 || point_array[i] != empty) { for (int j = i + 1; j < nPoint; ++j) { // If same, remove & minus one if (point_array[i] == point_array[j]) { point_array[j] = empty; left--; } } } } ``` ### 第二版 >已自行重新定義 List(linked list) 及 Node 型別,及其基本函式。 此版的第一副版本是使用單一 linked list 紀錄已出現之非重複點, 接著用所有點對 list 進行查詢及插入。 而後我再進行改良, 畢竟對410萬筆資料的第二測資來說,移除重複點後仍有69萬個點,及list長69萬, 而 List::find() 的方法即完全遍歷,效率上實在不足。 目前最終版,我選擇較為暴力的拆解法, 我根據每個點的三軸座標,將其小數位捨棄化整後,再取其除100的餘數, 最後依照這三個數將其插入該特定的 List 中, 以試圖避免單一 List 過長的問題。 以下為程式: ```cpp=1 // A three-dimension array of linked list // categorate points by its tail of integer part List ****result = new List***[100]; for (int i = 0; i < 100; ++i) result[i] = new List**[100]; for (int i = 0; i < 100; ++i) for (int j = 0; j < 100; ++j) result[i][j] = new List*[100]; for (int i = 0; i < 100; ++i) for (int j = 0; j < 100; ++j) for (int p = 0; p < 100; ++p) result[i][j][p] = new List(); // Check if a point exist at its list, if not, insert it for (int i = 0; i < nPoint; ++i) { int x = (long long int)(point_array[i][0]) >= 0 ? (long long int)(point_array[i][0]) % 100 : (long long int)(-point_array[i][0]) % 100; int y = (long long int)(point_array[i][1]) >= 0 ? (long long int)(point_array[i][1]) % 100 : (long long int)(-point_array[i][1]) % 100; int z = (long long int)(point_array[i][2]) >= 0 ? (long long int)(point_array[i][2]) % 100 : (long long int)(-point_array[i][2]) % 100; if (!result[x][y][z]->find(point_array[i])) result[x][y][z]->insert(point_array[i]); } // Get sum of sizes int size = 0; for (int i = 0; i < 100; ++i) for (int j = 0; j < 100; ++j) for (int p = 0; p < 100; ++p) size += result[i][j][p]->size; ``` ## 效率結果討論 這兩版演算法的時間複雜度皆為 O(n^2) 對最糟的結果來說 1. 全相異點 2. 在第二版中皆位於同一 linked list 上 實際上第二版甚至比第一版慢。 但兩者不同的是, 第一版的 nested loop,決定其運算時間的主要是測資點數量,其變動性較低, 無論點分布為何,都無法改變運算時間太多。 第二版的時間複雜度則取決於點在100\*100\*100 List陣列的分布上, 測資數量並不是最大的影響,畢竟n個點我們也就每個點丟進去分析一次, 所謂最糟情況是全部在同一List上,遍歷時間拉高導致單一點的運算量跟著提高, 因此時間複雜度計算為(n+1)\*n/2,即1加到n, 但倘若今天點位較為分散,則將大大提升程式效率。 總結一下, 在看測試時間比較時可以看到,第三筆測資 (132->24) 上,反而原版程式較快, 推測由於第二版有較多記憶體動態分配流程,以及最壞情況下本來程式執行效率就較差的緣故。 但第一二筆測資,分別有31632 和 4123500 的基底資料, 基數的拉高導致倘若分配均勻,第二版程式則會有較第一版程式顯著成長的執行效率。