Travelling salesman
One classic computation problem, the travelling salesman. Even if it’s quite simple to describe, it’s one of those hideous NP complete problems, the ones that are quite difficult to resolve, as the computation time needed grows greatly with the length of the data. On Programming Praxis they have proposed to resolve the problem using brute force, and using the closest neighbor (a simplification of the problem).
I’ve created this program, in Python.
import random
import itertools
import operator
import datetime
MAX_X = 100
MAX_Y = 100
def random_cities(number):
''' Generate a number of cities located on random places '''
cities = [ (random.randrange(0, MAX_X),
random.randrange(0, MAX_Y))
for i in range(number) ]
return cities
def path_lenght(path):
''' Get the lenght of a path '''
lenght = 0
for i in xrange(len(path) - 1):
# Add the distance between two cities
lenght += abs(complex(path[i][0], path[i][1])
- complex(path[i + 1][0], path[i + 1][1]))
return lenght
def find_path_bruteforce(cities):
''' Find the smallest path using brute force '''
lenghts = []
for path in itertools.permutations(cities, len(cities)):
# Get the length of the path, adding the returning point
total_path = path + (path[0],)
lenght = path_lenght(total_path)
lenghts.append((total_path, lenght))
# Get minimum
lenghts.sort(key=operator.itemgetter(1))
return lenghts[0]
def find_path_nearest(cities):
''' Find the closest neibour '''
lenghts = []
for city in cities:
lenght = 0
actual_cities = cities[:]
actual_city = actual_cities.pop(actual_cities.index(city))
path = [actual_city, ]
# Find nearest neibour
while actual_cities:
min_lenght = []
for next_city in actual_cities:
min_lenght.append((next_city, abs(complex(city[0], city[1])
- complex(next_city[0], next_city[1]))))
# Get closest neibor
min_lenght.sort(key=operator.itemgetter(1))
actual_city = min_lenght[0][0]
lenght += min_lenght[0][1]
actual_cities.pop(actual_cities.index(actual_city))
path.append(actual_city)
# Complete the trip with the first city
path.append(city)
lenghts.append((tuple(path), path_lenght(path)))
# Get minimum
lenghts.sort(key=operator.itemgetter(1))
return lenghts[0]
if __name__ == '__main__':
for i in range(3, 10):
print 'Number of cities: ', i
cities = random_cities(i)
time1 = datetime.datetime.now()
path2, lenght_neighbor = find_path_nearest(cities)
time2 = datetime.datetime.now()
print path2, lenght_neighbor
time_neighbor = time2 - time1
print 'Time neighbor: ', time_neighbor
time1 = datetime.datetime.now()
path1, lenght_brute = find_path_bruteforce(cities)
time2 = datetime.datetime.now()
print path1, lenght_brute
time_brute = time2 - time1
print 'Time brute force: ', time_brute
print 'Diff: ', float(lenght_neighbor - lenght_brute) / lenght_brute * 100, '%'
The time spend on each is brutally different, specially as the number of cities grow. With more than 9 cities, the time grows at some point when my computer has to take more than minutes to compute the brute force approach, while the neighbor approach will still be below the 1/100 of a second. The error also grows, being the same on small number of cities, but getting afar fast when the number of cities grow. At 9 cities, the error is about a 20-30%
