Search code examples
pythonalgorithmcsvcombinationspermutation

I want to calculate the difference between two values in a "distance_table = []" with permutations, how can I use permutations correctly in this case?


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?


Solution

  • 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