Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optimizing a Traveling Salesman Algorithm (Time Traveler Algorithm)

I try to optimize a simple python algorithm I made that approximately solve the Traveling Salesman Problem :

import math
import random
import matplotlib.pyplot as plt
import datetime


#Distance between two point
def distance(point1, point2):
    return math.sqrt((point2[0]-point1[0])**2+(point2[1]-point1[1])**2)

#TSP TimeTraveler Algorithm
def TSP_TimeTraveler(Set_Points):
    print("Solving TSP")

    #For calculating execution time
    time_start = datetime.datetime.now()

    #Copy the set points
    points = Set_Points.copy()
    route = []

    #Take 3 points at random
    route.append(points.pop(random.randint(0,len(points)-1)))
    route.insert(0,points.pop(random.randint(0,len(points)-1)))
    route.insert(1,points.pop(random.randint(0,len(points)-1)))

    #Calulating the initial route length
    Length = distance(route[0],route[1]) + distance(route[1],route[-1]) + distance(route[-1],route[0])

    #Time Traveler Algorithm
    while len(points)>0 :
        print("Points left : ", len(points),'              ', end="\r")

        #Take a random point from the Set
        point = points.pop(random.randint(0,len(points)-1))

        ###############################################################################################################
        #### Finding the closest route segment by calculation all lengths posibilities and finding the minimum one ####
        ###############################################################################################################
        Set_Lengths = []
        for i in range(1,len(route)):
            #Set of Lengths when the point is on each route segment except the last one
            L = Length - distance(route[i-1],route[i]) + distance(route[i-1],point) + distance(point, route[i])
            Set_Lengths.append((i,L))

        #Adding the last length when the point is on the last segement
        L = Length - distance(route[-1],route[0]) + distance(route[-1],point) + distance(point, route[0])
        Set_Lengths.append((0,L))
        ###############################################################################################################
        ###############################################################################################################

        #Sorting the set of lengths
        Set_Lengths.sort(key=lambda k: k[1])

        #Inserting the point on the minimum length segment
        route.insert(Set_Lengths[0][0], point)

        #Updating the new route length
        Length = Set_Lengths[0][1]

    #Connecting the start point with the finish point
    route.append(route[0])

    #For calculating execution time
    time_end = datetime.datetime.now()
    delta = (time_end-time_start).total_seconds()

    print("Points left : ", len(points),' Done              ',)
    print("Execution time : ", delta, "secs")

    return route

#######################
#Testing the Algorithm#
#######################

#Size of the set
size = 2520

#Generating a set of random 2D points
points = []
for i in range(size):
    points.append([random.uniform(0, 100),random.uniform(0, 100)])

#Solve TSP
route = TSP_TimeTraveler(points)

#Plot the solution
plt.scatter(*zip(*points),s=5)
plt.plot(*zip(*route))
plt.axis('scaled')
plt.show()

The algorithm operate in 3 simple steps :

1/ First step I take 3 points at random from the points set and connect them as the initial route.

2/ Then each next step, I take a point at random from the set of points left. And try to find the closest segment of the route i have and connect it to it.

3/ I keep repeating step 2/ until the set of points left is empty.

Here is a gif of how the algorithm solve a set of 120 points : TimeTravelerAlgorithm.gif

I give it the name "Time Traveler" because it's operate like a greedy salesman algorithm. But instead traveling to the closest new city in the present, the greedy salesman time travel to the past to the closest city he had already visited and go visit that new city then continue his normal route.

The time traveler start a route of 3 cities, and the traveler add a new city each step in his past, until he reach a present where he visited all the cities and returned to his home city.

The algorithm give decent solutions fast for small set of points. Here is the execution time for each number of sets, all are made on a 2.6GHz dual-core Intel Core i5 processor Macbook :

  • 120 points in around 0.03 secs
  • 360 points in around 0.23 secs
  • 2520 points in around 10 secs
  • 10 000 points in around 3 mins
  • 100 000 points in around 5 hours (Solution Map)

The algorithm is far from being optimized, because in some cases it gives cross routes which is suboptimal. And It's all made in pure python. Maybe using numpy or some advance library or even GPU can speed up the program.

I want your review and help on how to optimize it. I try to approximately solve without cross routes for set of points that can be extremely large (from 1 million to 100 billions points).

PS: My algorithm and codes are open. People from internet, feel free to use it in any project or any research you have.

like image 959
Yoshi Takeshi Avatar asked Dec 31 '25 08:12

Yoshi Takeshi


1 Answers

Thanks for the comments. I re-implemented the algorithm using Objects, Sets and Linked list. I also removed the square root from distance function . Now the code look more clean :

import math
import random
import datetime
import matplotlib.pyplot as plt

#Distance between two point
def distance(point1, point2):
    return (point2[0]-point1[0])**2 + (point2[1]-point1[1])**2

#Distance between two point
class Node:
    def __init__(self, dataval=None):
        self.dataval = dataval
        self.nextval = None

class TSP_TimeTraveler():
    def __init__(self, dataval=None):
        self.count = 0
        self.position = None
        self.length = 0

    def get_position():
        return self.position

    def next_city():
        self.position = self.position.nextval
        return self.position

    #adding a city to the current route with Time Traveler Algorithm :
    def add_city(self, point):
        node = Node(point)
        if self.count <=0 :
            self.position = node
        elif self.count == 1 :
            node.nextval = self.position
            self.position.nextval = node
            self.length = 2*distance(self.position.dataval,node.dataval)
        else : 

            #Creating the traveler
            traveler = self.position

            c = traveler.dataval #current position
            n = traveler.nextval.dataval #next position

            #Calculating the length of adding the city to the path
            Min_L = self.length-distance(c,n)+distance(c,node.dataval)+distance(node.dataval,n)
            Min_Node = traveler

            traveler = traveler.nextval

            while traveler != self.position :
                c = traveler.dataval #current position
                n = traveler.nextval.dataval #next position

                #Calculating the length of adding the city to the path
                L = self.length-distance(c,n)+distance(c,node.dataval)+distance(node.dataval,n)

                #Searching the path to the of city with minimum length
                if L < Min_L :
                    Min_L = L
                    Min_Node = traveler

                traveler = traveler.nextval


            #Adding the city to the minimum path
            node.nextval = Min_Node.nextval
            Min_Node.nextval = node
            self.length = Min_L

        #Incrementing the number of city in the route
        self.count = self.count + 1

    #Get the list of the route
    def getRoute(self):
        result = []

        traveler = self.position
        result.append(traveler.dataval)

        traveler = traveler.nextval

        while traveler != self.position :
            result.append(traveler.dataval)
            traveler = traveler.nextval

        result.append(traveler.dataval)

        return result

    def Solve(self, Set_points):
        print("Solving TSP")

        #For calculating execution time
        time_start = datetime.datetime.now()

        #Copy the set points list
        points = Set_points.copy()

        #Transform the list into set
        points = set(tuple(i) for i in points)

        #Add 
        while len(points)>0 :
            print("Points left : ", len(points),'              ', end="\r")
            point = points.pop()
            self.add_city(point)

        result = self.getRoute()

        #For calculating execution time
        time_end = datetime.datetime.now()
        delta = (time_end-time_start).total_seconds()

        print("Points left : ", len(points),' Done              ',)
        print("Execution time : ", delta, "secs")

        return result

#######################
#Testing the Algorithm#
#######################

#Size of the set
size = 120

#Generating a set of random 2D points
points = []
for i in range(size):
    points.append((random.uniform(0, 100),random.uniform(0, 100)))

#Solve TSP
TSP = TSP_TimeTraveler()

route = TSP.Solve(points)

#Plot the solution
plt.scatter(*zip(*points),s=5)
plt.plot(*zip(*route))
plt.axis('scaled')
plt.show()

And using PyPy instead of normal python it runs alot faster :

  • 120 in around 0.03sec
  • 360 in around 0.05sec
  • 2520 in around 0.22sec
  • 10 000 in around 2sec
  • 100 000 in around 7min

The 100 000 case that took before 5 hours, now it's solved in 7min.

Next, I will try to implement a 2-opt with double linked list and KD-tree. So it can solve for large sets without crosses.

like image 127
Yoshi Takeshi Avatar answered Jan 02 '26 21:01

Yoshi Takeshi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!