Search code examples
pythongraphpermutation

Find shortest distance, while visiting every node exactly once


For practicing reasons, I am working on Advent of Code 2015, and I am stuck at day 9. The goal is to find the shortest distance, while visiting every location within the graph exactly once. Every point is directly connected to each other and the end point must be different from the starting point. I have formulated a solution, but the final value is not correct, and I am not seeing the underlying problem.

First, I create a graph object with the locations and the distances. Then I collect every permutation of the locations into a list, and then I find and summarize the distances for each permutation. Finally, I print out the minimum distance value, which is the solution to the exercise.

The code:

from collections import defaultdict
from itertools import permutations

with open("input.txt") as file:
    input_ = file.read().split("\n")[:-1]

class Graph():
    def __init__(self):
        self.edges = defaultdict(list)
        self.weights = {}
    
    def add_edge(self, from_node, to_node, weight):
        self.edges[from_node].append(to_node)
        self.edges[to_node].append(from_node)
        self.weights[(from_node, to_node)] = weight
        self.weights[(to_node, from_node)] = weight

graph = Graph()

edges = [(i.split()[0], i.split()[2], int(i.split()[-1])) for i in input_]
for edge in edges:
    graph.add_edge(*edge)
    
loc_set = set([i[0] for i in edges])
routes = list(permutations(loc_set, len(loc_set)))

dists = []
for i in routes:
    print(i)
    dist_temp = []
    for k in range(len(i))[1:]:
        dist_temp.append(graph.weights[(i[k-1], i[k])])
    dists.append(sum(dist_temp))
    print(dist_temp)
    print(sum(dist_temp))
    
print(min(dists))

After getting an invalid value, I have manually checked some permutations and their corresponding distances, hence the prints in the code.

The input (copying and pasting it to notepad and saving it as input.txt should be working fine for my code):

Faerun to Tristram = 65
Faerun to Tambi = 129
Faerun to Norrath = 144
Faerun to Snowdin = 71
Faerun to Straylight = 137
Faerun to AlphaCentauri = 3
Faerun to Arbre = 149
Tristram to Tambi = 63
Tristram to Norrath = 4
Tristram to Snowdin = 105
Tristram to Straylight = 125
Tristram to AlphaCentauri = 55
Tristram to Arbre = 14
Tambi to Norrath = 68
Tambi to Snowdin = 52
Tambi to Straylight = 65
Tambi to AlphaCentauri = 22
Tambi to Arbre = 143
Norrath to Snowdin = 8
Norrath to Straylight = 23
Norrath to AlphaCentauri = 136
Norrath to Arbre = 115
Snowdin to Straylight = 101
Snowdin to AlphaCentauri = 84
Snowdin to Arbre = 96
Straylight to AlphaCentauri = 107
Straylight to Arbre = 14
AlphaCentauri to Arbre = 46

I am pretty sure, that there are much more refined solutions to the problem, and I am open to recommendations, since I am just a hobbyist. However, I would be very happy, if we'd be able to work out the shortcomings of my approach and make it work properly.


Solution

  • I have found the error thanks to Setonix's comment! Turns out, the approach is working (it is most probably nowhere near a nice implementation of TSP, as mentioned by Mark Ransom, but it is functional nonetheless!), I was only careless, when defining the set of locations.

    I have assumed that every location is present at least once in the beginning of the instruction strings. However, one location ("Arbre") was only present at the end of the instructions. Therefore, the graph was incomplete, hence the wrong outputs.

    As a quick fix, I have modified the code in the following way:

    loc_set = set([i[0] for i in edges])
    

    to

    loc_set = list(set([i[0] for i in edges]))
    loc_set.append("Arbre")
    

    Also, turns out, that incidentally the approach was good for this exercise, since the second part asks for the longest distance, which can be found via a single additional line of code at the end:

    print(max(dists))