diff --git a/009_Greedy/01Greedy.md b/009_Greedy/01Greedy.md index 99ea23a..ad96632 100644 --- a/009_Greedy/01Greedy.md +++ b/009_Greedy/01Greedy.md @@ -3,11 +3,11 @@ 定義貪婪規則,每一步都會根據貪婪規則執行,找出局部最佳解 ## 奢望_找出最佳解 -1. 挑當下"看起來最好"的先用 +1. 挑當下**看起來最好**的先用 ## 找出最佳解 -1. 挑當下"看起來最好"的先用 +1. 挑當下**看起來最好**的先用 2. 當下最好 = 整體最好 貪婪演算法需要注意如果 當下最好 != 整體最好 ,則無法找出最佳解 diff --git a/010_Enumeration/01Enumeration.md b/010_Enumeration/01Enumeration.md index 8121edf..898d3b3 100644 --- a/010_Enumeration/01Enumeration.md +++ b/010_Enumeration/01Enumeration.md @@ -1,5 +1,6 @@ ## 枚舉法 -又稱Brute Force 暴力破解法 、 窮舉法 + +又稱 窮舉法 把每個結果都計算出來,在求出合格解、最佳解 @@ -9,5 +10,5 @@ ## 找出最佳解 1. 所有結果比對進行比較 - +## 備註 比如Tree探索之類相關的Problem,可以透過Backtracking回朔法優化,減少不必要的計算 \ No newline at end of file diff --git a/011_Backtracking/01Backtracking.md b/011_Backtracking/01Backtracking.md index 280e1ae..2369989 100644 --- a/011_Backtracking/01Backtracking.md +++ b/011_Backtracking/01Backtracking.md @@ -3,8 +3,8 @@ 適合找出合格解或是所有解,如果無法往下就開始回朔上一個步驟,再跳過自己這個步驟進入下一個步驟 ## 找出合格解 -1. 走的過程中發現超出條件就停止繼續往下並回頭 -2. 走到底了沒辦法繼續並回頭 +1. 走的過程中發現超出條件就停止繼續往下並回朔 +2. 走到底了沒辦法繼續並回朔 ## 找出所有解 1. 同枚舉法 diff --git a/011_Backtracking/Travelplan_backtracking.java b/011_Backtracking/Travelplan_backtracking.java index fb80752..0cb4e3c 100644 --- a/011_Backtracking/Travelplan_backtracking.java +++ b/011_Backtracking/Travelplan_backtracking.java @@ -1,4 +1,3 @@ - import java.util.ArrayList; import java.util.List; @@ -89,6 +88,7 @@ private void backtracking_recursion(Integer constraint_hour) { print_result(hour_total); } } else { + // backtracked if (hour_total >= constraint_hour) { System.out.print("[backtracked]: "); print_result(hour_total); diff --git a/012_Banch_and_bound/01Banch_and_bound.md b/012_Banch_and_bound/01Banch_and_bound.md new file mode 100644 index 0000000..cf83026 --- /dev/null +++ b/012_Banch_and_bound/01Banch_and_bound.md @@ -0,0 +1,17 @@ +## 分枝界限法 + +又稱 分枝定界法 + +選擇當下最佳,並對其分枝給予上界限or下界限( 界限會自動更新 )進行決定是否要結束其分枝並回朔,再選擇次佳 + +## 找出最佳解 + +1. Branch: 挑當下**看起來最好**的先走 +2. Bound: 不可能更好,就回朔 + +## 備註 + +1. 會一直記錄最佳值 +2. 如果當下已知,不可能更好,就回朔 Bound +3. 在進入分枝前會先排序可以往下的分枝 Branch +4. 本質上是一種Backtracking之DFS版本優化 \ No newline at end of file diff --git a/012_Banch_and_bound/Travelplan_BranchBound.java b/012_Banch_and_bound/Travelplan_BranchBound.java new file mode 100644 index 0000000..e90d976 --- /dev/null +++ b/012_Banch_and_bound/Travelplan_BranchBound.java @@ -0,0 +1,191 @@ +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; + +public class Travelplan_BranchBound { + + Integer[][] hours; + String[] c_remained = { "NP", "IS", "CA", "UK", "US" }; + + private void build_hour_table() { + // NP: 0 (North Pole) + // IS: 1 + // CA: 2 + // UK: 3 + // US: 4 + + this.hours = new Integer[5][5]; + + hours[0][0] = 0; // NP -> NP + hours[0][1] = 14; // NP -> IS + hours[0][2] = 15; // NP -> CA + hours[0][3] = 17; // NP -> UK + hours[0][4] = 16; // NP -> US + + hours[1][0] = 14; // IS -> NP + hours[1][1] = 0; // IS -> IS + hours[1][2] = 24; // IS -> CA + hours[1][3] = 8; // IS -> UK + hours[1][4] = 36; // IS -> US + + hours[2][0] = 15; // CA -> NP + hours[2][1] = 24; // CA -> IS + hours[2][2] = 0; // CA -> CA + hours[2][3] = 34; // CA -> UK + hours[2][4] = 4; // CA -> US + + hours[3][0] = 17; // UK -> NP + hours[3][1] = 8; // UK -> IS + hours[3][2] = 34; // UK -> CA + hours[3][3] = 0; // UK -> UK + hours[3][4] = 30; // UK -> US + + hours[4][0] = 16; // US -> NP + hours[4][1] = 36; // US -> IS + hours[4][2] = 4; // US -> CA + hours[4][3] = 30; // US -> UK + hours[4][4] = 0; // US -> US + } + + public Integer get_hour(String start, String end) { + Integer x = get_index(start); + Integer y = get_index(end); + return this.hours[x][y]; + } + + private Integer get_index(String str) { + if (str.equals("NP")) + return 0; + if (str.equals("IS")) + return 1; + if (str.equals("CA")) + return 2; + if (str.equals("UK")) + return 3; + if (str.equals("US")) + return 4; + return null; + } + + // enumeration -> backtracking -> branchBound + static class Country { + String name; + Integer index; + Integer hour_to_take; // by sort + + public Country(String name, Integer index, Integer hour_to_take) { + this.name = name; + this.index = index; + this.hour_to_take = hour_to_take; + } + } + + static class myComp implements Comparator { + + @Override + public int compare(Country c1, Country c2) { + return c1.hour_to_take - c2.hour_to_take; // low-> high + } + + } + + List route = new ArrayList<>(); + Integer hour_best = null; + + public void branchbound() { + String c_start = "NP"; + route.add(c_start); + c_remained[0] = null; + + branchbound_recursion(); + } + + private void branchbound_recursion() { + int hour_total = get_hour_total(); + + if (route.size() == 5) { + if (hour_best == null || hour_total < hour_best) { + hour_best = hour_total; + print_result(hour_total); + } else { + System.out.print("[X]: "); + print_result(hour_total); + } + } else { + // bound check + if (hour_best != null && hour_total >= hour_best) { + System.out.print("[bounded]: "); + print_result(hour_total); + return; + } + } + + // branch strategy + PriorityQueue pq = new PriorityQueue<>(c_remained.length, new myComp()); + + // step01: sort child node by hours + String c_start = route.get(route.size() - 1); + for (int i = 0; i < c_remained.length; i++) { + if (c_remained == null) { + continue; + } + + String c_end = c_remained[i]; + Integer hour = get_hour(c_start, c_end); + Country c = new Country(c_end, i, hour); + + pq.add(c); + } + + /** step02: just pick any child to continue each round **/ + while (true) { + if (pq.size() == 0) { + break; + } + + Country c = pq.poll(); + route.add(c.name); + c_remained[c.index] = null; + + branchbound_recursion(); + + route.remove(c.name); + c_remained[c.index] = c.name; + + } + } + + private int get_hour_total() { + if (route.size() == 0) + return 0; + int hour_total = 0; + String c_start = route.get(0); + String c_end = null; + for (int i = 1; i < route.size(); i++) { + c_end = route.get(i); + hour_total += get_hour(c_start, c_end); + + c_start = c_end; + } + + return hour_total; + } + + private void print_result(int hour_total) { + for (int i = 0; i < route.size(); i++) { + System.out.print(route.get(i)); + if (i != route.size() - 1) { + System.out.print("->"); + } + } + + System.out.println(" : " + hour_total); + } + + public static void main(String[] args) { + Travelplan_BranchBound tp = new Travelplan_BranchBound(); + tp.build_hour_table(); + tp.branchbound(); + } +} diff --git a/012_Banch_and_bound/Travelplan_BranchBound.md b/012_Banch_and_bound/Travelplan_BranchBound.md new file mode 100644 index 0000000..5ea494a --- /dev/null +++ b/012_Banch_and_bound/Travelplan_BranchBound.md @@ -0,0 +1,140 @@ +```java +String[] c_remained = { "NP", "IS", "CA", "UK", "US" }; // index 與 字串 映射表 +Integer[][] hours = { + { 0, 14, 15, 17, 16 }, + { 14, 0, 24, 8, 36 }, + { 15, 24, 0, 34, 4 }, + { 17, 8, 34, 0, 30 }, + { 16, 36, 4, 30, 0 }, +}; // 圖路徑的權重表 +; + +``` + +| | NP | IS | CA | UK | US | +| --- | --- | --- | --- | --- | --- | +| NP | 0 | 14 | 15 | 17 | 16 | +| IS | 14 | 0 | 24 | 8 | 36 | +| CA | 15 | 24 | 0 | 34 | 4 | +| UK | 17 | 8 | 34 | 0 | 30 | +| US | 16 | 36 | 4 | 30 | 0 | + + +## 起動遞迴函數 +```java +public void enumeration(Integer constraint_hour) { + String c_start = "NP"; + route.add(c_start); + c_remained[0] = null; + + enumeration_recursion(constraint_hour); +} +``` + +1. 紀錄起點到route +2. 將起點從國家陣列中清空 + +--- + +## 遞迴函數 +```java +Integer hour_best = null; + +private void enumeration_recursion(Integer constraint_hour) { + int hour_total = get_hour_total(); + + if (route.size() == 5) { + if (hour_best == null || hour_total < hour_best) { + hour_best = hour_total; + print_result(hour_total); + } else { + System.out.print("[X]: "); + print_result(hour_total); + } + } else { + // bound check + if (hour_best != null && hour_total >= hour_best) { + System.out.print("[bounded]: "); + print_result(hour_total); + return; + } + } + + // branch strategy + PriorityQueue pq = new PriorityQueue<>(c_remained.length, new myComp()); + + // step01: sort child node by hours + String c_start = route.get(route.size() - 1); + for (int i = 0; i < c_remained.length; i++) { + if (c_remained == null) { + continue; + } + + String c_end = c_remained[i]; + Integer hour = get_hour(c_start, c_end); + Country c = new Country(c_end, i, hour); + + pq.add(c); + } + + /** step02: just pick any child to continue each round **/ + while (true) { + if (pq.size() == 0) { + break; + } + + Country c = pq.poll(); + route.add(c.name); + c_remained[c.index] = null; + + branchbound_recursion(); + + route.remove(c.name); + c_remained[c.index] = c.name; + + } +} +``` + +1. 設一個變數紀錄最小時長 +2. 開始計算當前總時長 +3. 如果路由走到底開始判斷是否為比紀錄的最小時長還短 +4. 反之判斷當前總時長是否超過紀錄的最小時長,超時則結束遞迴 + ( Bound ) +5. 使用PriorityQueue儲存剩餘的國家,pq的poll必然由小到大 +6. 進入迴圈 +7. 取得剩餘的國家,添加到路由,並清空該國家 +8. 展開遞迴 +9. 遞迴結束,清調路由,並歸還國家 + ( Branch ) + +不會產生所有排列組合! + +且保證**當下最小值**優先取出即( Branch ),當走完時候會比較**是否為總體最小時長**則是( Bound ) + + + +--- + +## 計算距離 +```java +private int get_hour_total() { + if (route.size() == 0) + return 0; + int hour_total = 0; + String c_start = route.get(0); + String c_end = null; + for (int i = 1; i < route.size(); i++) { + c_end = route.get(i); + hour_total += get_hour(c_start, c_end); + + c_start = c_end; + } + + return hour_total; +} +``` + +1. 從路由開始取得第一個節點,慢慢往下尋找下一個端點 +2. 計算兩個位置時間 +3. 回傳加總的時間 \ No newline at end of file diff --git a/README.md b/README.md index 6a00ebf..d9269e1 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ 9. Greedy 10. Enumeration aka Brute Force 11. Backtracking + 12. Branch and bound 2. 樹系列 3. Hash系列 4. 平衡樹