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?

Asked By: Aksel Frydenlund

||

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.

Answered By: גלעד ברקן

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
Answered By: Peter de Rivaz

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
Answered By: martineau

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)
Answered By: John