I want to calculate the difference between two values in a "distance_table = []" with permutations, how can I use permutations correctly in this case?
Question:
I want to calculate the difference between two values in a distancetable which is read in from a file, a csv file with a number of cities with distances between them. In the .csv-file I have the first row with cities,
like this:
Barcelona;Belgrade;Berlin
The next rows are the distances between the cities, written like this:
0;1528.13;1497.61
1528.13;0;999.25
1497.61;999.25;0
For example, the distance from Barcelona to Barcelona is 0, (first number), Barcelona and Belgrade is 1528.13, (second), Belgrade and Berlin 999.25.. etc
I am trying to create an algorithm to search for the shortest path through all the cities in file like this. But I will need to use Python and probably permutations from itertools.
I don’t know how I can use permutations correctly so I can add together the distance from the different possibilites. How can I do this?
So I am importing permutations, csv, reading in the file, and and starting from here…
from itertools import permutations
import csv
# Read data file
distance_table = []
with open('european_cities.csv') as file:
reader = csv.reader(file,delimiter=';')
# First row is the city names
city_names = reader.next()
# The rest of the rows are the distance table
for row in reader:
distance_table.append([float(cell) for cell in row])
so now I can fore example see from the distance_table the distance between city A and city B like this:
distance_table[city_A][city_B]
How can I loop through all combinations in the permutation when I only want each city to appear once??
I want for example: cityA-cityB + cityB-cityC + cityC-cityA
and not: cityA-cityB + cityA-cityC + cityB-cityC + cityC-cityA etcetra . . .
I would like to use different algorithms here, firstly a stupid algorithm lie brute force to see the difference in time between this and a smarter algorithm, like the shortest-path algorithm.
But I don’t know how I can loop through the cities. How?
Answers:
If you think about it a little, you’ll see that a combination of the form, cityA-cityB + cityB-cityC + cityC-cityA
is actually just A,B,C
slightly modified. Can you think of an algorithm to convert an A,B,C
to cityA-cityB + cityB-cityC + cityC-cityA
? Perhaps you could combine that kind of modifying function with a python tool that generates unique combinations from your list.
One simple approach using the permutations function is:
N = len(city_names)
for perm in permutations(range(N)):
dist = sum(distance_table[perm[i]][perm[(i+1)%N]] for i in range(N))
print perm,dist
You say you want all permutations without any city to appearing twice, yet your example has the starting city (cityA) listed again at the end:
I want for example: cityA-cityB + cityB-cityC + cityC-cityA
So, assuming the first city appearing again at the end is actually what you meant, I think the following shows how you could generate the permutations of cities you want — if that assumption is wrong, simply remove the one line where the first city is duplicated.
In order to get differing total distances (with three it’s always the same), I added a fourth city and changed the output format so it’s more compact to better accommodate more cities.
Barcelona;Belgrade;Berlin;Brussels
0;1528.13;1497.61;1346.0
1528.13;0;999.25;1723.0
1497.61;999.25;0;764.0
1346.0;1723.0;764.0;0
Here’s the code:
from __future__ import print_function
import csv
import functools
try:
from itertools import izip, imap
except ImportError: # Python 3
izip = zip
imap = map
from itertools import permutations, repeat
# Create a distance dictionary from csv data file with entries like this:
# (city_a, city_b): float(distance-between-city_a-and-city_b)
# for all pairs of city names in the file.
data_filename = 'european_cities.csv'
dist_dict = {}
with open(data_filename, 'r') as file:
reader = csv.reader(file, delimiter=';')
cities = next(reader) # header row
num_cities = len(cities)
for city in cities: # should be a row of distances for each city
from_city_iter = repeat(city, num_cities)
dist_dict.update((pair for pair in izip(izip(from_city_iter, cities),
imap(float, next(reader)))
if pair[1])) # skip 0 distances (city_a == city_b)
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2,s3), ..."
a, b = iter(iterable), iter(iterable)
next(b) # advance second iterator one iteration
return izip(a, b)
for tour in permutations(cities, len(cities)):
tour += (tour[0],) # make round trip by appending starting city
route = '->'.join(tour)
dist = sum(dist_dict[city_a, city_b] for city_a, city_b in pairwise(tour))
print('{:^49}: {:,}'.format(route, dist))
Output:
Barcelona->Belgrade->Berlin->Brussels->Barcelona : 4,637.38
Barcelona->Belgrade->Brussels->Berlin->Barcelona : 5,512.74
Barcelona->Berlin->Belgrade->Brussels->Barcelona : 5,565.86
Barcelona->Berlin->Brussels->Belgrade->Barcelona : 5,512.74
Barcelona->Brussels->Belgrade->Berlin->Barcelona : 5,565.86
Barcelona->Brussels->Berlin->Belgrade->Barcelona : 4,637.38
Belgrade->Barcelona->Berlin->Brussels->Belgrade : 5,512.74
Belgrade->Barcelona->Brussels->Berlin->Belgrade : 4,637.38
Belgrade->Berlin->Barcelona->Brussels->Belgrade : 5,565.86
Belgrade->Berlin->Brussels->Barcelona->Belgrade : 4,637.38
Belgrade->Brussels->Barcelona->Berlin->Belgrade : 5,565.86
Belgrade->Brussels->Berlin->Barcelona->Belgrade : 5,512.74
Berlin->Barcelona->Belgrade->Brussels->Berlin : 5,512.74
Berlin->Barcelona->Brussels->Belgrade->Berlin : 5,565.86
Berlin->Belgrade->Barcelona->Brussels->Berlin : 4,637.38
Berlin->Belgrade->Brussels->Barcelona->Berlin : 5,565.86
Berlin->Brussels->Barcelona->Belgrade->Berlin : 4,637.38
Berlin->Brussels->Belgrade->Barcelona->Berlin : 5,512.74
Brussels->Barcelona->Belgrade->Berlin->Brussels : 4,637.38
Brussels->Barcelona->Berlin->Belgrade->Brussels : 5,565.86
Brussels->Belgrade->Barcelona->Berlin->Brussels : 5,512.74
Brussels->Belgrade->Berlin->Barcelona->Brussels : 5,565.86
Brussels->Berlin->Barcelona->Belgrade->Brussels : 5,512.74
Brussels->Berlin->Belgrade->Barcelona->Brussels : 4,637.38
How about this solution:
from itertools import permutations
import csv
sums = []
for p in permutations(distance_table[0], 2):
sums.append(sum(p))
for s in sums:
print(s)
I want to calculate the difference between two values in a distancetable which is read in from a file, a csv file with a number of cities with distances between them. In the .csv-file I have the first row with cities,
like this:
Barcelona;Belgrade;Berlin
The next rows are the distances between the cities, written like this:
0;1528.13;1497.61
1528.13;0;999.25
1497.61;999.25;0
For example, the distance from Barcelona to Barcelona is 0, (first number), Barcelona and Belgrade is 1528.13, (second), Belgrade and Berlin 999.25.. etc
I am trying to create an algorithm to search for the shortest path through all the cities in file like this. But I will need to use Python and probably permutations from itertools.
I don’t know how I can use permutations correctly so I can add together the distance from the different possibilites. How can I do this?
So I am importing permutations, csv, reading in the file, and and starting from here…
from itertools import permutations
import csv
# Read data file
distance_table = []
with open('european_cities.csv') as file:
reader = csv.reader(file,delimiter=';')
# First row is the city names
city_names = reader.next()
# The rest of the rows are the distance table
for row in reader:
distance_table.append([float(cell) for cell in row])
so now I can fore example see from the distance_table the distance between city A and city B like this:
distance_table[city_A][city_B]
How can I loop through all combinations in the permutation when I only want each city to appear once??
I want for example: cityA-cityB + cityB-cityC + cityC-cityA
and not: cityA-cityB + cityA-cityC + cityB-cityC + cityC-cityA etcetra . . .
I would like to use different algorithms here, firstly a stupid algorithm lie brute force to see the difference in time between this and a smarter algorithm, like the shortest-path algorithm.
But I don’t know how I can loop through the cities. How?
If you think about it a little, you’ll see that a combination of the form, cityA-cityB + cityB-cityC + cityC-cityA
is actually just A,B,C
slightly modified. Can you think of an algorithm to convert an A,B,C
to cityA-cityB + cityB-cityC + cityC-cityA
? Perhaps you could combine that kind of modifying function with a python tool that generates unique combinations from your list.
One simple approach using the permutations function is:
N = len(city_names)
for perm in permutations(range(N)):
dist = sum(distance_table[perm[i]][perm[(i+1)%N]] for i in range(N))
print perm,dist
You say you want all permutations without any city to appearing twice, yet your example has the starting city (cityA) listed again at the end:
I want for example: cityA-cityB + cityB-cityC + cityC-cityA
So, assuming the first city appearing again at the end is actually what you meant, I think the following shows how you could generate the permutations of cities you want — if that assumption is wrong, simply remove the one line where the first city is duplicated.
In order to get differing total distances (with three it’s always the same), I added a fourth city and changed the output format so it’s more compact to better accommodate more cities.
Barcelona;Belgrade;Berlin;Brussels
0;1528.13;1497.61;1346.0
1528.13;0;999.25;1723.0
1497.61;999.25;0;764.0
1346.0;1723.0;764.0;0
Here’s the code:
from __future__ import print_function
import csv
import functools
try:
from itertools import izip, imap
except ImportError: # Python 3
izip = zip
imap = map
from itertools import permutations, repeat
# Create a distance dictionary from csv data file with entries like this:
# (city_a, city_b): float(distance-between-city_a-and-city_b)
# for all pairs of city names in the file.
data_filename = 'european_cities.csv'
dist_dict = {}
with open(data_filename, 'r') as file:
reader = csv.reader(file, delimiter=';')
cities = next(reader) # header row
num_cities = len(cities)
for city in cities: # should be a row of distances for each city
from_city_iter = repeat(city, num_cities)
dist_dict.update((pair for pair in izip(izip(from_city_iter, cities),
imap(float, next(reader)))
if pair[1])) # skip 0 distances (city_a == city_b)
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2,s3), ..."
a, b = iter(iterable), iter(iterable)
next(b) # advance second iterator one iteration
return izip(a, b)
for tour in permutations(cities, len(cities)):
tour += (tour[0],) # make round trip by appending starting city
route = '->'.join(tour)
dist = sum(dist_dict[city_a, city_b] for city_a, city_b in pairwise(tour))
print('{:^49}: {:,}'.format(route, dist))
Output:
Barcelona->Belgrade->Berlin->Brussels->Barcelona : 4,637.38
Barcelona->Belgrade->Brussels->Berlin->Barcelona : 5,512.74
Barcelona->Berlin->Belgrade->Brussels->Barcelona : 5,565.86
Barcelona->Berlin->Brussels->Belgrade->Barcelona : 5,512.74
Barcelona->Brussels->Belgrade->Berlin->Barcelona : 5,565.86
Barcelona->Brussels->Berlin->Belgrade->Barcelona : 4,637.38
Belgrade->Barcelona->Berlin->Brussels->Belgrade : 5,512.74
Belgrade->Barcelona->Brussels->Berlin->Belgrade : 4,637.38
Belgrade->Berlin->Barcelona->Brussels->Belgrade : 5,565.86
Belgrade->Berlin->Brussels->Barcelona->Belgrade : 4,637.38
Belgrade->Brussels->Barcelona->Berlin->Belgrade : 5,565.86
Belgrade->Brussels->Berlin->Barcelona->Belgrade : 5,512.74
Berlin->Barcelona->Belgrade->Brussels->Berlin : 5,512.74
Berlin->Barcelona->Brussels->Belgrade->Berlin : 5,565.86
Berlin->Belgrade->Barcelona->Brussels->Berlin : 4,637.38
Berlin->Belgrade->Brussels->Barcelona->Berlin : 5,565.86
Berlin->Brussels->Barcelona->Belgrade->Berlin : 4,637.38
Berlin->Brussels->Belgrade->Barcelona->Berlin : 5,512.74
Brussels->Barcelona->Belgrade->Berlin->Brussels : 4,637.38
Brussels->Barcelona->Berlin->Belgrade->Brussels : 5,565.86
Brussels->Belgrade->Barcelona->Berlin->Brussels : 5,512.74
Brussels->Belgrade->Berlin->Barcelona->Brussels : 5,565.86
Brussels->Berlin->Barcelona->Belgrade->Brussels : 5,512.74
Brussels->Berlin->Belgrade->Barcelona->Brussels : 4,637.38
How about this solution:
from itertools import permutations
import csv
sums = []
for p in permutations(distance_table[0], 2):
sums.append(sum(p))
for s in sums:
print(s)