def __init__(self, graph): """The algorithm initialization.""" if graph.is_directed(): raise ValueError("the graph is directed") self.graph = graph self.hamiltonian_cycle = list() self._uf = UnionFind() self._pq = PriorityQueue()
def __init__(self, graph): """The algorithm initialization.""" if graph.is_directed(): raise ValueError("the graph is directed") self.graph = graph self.hamiltonian_cycle = self.graph.__class__(self.graph.v()) for node in self.graph.iternodes(): self.hamiltonian_cycle.add_node(node) self._uf = UnionFind() self._pq = PriorityQueue()
def __init__(self, graph): """The algorithm initialization. Parameters ---------- graph : undirected weighted graph or multigraph """ if graph.is_directed(): raise ValueError("the graph is directed") self.graph = graph self.mst = self.graph.__class__(self.graph.v()) for node in self.graph.iternodes(): # isolated nodes are possible self.mst.add_node(node) self._uf = UnionFind()
class SortedEdgeTSPWithGraph: """The sorted edge algorithm for TSP. Attributes ---------- graph : input weighted complete graph hamiltonian_cycle : cycle graph _uf : disjoint-set data structure, private _pq : priority queue, private """ def __init__(self, graph): """The algorithm initialization.""" if graph.is_directed(): raise ValueError("the graph is directed") self.graph = graph self.hamiltonian_cycle = self.graph.__class__(self.graph.v()) for node in self.graph.iternodes(): self.hamiltonian_cycle.add_node(node) self._uf = UnionFind() self._pq = PriorityQueue() def run(self, source=None): """Executable pseudocode.""" for node in self.graph.iternodes(): self._uf.create(node) for edge in self.graph.iteredges(): self._pq.put((edge.weight, edge)) while not self._pq.empty(): _, edge = self._pq.get() degree1 = self.hamiltonian_cycle.degree(edge.source) degree2 = self.hamiltonian_cycle.degree(edge.target) # Zabezpieczam przed skrzyzowaniami T, X, itp. if degree1 == 2 or degree2 == 2: continue if degree1 == 0 or degree2 == 0: self._uf.union(edge.source, edge.target) self.hamiltonian_cycle.add_edge(edge) continue # Here degree1 = degree2 = 1. if self._uf.find(edge.source) != self._uf.find(edge.target): # Join pieces. self._uf.union(edge.source, edge.target) self.hamiltonian_cycle.add_edge(edge) elif self.hamiltonian_cycle.e() == self.hamiltonian_cycle.v()-1: # Close the Hamiltonian cycle. self.hamiltonian_cycle.add_edge(edge)
class KruskalMSTSorted: """Kruskal's algorithm for finding a minimum spanning tree. Attributes ---------- graph : input undirected graph or multigraph mst : graph (MST) _uf : disjoint-set data structure, private Examples -------- >>> from graphtheory.structures.edges import Edge >>> from graphtheory.structures.graphs import Graph >>> from graphtheory.spanningtrees.kruskal import KruskalMSTSorted >>> G = Graph(n=10, False) # an exemplary undirected graph # Add nodes and edges here. >>> algorithm = KruskalMSTSorted(G) # initialization >>> algorithm.run() # calculations >>> algorithm.mst.show() # MST as an undirected graph Notes ----- Based on: Cormen, T. H., Leiserson, C. E., Rivest, R. L., and Stein, C., 2009, Introduction to Algorithms, third edition, The MIT Press, Cambridge, London. https://en.wikipedia.org/wiki/Kruskal's_algorithm J. B. Kruskal, On the shortest spanning subtree of a graph and the traveling salesman problem, Proc. Amer. Math. Soc. 7, 48-50 (1956). D. Eppstein, Kruskal's algorithm for minimum spanning trees, 2006, https://www.ics.uci.edu/~eppstein/PADS/MinimumSpanningTree.py """ def __init__(self, graph): """The algorithm initialization. Parameters ---------- graph : undirected weighted graph or multigraph """ if graph.is_directed(): raise ValueError("the graph is directed") self.graph = graph self.mst = self.graph.__class__(self.graph.v()) for node in self.graph.iternodes(): # isolated nodes are possible self.mst.add_node(node) self._uf = UnionFind() def run(self): """Finding MST.""" for node in self.graph.iternodes(): self._uf.create(node) #for edge in sorted(self.graph.iteredges()): # very slow #for (_, edge) in sorted((edge.weight, edge) for edge in self.graph.iteredges()): from operator import attrgetter for edge in sorted(self.graph.iteredges(), key=attrgetter("weight")): if self._uf.find(edge.source) != self._uf.find(edge.target): self._uf.union(edge.source, edge.target) self.mst.add_edge(edge) def to_tree(self): """Compatibility with other classes.""" return self.mst
class BoruvkaMST: """Boruvka's algorithm for finding a minimum spanning tree. Attributes ---------- graph : input undirected graph or multigraph mst : graph (MST) _uf : disjoint-set data structure, private Examples -------- >>> from graphtheory.structures.edges import Edge >>> from graphtheory.structures.graphs import Graph >>> from graphtheory.spanningtrees.boruvka import BoruvkaMST >>> G = Graph(n=10, False) # an exemplary undirected graph # Add nodes and edges here. >>> algorithm = BoruvkaMST(G) >>> algorithm.run() # calculations >>> algorithm.mst.show() # MST as an undirected graph Notes ----- Based on pseudocode from Wikipedia: https://en.wikipedia.org/wiki/Boruvka's_algorithm https://en.wikipedia.org/wiki/Minimum_spanning_tree """ def __init__(self, graph): """The algorithm initialization. Parameters ---------- graph : undirected weighted graph or multigraph """ if graph.is_directed(): raise ValueError("the graph is directed") self.graph = graph self.mst = self.graph.__class__(self.graph.v()) for node in self.graph.iternodes(): # isolated nodes are possible self.mst.add_node(node) self._uf = UnionFind() def run(self): """Finding MST.""" for node in self.graph.iternodes(): self._uf.create(node) forest = set(node for node in self.graph.iternodes()) dummy_edge = Edge(None, None, float("inf")) new_len = len(forest) old_len = new_len + 1 while old_len > new_len: old_len = new_len min_edges = dict(((node, dummy_edge) for node in forest)) # Finding the cheapest eadges. for edge in self.graph.iteredges(): # O(E) time source = self._uf.find(edge.source) target = self._uf.find(edge.target) if source != target: # different components if edge < min_edges[source]: min_edges[source] = edge if edge < min_edges[target]: min_edges[target] = edge # Connecting components, total time is O(V). forest = set() for edge in min_edges.itervalues(): if edge is dummy_edge: # a disconnected graph continue source = self._uf.find(edge.source) target = self._uf.find(edge.target) if source != target: # different components self._uf.union(source, target) forest.add(source) self.mst.add_edge(edge) # Remove duplicates, total time is O(V). forest = set(self._uf.find(node) for node in forest) new_len = len(forest) if new_len == 1: # a connected graph break def to_tree(self): """Return the minimum spanning tree.""" return self.mst
def test_unionfind(self): algorithm = UnionFind() for node in range(10): algorithm.create(node) pairs = [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9), (3, 1)] for a, b in pairs: algorithm.union(a, b) self.assertTrue(algorithm.find(1) == algorithm.find(2)) self.assertTrue(algorithm.find(7) != algorithm.find(9)) pairs = [(7, 5), (3, 7), (8, 7)] for a, b in pairs: algorithm.union(a, b) self.assertTrue(algorithm.find(0) == algorithm.find(6)) self.assertTrue(algorithm.find(1) == algorithm.find(9))
class SortedEdgeTSPWithEdges: """The sorted edge algorithm (a.k.a. cheapest link) for TSP. Attributes ---------- graph : input weighted complete graph hamiltonian_cycle : list of edges _uf : disjoint-set data structure, private _pq : priority queue, private """ def __init__(self, graph): """The algorithm initialization.""" if graph.is_directed(): raise ValueError("the graph is directed") self.graph = graph self.hamiltonian_cycle = list() self._uf = UnionFind() self._pq = PriorityQueue() def run(self, source=None): """Executable pseudocode.""" degree_dict = dict((node, 0) for node in self.graph.iternodes()) for node in self.graph.iternodes(): self._uf.create(node) for edge in self.graph.iteredges(): self._pq.put((edge.weight, edge)) while not self._pq.empty(): _, edge = self._pq.get() degree1 = degree_dict[edge.source] degree2 = degree_dict[edge.target] # Zabezpieczam przed skrzyzowaniami T, X, itp. if degree1 == 2 or degree2 == 2: continue if degree1 == 0 or degree2 == 0: self._uf.union(edge.source, edge.target) self.hamiltonian_cycle.append(edge) degree_dict[edge.source] += 1 degree_dict[edge.target] += 1 continue # Here degree1 = degree2 = 1. if self._uf.find(edge.source) != self._uf.find(edge.target): # Join pieces. self._uf.union(edge.source, edge.target) self.hamiltonian_cycle.append(edge) degree_dict[edge.source] += 1 degree_dict[edge.target] += 1 elif len(self.hamiltonian_cycle) == self.graph.v() - 1: # Close the Hamiltonian cycle. self.hamiltonian_cycle.append(edge) degree_dict[edge.source] += 1 degree_dict[edge.target] += 1 # Create the corrected (directed) list of edges. # Przeksztalcam cykl Hamiltona do postaci poprawnej listy krawedzi. # Na razie krawedzie moga miec zla kolejnosc i kierunek. # Tworze slownik z krawedziami w obie strony. edge_dict = dict((node, []) for node in self.graph.iternodes()) for edge in self.hamiltonian_cycle: # O(V) time edge_dict[edge.source].append(edge) edge_dict[edge.target].append(~edge) edge = self.hamiltonian_cycle[0] self.hamiltonian_cycle = [edge] # Kompletuje kolejne krawedzie. for step in xrange(self.graph.v() - 1): # O(V) time edge1, edge2 = edge_dict[edge.target] if edge1.target == edge.source: self.hamiltonian_cycle.append(edge2) edge = edge2 else: self.hamiltonian_cycle.append(edge1) edge = edge1
class BoruvkaMST: """Boruvka's algorithm for finding a minimum spanning tree. Attributes ---------- graph : input undirected graph or multigraph mst : graph (MST) _uf : disjoint-set data structure, private Examples -------- >>> from graphtheory.structures.edges import Edge >>> from graphtheory.structures.graphs import Graph >>> from graphtheory.spanningtrees.boruvka import BoruvkaMST >>> G = Graph(n=10, False) # an exemplary undirected graph # Add nodes and edges here. >>> algorithm = BoruvkaMST(G) >>> algorithm.run() # calculations >>> algorithm.mst.show() # MST as an undirected graph Notes ----- Based on pseudocode from Wikipedia: https://en.wikipedia.org/wiki/Boruvka's_algorithm https://en.wikipedia.org/wiki/Minimum_spanning_tree """ def __init__(self, graph): """The algorithm initialization. Parameters ---------- graph : undirected weighted graph or multigraph """ if graph.is_directed(): raise ValueError("the graph is directed") self.graph = graph self.mst = self.graph.__class__(self.graph.v()) for node in self.graph.iternodes(): # isolated nodes are possible self.mst.add_node(node) self._uf = UnionFind() def run(self): """Finding MST.""" for node in self.graph.iternodes(): self._uf.create(node) forest = set(self.graph.iternodes()) dummy_edge = Edge(None, None, float("inf")) new_len = len(forest) old_len = new_len + 1 while old_len > new_len: old_len = new_len min_edges = dict(((node, dummy_edge) for node in forest)) # Finding the cheapest edges. for edge in self.graph.iteredges(): # O(E) time source = self._uf.find(edge.source) target = self._uf.find(edge.target) if source != target: # different components if edge < min_edges[source]: min_edges[source] = edge if edge < min_edges[target]: min_edges[target] = edge # Connecting components, total time is O(V). for edge in min_edges.itervalues(): if edge is dummy_edge: # a disconnected graph continue source = self._uf.find(edge.source) target = self._uf.find(edge.target) if source != target: # different components self._uf.union(source, target) self.mst.add_edge(edge) # Remove duplicates, total time is O(V). forest = set(self._uf.find(node) for node in forest) new_len = len(forest) if new_len == 1: # a connected graph break def to_tree(self): """Return the minimum spanning tree.""" return self.mst
class SortedEdgeTSPWithEdges: """The sorted edge algorithm (a.k.a. cheapest link) for TSP. Attributes ---------- graph : input weighted complete graph hamiltonian_cycle : list of edges _uf : disjoint-set data structure, private _pq : priority queue, private """ def __init__(self, graph): """The algorithm initialization.""" if graph.is_directed(): raise ValueError("the graph is directed") self.graph = graph self.hamiltonian_cycle = list() self._uf = UnionFind() self._pq = PriorityQueue() def run(self, source=None): """Executable pseudocode.""" degree_dict = dict((node, 0) for node in self.graph.iternodes()) for node in self.graph.iternodes(): self._uf.create(node) for edge in self.graph.iteredges(): self._pq.put((edge.weight, edge)) while not self._pq.empty(): _, edge = self._pq.get() degree1 = degree_dict[edge.source] degree2 = degree_dict[edge.target] # Zabezpieczam przed skrzyzowaniami T, X, itp. if degree1 == 2 or degree2 == 2: continue if degree1 == 0 or degree2 == 0: self._uf.union(edge.source, edge.target) self.hamiltonian_cycle.append(edge) degree_dict[edge.source] += 1 degree_dict[edge.target] += 1 continue # Here degree1 = degree2 = 1. if self._uf.find(edge.source) != self._uf.find(edge.target): # Join pieces. self._uf.union(edge.source, edge.target) self.hamiltonian_cycle.append(edge) degree_dict[edge.source] += 1 degree_dict[edge.target] += 1 elif len(self.hamiltonian_cycle) == self.graph.v()-1: # Close the Hamiltonian cycle. self.hamiltonian_cycle.append(edge) degree_dict[edge.source] += 1 degree_dict[edge.target] += 1 # Create the corrected (directed) list of edges. # Przeksztalcam cykl Hamiltona do postaci poprawnej listy krawedzi. # Na razie krawedzie moga miec zla kolejnosc i kierunek. # Tworze slownik z krawedziami w obie strony. edge_dict = dict((node, []) for node in self.graph.iternodes()) for edge in self.hamiltonian_cycle: # O(V) time edge_dict[edge.source].append(edge) edge_dict[edge.target].append(~edge) edge = self.hamiltonian_cycle[0] self.hamiltonian_cycle = [edge] # Kompletuje kolejne krawedzie. for step in xrange(self.graph.v()-1): # O(V) time edge1, edge2 = edge_dict[edge.target] if edge1.target == edge.source: self.hamiltonian_cycle.append(edge2) edge = edge2 else: self.hamiltonian_cycle.append(edge1) edge = edge1