]> Skullheadx's Git Forge - The-Traveling-Salesman-Problem.git/commitdiff
bad greedy
authorSkullheadx <704277@pdsb.net>
Thu, 29 Dec 2022 02:19:32 +0000 (21:19 -0500)
committerSkullheadx <704277@pdsb.net>
Thu, 29 Dec 2022 02:19:32 +0000 (21:19 -0500)
README.md
display.py
graph.py
greedy.py [new file with mode: 0644]
main.py

index c87c184b32a93c292a15a68453b6604c149df170..a5be25d06cd5e845f5f38c30d9f5a56a5f3dc0c5 100644 (file)
--- a/README.md
+++ b/README.md
@@ -76,3 +76,9 @@ In this example, the `One Tree Lower Bound Cost = 1,734.686` and the `Tour Cost
 
 
 In this case, the approximation ratio is `15.0%` of the One Tree Lower Bound Cost! This is a significantly better approximation of how close we are to the optimal tour cost compared to the approximation ratio using the MST.  
+
+
+Using these new approximation ratios, we can now determine how effective different heuristic approaches are to the optimal solution.
+
+--------------------------------------------------------------------------------------------------------------
+**Method 3: Greedy Heuristic**
index a9c7ecd4eeec7a3c4e2ab56749162199de4f11a6..c9aa16a2de41538784472f75d7e998f70e4f8b08 100644 (file)
@@ -73,6 +73,7 @@ class Salesman:
 
 class Display:
     pygame.display.set_caption("Traveling Salesman Problem")
+    font = pygame.font.SysFont("arial", 20)
 
     def __init__(self, path: str, route: list, mst=None, one_tree=None, removed_vertex=None) -> None:
         with open(path, "r") as f:
@@ -85,14 +86,14 @@ class Display:
         self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT))
 
         self.route = route
-        self.salesman = Salesman(self.route)
+        self.salesman = Salesman(self.route)
 
         self.mst = mst
         self.one_tree = one_tree
         self.removed_vertex = removed_vertex
 
-    def update(self, delta: float) -> None:
-        self.salesman.update(delta)
+    def update(self, delta: float) -> None:
+        self.salesman.update(delta)
 
     def show(self) -> None:
         is_running = True
@@ -104,7 +105,7 @@ class Display:
                 if event.type == pygame.QUIT:
                     is_running = False
 
-            self.salesman.update(delta)
+            self.salesman.update(delta)
 
             self.screen.fill(WHITE)
             if self.one_tree is not None:
@@ -113,15 +114,19 @@ class Display:
                     start, end = line
                     pygame.draw.line(self.screen, GREEN, start, end, 12)
             if self.mst is not None:
-                for line in self.mst:  # Minimum Spanning Tree
+                for i,line in enumerate(self.mst):  # Minimum Spanning Tree
                     start, end = line
-                    pygame.draw.line(self.screen, RED, start, end, 8)
+                    pygame.draw.line(self.screen,ORANGE, start, end, 4)
+                    text = self.font.render(str(i), True, (0,0,0))
+                    self.screen.blit(text, text.get_rect(center=((start[0]+end[0])/2,(end[1]+start[1])/2)))
 
             if len(self.route) > 1:
                 pygame.draw.lines(self.screen, BLUE, True, self.route, 3)  # Route
 
-            for node in self.nodes:
+            for i,node in enumerate(self.nodes):
                 node.draw(self.screen)
+                text = self.font.render(str((node.position)), True, (0, 0, 0))
+                self.screen.blit(text, text.get_rect(center=node.position))
             # self.salesman.draw(self.screen)
 
             pygame.display.update()
index e6f24fa9e603ae4498063bfa51deb47321a390b6..55670b386bd98b4a70a68df814ce7fb58687dcb5 100644 (file)
--- a/graph.py
+++ b/graph.py
@@ -56,15 +56,23 @@ def get_distances(graph: list) -> dict:
     return distances
 
 
-def calculate_route(route: list) -> float:
-    town1 = route[0]
-    town2 = route[-1]
-    d = distance(town1, town2)
-    for i, node in enumerate(route[:-1]):
-        town2 = route[i + 1]
-        d += distance(town1, town2)
-        town1 = town2
-    return d
+def calculate_route(route: list, mode="direct") -> float:
+    if mode == "direct":
+        town1 = route[0]
+        town2 = route[-1]
+        d = distance(town1, town2)
+        for i, node in enumerate(route[:-1]):
+            town2 = route[i + 1]
+            d += distance(town1, town2)
+            town1 = town2
+        return d
+    elif mode == "points":
+        d = 0.0
+        for i in route:
+            start,end = i
+
+            d += distance(start,end)
+        return d
 
 
 def find_shortest_route(routes: list) -> list:
@@ -78,15 +86,19 @@ def find_shortest_route(routes: list) -> list:
     return shortest_route
 
 
-def print_info(route: list, time: float, method_name: str, one_tree: float, one_tree_time: float, r=0) -> None:
-    d = calculate_route(route)
+def print_info(route: list, time: float, method_name: str, one_tree: float, one_tree_time: float, r=0, mode="direct") -> None:
+    d = calculate_route(route, mode)
+    if mode == "direct":
+        num_nodes =  (len(route) - 1)
+    elif mode == "points":
+        num_nodes = len(route)
     print(
         f"""
 Traveling Salesman Problem
 Method Used: {method_name}
 Approximation ratio: {round(d / one_tree * 100 - 100, r)}%
 Time Used: {round(time, r):,} seconds
-Number of Nodes: {(len(route) - 1):,}
+Number of Nodes: {num_nodes:,}
 Distance: {round(d, r):,}
 One Tree Lower Bound: {round(one_tree, r):,}
 One Tree Time Used: {round(one_tree_time, r):,} seconds
diff --git a/greedy.py b/greedy.py
new file mode 100644 (file)
index 0000000..cc93922
--- /dev/null
+++ b/greedy.py
@@ -0,0 +1,130 @@
+from queue import PriorityQueue
+from graph import distance
+from copy import deepcopy
+
+
+def greedy(graph: list):
+    lines = []
+    distances = []
+    for town1 in graph:
+        for town2 in graph:
+            if town1 != town2:
+                distances.append((distance(town1, town2), town1, town2))
+    distances.sort()
+
+    g = {town: [] for town in graph}
+
+    def detect_cycle(start, end, target, gr, seen):
+        if start == target:
+            gr = gr.copy()
+            gr[start].append(end)
+            gr[end].append(start)
+
+        if end == target or start in seen:
+            return True
+
+        seen.add(start)
+
+        for x in gr[end]:
+            if x != start:
+                t = detect_cycle(end, x, target, gr, seen)
+                if t:
+                    return t
+        return False
+
+    seen = set()
+
+    for i in range(len(distances)):
+        if len(lines) == len(graph):
+            break
+
+        d, start, end = distances[i]
+
+        if (start, end) in seen:
+            continue
+        seen.add((start, end))
+        seen.add((end, start))
+
+        if len(lines) < len(graph) and detect_cycle(start, end, start, deepcopy(g), set()):
+            continue
+
+        lines.append((start, end))
+
+        g[start].append(end)
+        g[end].append(start)
+
+    print(tuple(find_missing(lines)))
+    # lines.append(tuple(find_missing(lines)))
+
+    return lines
+
+def find_missing(lines):
+
+    counter = dict()
+
+    for pair in lines:
+        start, end = pair
+        if start in counter:
+            counter[start] += 1
+        else:
+            counter[start] = 1
+        if end in counter:
+            counter[end] += 1
+        else:
+            counter[end] = 1
+
+    missing = []
+    for i in counter:
+        if counter[i] == 1:
+            missing.append(i)
+    return missing
+
+
+# def linker():
+#     def find_index(l, val):
+#         for i, v in enumerate(l):
+#             print(i, v[0], val)
+#             if val == v[0]:
+#                 return i
+#
+#     starts = [i[0] for i in lines]
+#     ends = [i[1] for i in lines]
+#     s = set()
+#     for i in lines:
+#         print(i)
+#     print(len(lines))
+#     print()
+#     print(lines[0])
+#     print()
+#     head = lines[0]
+#     current = head[:]
+#     route = [head[0], head[1]]
+#     while True:
+#         if current[1] in starts:
+#             x = starts.index(current[1])
+#             current = lines[x]
+#             y = 1
+#
+#         elif current[1] in ends:
+#             x = ends.index(current[1])
+#             current = lines[x]
+#             y = 0
+#
+#         elif current[0] in starts:
+#             x = starts.index(current[0])
+#             current = lines[x]
+#             y= 1
+#
+#         elif current[0] in starts:
+#             x = ends.index(current[0])
+#             current = lines[x]
+#             y = 0
+#         else:
+#             break
+#         print(current)
+#         # del lines[x]
+#         del starts[x]
+#         del ends[x]
+#         if current[y] not in s:
+#             route.append(current[y])
+#         s.add(current[y])
diff --git a/main.py b/main.py
index 39d8240a1518ea14871a8959df65c14799d05c8a..06f72e18e3a6fea6cb65f8ebf6e0d6f8b1b3260b 100644 (file)
--- a/main.py
+++ b/main.py
@@ -2,11 +2,12 @@ from graph import create, read, print_info, find_MST, find_one_tree, find_lower_
 from display import Display
 from brute_force import brute_force
 from nearest_neighbor import nearest_neighbor
+from greedy import greedy
 from time import perf_counter
 import os
 
 GRAPH_PATH = "graphs/"
-CREATE_NEW_GRAPHS = False
+CREATE_NEW_GRAPHS = True
 
 
 def main():
@@ -21,27 +22,30 @@ def main():
                     print("The file does not exist")
 
     if CREATE_NEW_GRAPHS:
-        graph, filename = create(GRAPH_PATH, 640, 640, 10)
+        graph, filename = create(GRAPH_PATH, 640, 640, 15)
     else:
         filename = "graph1.txt"
         graph = read(GRAPH_PATH, filename)
 
     route_time_start = perf_counter()
     # route = brute_force(graph)  # 10 nodes in 85.042 seconds. Optimal = 2,262.29
-    route = nearest_neighbor(graph)  # 100 nodes in 0.5762094999663532 seconds. Distance = 6,270.568142156188
+    # route = nearest_neighbor(graph)  # 100 nodes in 0.5762094999663532 seconds. Distance = 6,270.568142156188
+    route = greedy(graph)  # 100 nodes in 0.5762094999663532 seconds. Distance = 6,270.568142156188
+    print(f"{route=}")
     route_time_end = perf_counter()
 
-    MST_distance, MST = find_MST(graph)
-    print("MST_DISTANCE:", MST_distance)
+    MST_distance, MST = find_MST(graph)
+    print("MST_DISTANCE:", MST_distance)
     one_tree_time_start = perf_counter()
-    one_tree_distance, one_tree = find_one_tree(graph)
+    one_tree_distance, one_tree = find_one_tree(graph)
     lower_bound, one_tree, removed_vertex = find_lower_bound(graph)
     one_tree_time_end = perf_counter()
 
-    print_info(route, route_time_end - route_time_start, "NN Heuristic", lower_bound,
-               one_tree_time_end - one_tree_time_start, r=3000)
+    print_info(route, route_time_end - route_time_start, "Greedy Heuristic", lower_bound,
+               one_tree_time_end - one_tree_time_start, r=3000, mode="points")
 
-    display = Display(os.path.join(GRAPH_PATH, filename), route, mst=None, one_tree=one_tree, removed_vertex = removed_vertex)
+    display = Display(os.path.join(GRAPH_PATH, filename), [], mst=route, one_tree=None,
+                      removed_vertex=removed_vertex)
     display.show()