--------------------------------------------------------------------------------------------------------------
**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
import pygame
import math
+from graph import linker
pygame.init()
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] == "":
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
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()
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
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(
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
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
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
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)
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)
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()