Exemple #1
0
 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()
Exemple #2
0
 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()
Exemple #3
0
 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()
Exemple #4
0
 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()
Exemple #5
0
 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()
Exemple #6
0
 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()
Exemple #7
0
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)
Exemple #8
0
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
Exemple #9
0
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
Exemple #10
0
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))
Exemple #12
0
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
Exemple #13
0
 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))
Exemple #14
0
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
Exemple #15
0
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