Skip to content

Commit 9a7c586

Browse files
committed
Full algorithm
1 parent 61477a9 commit 9a7c586

1 file changed

Lines changed: 92 additions & 57 deletions

File tree

christofides.py

Lines changed: 92 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
def tsp(data):
2-
length = 0
3-
visited = [False] * len(data)
4-
paths = []
5-
62
G = build_graph(data)
73
print("Graph: ", G)
84

@@ -17,26 +13,31 @@ def tsp(data):
1713
minimum_weight_matching(MSTree, G, odd_vertexes)
1814
print("Minimum weight matching: ", MSTree)
1915

20-
eulerian_tour = find_eulerian_tour(MSTree)
21-
visited = [False] * (len(eulerian_tour) - 1)
22-
16+
eulerian_tour = find_eulerian_tour(MSTree, G)
2317

2418
print("Eulerian tour: ", eulerian_tour)
2519

2620
current = eulerian_tour[0]
27-
paths = [current]
28-
for x in eulerian_tour[1:]:
29-
if not visited[x]:
30-
visited[x] = True
21+
path = [current]
22+
visited = [False] * len(eulerian_tour)
3123

32-
paths.append(x)
33-
length += G[current][x]
24+
length = 0
3425

35-
current = x
26+
for v in eulerian_tour[1:]:
27+
if not visited[v]:
28+
path.append(v)
29+
visited[v] = True
3630

37-
print("Result path: ", paths)
31+
length += G[current][v]
32+
current = v
33+
34+
path.append(path[0])
35+
36+
print("Result path: ", path)
3837
print("Result length of the path: ", length)
3938

39+
return length, path
40+
4041

4142
def get_length(x1, y1, x2, y2):
4243
return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** (1 / 2)
@@ -57,31 +58,11 @@ def build_graph(data):
5758

5859

5960
class UnionFind:
60-
"""Union-find data structure.
61-
62-
Each unionFind instance X maintains a family of disjoint sets of
63-
hashable objects, supporting the following two methods:
64-
65-
- X[item] returns a name for the set containing the given item.
66-
Each set is named by an arbitrarily-chosen one of its members; as
67-
long as the set remains unchanged it will keep the same name. If
68-
the item is not yet part of a set in X, a new singleton set is
69-
created for it.
70-
71-
- X.union(item1, item2, ...) merges the sets containing each item
72-
into a single larger set. If any item is not yet part of a set
73-
in X, it is added to X as one of the members of the merged set.
74-
"""
75-
7661
def __init__(self):
77-
"""Create a new empty union-find structure."""
7862
self.weights = {}
7963
self.parents = {}
8064

8165
def __getitem__(self, object):
82-
"""Find and return the name of the set containing the object."""
83-
84-
# check for previously unknown object
8566
if object not in self.parents:
8667
self.parents[object] = object
8768
self.weights[object] = 1
@@ -100,11 +81,9 @@ def __getitem__(self, object):
10081
return root
10182

10283
def __iter__(self):
103-
"""Iterate through all items ever found or unioned by this structure."""
10484
return iter(self.parents)
10585

10686
def union(self, *objects):
107-
"""Find the sets containing the objects and merge them all."""
10887
roots = [self[x] for x in objects]
10988
heaviest = max([(self.weights[r], r) for r in roots])[1]
11089
for r in roots:
@@ -145,44 +124,100 @@ def find_odd_vertexes(MST):
145124

146125

147126
def minimum_weight_matching(MST, G, odd_vert):
127+
import random
128+
odd_vert = random.shuffle(odd_vert)
129+
148130
while odd_vert:
149131
v = odd_vert.pop()
150132
length = float("inf")
151133
u = 1
152134
closest = 0
153135
for u in odd_vert:
154-
if G[v][u] < length:
136+
if v != u and G[v][u] < length:
155137
length = G[v][u]
156138
closest = u
157139

158140
MST.append((v, closest, length))
159141
odd_vert.remove(closest)
160142

161143

162-
def find_eulerian_tour(MatchedMSTree):
163-
tour = []
144+
def find_eulerian_tour(MatchedMSTree, G):
145+
# find neigbours
146+
neighbours = {}
147+
for edge in MatchedMSTree:
148+
if edge[0] not in neighbours:
149+
neighbours[edge[0]] = []
164150

165-
start_vertex = MatchedMSTree[0][0]
151+
if edge[1] not in neighbours:
152+
neighbours[edge[1]] = []
166153

167-
tour.append(start_vertex)
154+
neighbours[edge[0]].append(edge[1])
155+
neighbours[edge[1]].append(edge[0])
156+
157+
# print("Neighbours: ", neighbours)
158+
159+
# finds the hamiltonian circuit
160+
start_vertex = MatchedMSTree[0][0]
161+
EP = [neighbours[start_vertex][0]]
168162

169163
while len(MatchedMSTree) > 0:
170-
current_vertex = tour[len(tour) - 1]
171-
for edge in MatchedMSTree:
172-
if current_vertex in edge:
173-
if edge[0] == current_vertex:
174-
current_vertex = edge[1]
175-
elif edge[1] == current_vertex:
176-
current_vertex = edge[0]
177-
else:
178-
# Edit to account for case no tour is possible
179-
return False
180-
181-
MatchedMSTree.remove(edge)
182-
tour.append(current_vertex)
164+
for i, v in enumerate(EP):
165+
if len(neighbours[v]) > 0:
183166
break
184167

185-
return tour
168+
while len(neighbours[v]) > 0:
169+
w = neighbours[v][0]
170+
171+
remove_edge_from_matchedMST(MatchedMSTree, v, w)
172+
173+
del neighbours[v][(neighbours[v].index(w))]
174+
del neighbours[w][(neighbours[w].index(v))]
175+
176+
i += 1
177+
EP.insert(i, w)
178+
179+
v = w
180+
181+
return EP
182+
183+
184+
def remove_edge_from_matchedMST(MatchedMST, v1, v2):
185+
186+
for i, item in enumerate(MatchedMST):
187+
if (item[0] == v2 and item[1] == v1) or (item[0] == v1 and item[1] == v2):
188+
del MatchedMST[i]
189+
190+
return MatchedMST
191+
192+
193+
tsp([[1380, 939], [2848, 96], [3510, 1671], [457, 334], [3888, 666], [984, 965], [2721, 1482], [1286, 525],
194+
[2716, 1432], [738, 1325], [1251, 1832], [2728, 1698], [3815, 169], [3683, 1533], [1247, 1945], [123, 862],
195+
[1234, 1946], [252, 1240], [611, 673], [2576, 1676], [928, 1700], [53, 857], [1807, 1711], [274, 1420],
196+
[2574, 946], [178, 24], [2678, 1825], [1795, 962], [3384, 1498], [3520, 1079], [1256, 61], [1424, 1728],
197+
[3913, 192], [3085, 1528], [2573, 1969], [463, 1670], [3875, 598], [298, 1513], [3479, 821], [2542, 236],
198+
[3955, 1743], [1323, 280], [3447, 1830], [2936, 337], [1621, 1830], [3373, 1646], [1393, 1368],
199+
[3874, 1318], [938, 955], [3022, 474], [2482, 1183], [3854, 923], [376, 825], [2519, 135], [2945, 1622],
200+
[953, 268], [2628, 1479], [2097, 981], [890, 1846], [2139, 1806], [2421, 1007], [2290, 1810], [1115, 1052],
201+
[2588, 302], [327, 265], [241, 341], [1917, 687], [2991, 792], [2573, 599], [19, 674], [3911, 1673],
202+
[872, 1559], [2863, 558], [929, 1766], [839, 620], [3893, 102], [2178, 1619], [3822, 899], [378, 1048],
203+
[1178, 100], [2599, 901], [3416, 143], [2961, 1605], [611, 1384], [3113, 885], [2597, 1830], [2586, 1286],
204+
[161, 906], [1429, 134], [742, 1025], [1625, 1651], [1187, 706], [1787, 1009], [22, 987], [3640, 43],
205+
[3756, 882], [776, 392], [1724, 1642], [198, 1810], [3950, 1558]])
186206

207+
# tsp([[1, 1], [2, 5], [8, 0]])
187208

188-
tsp([[1, 1], [2, 5], [8, 0]])
209+
#
210+
# tsp([
211+
# [0, 0],
212+
# [3, 0],
213+
# [6, 0],
214+
#
215+
# [0, 3],
216+
# [3, 3],
217+
# [6, 3],
218+
#
219+
# [0, 6],
220+
# [3, 6],
221+
# [6, 6],
222+
#
223+
# ])

0 commit comments

Comments
 (0)