From: Skullheadx <94652084+Skullheadx@users.noreply.github.com> Date: Thu, 29 Dec 2022 04:20:21 +0000 (-0500) Subject: greedy heuristic clean up + explanation X-Git-Url: http://git.skullheadx.com/nixos/static/projects.html?a=commitdiff_plain;h=707b55b3d22c4c86272c4e5d8d4232df028bf317;p=The-Traveling-Salesman-Problem.git greedy heuristic clean up + explanation --- diff --git a/README.md b/README.md index a5be25d..07822bd 100644 --- a/README.md +++ b/README.md @@ -82,3 +82,5 @@ Using these new approximation ratios, we can now determine how effective differe -------------------------------------------------------------------------------------------------------------- **Method 3: Greedy Heuristic** + +If we use an approach similar to how we created the Minimum Spanning Tree, we can apply similar logic to create a good heuristic. In each iteration, we determine the shortest edge distance to add to the route, however we must follow the rules that each town is connected to exactly 2 other towns and by adding an edge, we do not create a cycle of length smaller than n. \ No newline at end of file diff --git a/display.py b/display.py index c9aa16a..80aefc7 100644 --- a/display.py +++ b/display.py @@ -1,5 +1,6 @@ import pygame import math +from graph import linker pygame.init() @@ -75,7 +76,7 @@ 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: + def __init__(self, path: str, route: list, mst=None, one_tree=None, removed_vertex=None, mode="direct") -> None: with open(path, "r") as f: contents = f.read().split("\n") if contents[-1] == "": @@ -86,14 +87,17 @@ class Display: self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT)) self.route = route - # self.salesman = Salesman(self.route) + if mode == "points": + self.route = linker(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 @@ -105,31 +109,31 @@ 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: - pygame.draw.circle(self.screen,BLUE,self.removed_vertex,15) + pygame.draw.circle(self.screen, BLUE, self.removed_vertex, 15) for line in self.one_tree: # One Tree start, end = line pygame.draw.line(self.screen, GREEN, start, end, 12) if self.mst is not None: - for i,line in enumerate(self.mst): # Minimum Spanning Tree + for i, line in enumerate(self.mst): # Minimum Spanning Tree start, end = line - 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))) + 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 i,node in enumerate(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) + # 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() delta = clock.tick(60) / 1000 # Seconds - # pygame.image.save(self.screen, "NN_Heuristic+OneTree.png") + # pygame.image.save(self.screen, "Greedy_Heuristic+OneTree.png") # quit() diff --git a/graph.py b/graph.py index 55670b3..141fb64 100644 --- a/graph.py +++ b/graph.py @@ -69,9 +69,9 @@ def calculate_route(route: list, mode="direct") -> float: elif mode == "points": d = 0.0 for i in route: - start,end = i + start, end = i - d += distance(start,end) + d += distance(start, end) return d @@ -86,10 +86,11 @@ 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, mode="direct") -> None: +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) + num_nodes = (len(route) - 1) elif mode == "points": num_nodes = len(route) print( @@ -146,7 +147,7 @@ def find_lower_bound(graph: list): rm_vertex = None for removed_vertex_index in range(len(graph)): - one_tree_distance,one_tree= find_one_tree(graph,removed_vertex_index) + one_tree_distance, one_tree = find_one_tree(graph, removed_vertex_index) if lower_bound is None or one_tree_distance > lower_bound: lower_bound = one_tree_distance @@ -178,3 +179,39 @@ def find_MST(graph: list): q.put((distance(town, end), end, town)) return mst_distance, mst + + +def linker(points): + p = points[:] + direct = [p[0][0]] + head = p[0][0] + current = p[0] + + graph = dict() + + for pair in p: + start, end = pair + if start in graph: + graph[start].append(end) + else: + graph[start] = [end] + if end in graph: + graph[end].append(start) + else: + graph[end] = [start] + seen = set() + seen.add(head) + while True: + start, end = current + direct.append(end) + + a, b = graph[end] + if a == start: + current = (end, b) + else: + current = (end, a) + + if end == head: + break + + return direct diff --git a/greedy.py b/greedy.py index 419d9a1..499f903 100644 --- a/greedy.py +++ b/greedy.py @@ -1,18 +1,48 @@ from graph import distance from queue import PriorityQueue +from copy import deepcopy + def greedy(graph: list): - seen = set() route = [] q = PriorityQueue() - for town1 in graph: - for town2 in graph: + for i, town1 in enumerate(graph): + for town2 in graph[i:]: if town1 != town2: q.put((distance(town1, town2), town1, town2)) + def detect_cycle(start, end, target, gr, seen): + if start == target: + gr = deepcopy(gr) + 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 + + g = {town: [] for town in graph} + while not q.empty() and len(route) < len(graph): - current = q.get() + d, start, end = q.get() + + if len(g[start]) >= 2 or len(g[end]) >= 2: + continue + if len(route) < len(graph)-1 and detect_cycle(start, end, start, g, set()): + continue + + route.append((start, end)) + g[start].append(end) + g[end].append(start) - if + return route diff --git a/main.py b/main.py index 06f72e1..14e8b55 100644 --- a/main.py +++ b/main.py @@ -22,7 +22,7 @@ def main(): print("The file does not exist") if CREATE_NEW_GRAPHS: - graph, filename = create(GRAPH_PATH, 640, 640, 15) + graph, filename = create(GRAPH_PATH, 640, 640, 100) else: filename = "graph1.txt" graph = read(GRAPH_PATH, filename) @@ -30,8 +30,8 @@ def main(): 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 = greedy(graph) # 100 nodes in 0.5762094999663532 seconds. Distance = 6,270.568142156188 - print(f"{route=}") + route = greedy(graph) # 100 nodes in 0.1383088999427855 seconds. Distance = 5,523.211501332208 OTLB: 4, + # 344.881943246125 Approx. 27.119944188995277% route_time_end = perf_counter() # MST_distance, MST = find_MST(graph) @@ -44,8 +44,8 @@ def main(): 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), [], mst=route, one_tree=None, - removed_vertex=removed_vertex) + display = Display(os.path.join(GRAPH_PATH, filename), route, mst=None, one_tree=one_tree, + removed_vertex=removed_vertex, mode="points") display.show()