Skip to content

Commit

Permalink
Heuristic solver for tsp
Browse files Browse the repository at this point in the history
  • Loading branch information
philipandreadis committed Aug 27, 2020
1 parent 5109cca commit ca8067a
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 0 deletions.
69 changes: 69 additions & 0 deletions Algorithm_tests/graphtheory_tests/test_NN.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import unittest
import sys

# Import from different folder
sys.path.append("Algorithms/graphtheory/nearest-neighbor-tsp/")

import NearestNeighborTSP


class TestNN(unittest.TestCase):
def setUp(self):
self.G1 = [[0,3,-1],[3,0,1],[-1,1,0]]
self.correct_path1 = [0,1,2,0]

# No possible solution for this one so its a dead end
self.G2 = [[0, 2, -1,-1,-1], [2, 0,5,1,-1], [-1, 5, 0, -1, -1],[-1, 1, -1, 0, 3], [-1, -1, -1, 3, 0]]
self.correct_path2 = [0,1,3,4]

# No possible solution for this one so its a dead end
self.G3 = [[0, 2, -1,-1,-1], [2, 0,5,1,-1], [-1, 5, 0, -1, -1],[-1, 1, -1, 0, -1], [-1, -1, -1, -1, 0]]
self.correct_path3 = [0, 1, 3]

# Multiple possible solutions
self.G4 = [[0,1,1,1],[1,0,1,1],[1,1,0,1],[1,1,1,0]]
self.correct_path4 = [0, 1, 2, 3, 0]


# adjacency matrix of a graph for testing
adjMatrix = [[0,2,5,-1,3],[2,0,2,4,-1],[5,2,0,5,5],[-1,4,5,0,2],[3,-1,5,2,0]]
# correct rank of each node's neighbors
correctNeighbors = [[1,4,2],[0,2,3],[1,0,3,4],[4,1,2],[3,0,2]]


def test_0_rankNeighbors(self):
for i in range(0,4):
self.assertEqual(NearestNeighborTSP.rankNeighbors(i, self.adjMatrix), self.correctNeighbors[i], "Check if order is different.")


def test_1_nnTSP(self):
path=NearestNeighborTSP.nnTSP(self.adjMatrix)
# Test if path is null
self.assertIsNotNone(path,"Output is empty")
# Test if path is not complete
self.assertEqual(len(path),len(self.adjMatrix)+1,"Path in incomplete")


def test_linear_graph(self):
#print(NearestNeighbor.nnTSP(self.G2))
path = NearestNeighborTSP.nnTSP(self.G1)
self.assertEqual(path,self.correct_path1)


def test_simple_graph(self):
path = NearestNeighborTSP.nnTSP(self.G2)
self.assertEqual(path,self.correct_path2)


def test_disconnected_graph(self):
path = NearestNeighborTSP.nnTSP(self.G3)
self.assertEqual(path, self.correct_path3)


def test_complete_graph(self):
path = NearestNeighborTSP.nnTSP(self.G4)
self.assertEqual(path, self.correct_path4)

if __name__ == '__main__':
print("Running Nearest Neighbor TSP solver tests:")
unittest.main()
126 changes: 126 additions & 0 deletions Algorithms/graphtheory/nearest-neighbor-tsp/NearestNeighborTSP.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""
Author: Philip Andreadis
e-mail: [email protected]
This script implements a simple heuristic solver for the Traveling Salesman Problem.
It is not guaranteed that an optimal solution will be found.
Format of input text file must be as follows:
1st line - number of nodes
each next line is an edge and its respective weight
The program is executed via command line with the graph in the txt format as input.
"""

import time
import sys



"""
This function reads the text file and returns a 2d list which represents
the adjacency matrix of the given graph
"""
def parseGraph(path):
# Read number of vertices and create adjacency matrix
f = open(path, "r")
n = int(f.readline())
adjMatrix = [[-1 for i in range(n)] for j in range(n)]
#Fill adjacency matrix with the correct edges
for line in f:
edge = line.split(" ")
edge = list(map(int, edge))
adjMatrix[edge[0]][edge[1]] = edge[2]
adjMatrix[edge[1]][edge[0]] = edge[2]
for i in range(len(adjMatrix)):
adjMatrix[i][i] = 0
return adjMatrix



"""
Returns all the neighboring nodes of a node sorted based on the distance between them.
"""
def rankNeighbors(node,adj):
sortednn = {}
nList = []
for i in range(len(adj[node])):
if adj[node][i]>0:
sortednn[i] = adj[node][i]
sortednn = {k: v for k, v in sorted(sortednn.items(), key=lambda item: item[1])}
nList = list(sortednn.keys())
return nList


"""
Function implementing the logic of nearest neighbor TSP.
Generate two lists a and b, placing the starting node in list a and the rest in list b.
While b is not empty append to a the closest neighboring node of the last node in a and remove it from b.
Repeat until a full path has been added to a and b is empty.
Returns list a representing the shortest path of the graph.
"""
def nnTSP(adj):
nodes = list(range(0, len(adj)))
#print(nodes)
weight = 0
global length
# Starting node is 0
a = []
a.append(nodes[0])
b = nodes[1:]
while b:
# Take last placed node in a
last = a[-1]
# Find its nearest neighbor
sortedNeighbors = rankNeighbors(last,adj)
# If node being checked has no valid neighbors and the path is not complete a dead end is reached
if (not sortedNeighbors) and len(a)<len(nodes):
print("Dead end!")
return a
flag = True
# Find the neighbor that has not been visited
for n in sortedNeighbors:
if n not in a:
nextNode = n
flag = False
break
# If all neighbors of node have been already visited a dead end has been reached
if flag:
print("Dead end!")
return a
a.append(nextNode)
b.remove(nextNode)
# Add the weight of the edge to the total length of the path
weight = weight + adj[last][nextNode]
# Finishing node must be same as the starting node
weight = weight + adj[a[0]][a[-1]]
length = weight
a.append(a[0])
return a


if __name__ == "__main__":
# Import graph text file
filename = sys.argv[1]
print("~Nearest Neighbor~")
start = time.time()
graph = parseGraph(filename)
n = len(graph)
length = 0

# Print adjacency matrix
print("Adjacency matrix of input graph:")
for i in range(len(graph)):
print(graph[i])

start = time.time()

path = nnTSP(graph)

finish = time.time()-start

# Output
print("Path:",path)
print("Length",length)
print("Time:",finish)
9 changes: 9 additions & 0 deletions Algorithms/graphtheory/nearest-neighbor-tsp/graph.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
5
0 1 2
0 2 5
0 4 3
1 2 2
1 3 4
2 3 5
2 4 5
3 4 2

0 comments on commit ca8067a

Please sign in to comment.