From: Skullheadx <704277@pdsb.net> Date: Thu, 29 Dec 2022 02:19:32 +0000 (-0500) Subject: bad greedy X-Git-Url: http://git.skullheadx.com/about.html?a=commitdiff_plain;h=8e5d6d27df4fda097644a27968e5ff85ff304394;p=The-Traveling-Salesman-Problem.git bad greedy --- diff --git a/README.md b/README.md index c87c184..a5be25d 100644 --- 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** diff --git a/display.py b/display.py index a9c7ecd..c9aa16a 100644 --- a/display.py +++ b/display.py @@ -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() diff --git a/graph.py b/graph.py index e6f24fa..55670b3 100644 --- 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 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 39d8240..06f72e1 100644 --- 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()