# 物件導向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 的基底資料,
基數的拉高導致倘若分配均勻,第二版程式則會有較第一版程式顯著成長的執行效率。