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**
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:
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
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:
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()
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:
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
--- /dev/null
+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])
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():
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()