|
| 1 | +def tsp(data): |
| 2 | + length = 0 |
| 3 | + visited = [False] * len(data) |
| 4 | + paths = [] |
| 5 | + |
| 6 | + G = build_graph(data) |
| 7 | + print("Graph: ", G) |
| 8 | + |
| 9 | + MSTree = minimum_spanning_tree(G) |
| 10 | + print("MSTree: ", MSTree) |
| 11 | + |
| 12 | + # find odd vertexes |
| 13 | + odd_vertexes = find_odd_vertexes(MSTree) |
| 14 | + print("Odd vertexes in MSTree: ", odd_vertexes) |
| 15 | + |
| 16 | + # add minimum weight matching edges to MST |
| 17 | + minimum_weight_matching(MSTree, G, odd_vertexes) |
| 18 | + print("Minimum weight matching: ", MSTree) |
| 19 | + |
| 20 | + |
| 21 | +def get_length(x1, y1, x2, y2): |
| 22 | + return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** (1 / 2) |
| 23 | + |
| 24 | + |
| 25 | +def build_graph(data): |
| 26 | + graph = {} |
| 27 | + for this in range(len(data)): |
| 28 | + for another_point in range(len(data)): |
| 29 | + if this != another_point: |
| 30 | + if this not in graph: |
| 31 | + graph[this] = {} |
| 32 | + |
| 33 | + graph[this][another_point] = get_length(data[this][0], data[this][1], data[another_point][0], |
| 34 | + data[another_point][1]) |
| 35 | + |
| 36 | + return graph |
| 37 | + |
| 38 | + |
| 39 | +class UnionFind: |
| 40 | + """Union-find data structure. |
| 41 | +
|
| 42 | + Each unionFind instance X maintains a family of disjoint sets of |
| 43 | + hashable objects, supporting the following two methods: |
| 44 | +
|
| 45 | + - X[item] returns a name for the set containing the given item. |
| 46 | + Each set is named by an arbitrarily-chosen one of its members; as |
| 47 | + long as the set remains unchanged it will keep the same name. If |
| 48 | + the item is not yet part of a set in X, a new singleton set is |
| 49 | + created for it. |
| 50 | +
|
| 51 | + - X.union(item1, item2, ...) merges the sets containing each item |
| 52 | + into a single larger set. If any item is not yet part of a set |
| 53 | + in X, it is added to X as one of the members of the merged set. |
| 54 | + """ |
| 55 | + |
| 56 | + def __init__(self): |
| 57 | + """Create a new empty union-find structure.""" |
| 58 | + self.weights = {} |
| 59 | + self.parents = {} |
| 60 | + |
| 61 | + def __getitem__(self, object): |
| 62 | + """Find and return the name of the set containing the object.""" |
| 63 | + |
| 64 | + # check for previously unknown object |
| 65 | + if object not in self.parents: |
| 66 | + self.parents[object] = object |
| 67 | + self.weights[object] = 1 |
| 68 | + return object |
| 69 | + |
| 70 | + # find path of objects leading to the root |
| 71 | + path = [object] |
| 72 | + root = self.parents[object] |
| 73 | + while root != path[-1]: |
| 74 | + path.append(root) |
| 75 | + root = self.parents[root] |
| 76 | + |
| 77 | + # compress the path and return |
| 78 | + for ancestor in path: |
| 79 | + self.parents[ancestor] = root |
| 80 | + return root |
| 81 | + |
| 82 | + def __iter__(self): |
| 83 | + """Iterate through all items ever found or unioned by this structure.""" |
| 84 | + return iter(self.parents) |
| 85 | + |
| 86 | + def union(self, *objects): |
| 87 | + """Find the sets containing the objects and merge them all.""" |
| 88 | + roots = [self[x] for x in objects] |
| 89 | + heaviest = max([(self.weights[r], r) for r in roots])[1] |
| 90 | + for r in roots: |
| 91 | + if r != heaviest: |
| 92 | + self.weights[heaviest] += self.weights[r] |
| 93 | + self.parents[r] = heaviest |
| 94 | + |
| 95 | + |
| 96 | +def minimum_spanning_tree(G): |
| 97 | + tree = [] |
| 98 | + subtrees = UnionFind() |
| 99 | + for W, u, v in sorted((G[u][v], u, v) for u in G for v in G[u]): |
| 100 | + if subtrees[u] != subtrees[v]: |
| 101 | + tree.append((u, v, W)) |
| 102 | + subtrees.union(u, v) |
| 103 | + |
| 104 | + return tree |
| 105 | + |
| 106 | + |
| 107 | +def find_odd_vertexes(MST): |
| 108 | + tmp_g = {} |
| 109 | + vertexes = [] |
| 110 | + for edge in MST: |
| 111 | + if edge[0] not in tmp_g: |
| 112 | + tmp_g[edge[0]] = 0 |
| 113 | + |
| 114 | + if edge[1] not in tmp_g: |
| 115 | + tmp_g[edge[1]] = 0 |
| 116 | + |
| 117 | + tmp_g[edge[0]] += 1 |
| 118 | + tmp_g[edge[1]] += 1 |
| 119 | + |
| 120 | + for vertex in tmp_g: |
| 121 | + if tmp_g[vertex] % 2 == 1: |
| 122 | + vertexes.append(vertex) |
| 123 | + |
| 124 | + return vertexes |
| 125 | + |
| 126 | + |
| 127 | +# utility function that adds minimum weight matching edges to MST |
| 128 | +def minimum_weight_matching(MST, G, odd_vert): |
| 129 | + while odd_vert: |
| 130 | + v = odd_vert.pop() |
| 131 | + length = float("inf") |
| 132 | + u = 1 |
| 133 | + closest = 0 |
| 134 | + for u in odd_vert: |
| 135 | + if G[v][u] < length: |
| 136 | + length = G[v][u] |
| 137 | + closest = u |
| 138 | + |
| 139 | + MST.append((v, closest, length)) |
| 140 | + odd_vert.remove(closest) |
| 141 | + |
| 142 | + |
| 143 | + |
| 144 | +tsp([[1, 1], [2, 5], [8, 0]]) |
0 commit comments