def __init__(self, G, s):
        """Computes a shortest-paths tree from the source vertex s to every
        other vertex in the edge-weighted graph G.

        :param G: the edge-weighted graph
        :param s: the source vertex
        :raises IllegalArgumentException: if an edge weight is negative
        :raises IllegalArgumentException: unless 0 <= s < V

        """
        for e in G.edges():
            if e.weight() < 0:
                raise IllegalArgumentException(
                    "edge {} has negative weight".format(e))

        self._dist_to = [float("inf")] * G.V()
        self._edge_to = [None] * G.V()
        self._dist_to[s] = 0.0
        self._validate_vertex(s)
        self._pq = IndexMinPQ(G.V())
        self._pq.insert(s, 0)

        while not self._pq.is_empty():
            v = self._pq.del_min()
            for e in G.adj(v):
                self._relax(e, v)
Exemplo n.º 2
0
    def __init__(self, G, s=0):
        for e in G.edges():
            if e.weight() < 0:
                raise ValueError("edge {} has negative weight".format(e))
        self._dist_to = [math.inf] * G.V()
        self._edge_to = [None] * G.V()

        # initialise source distance to 0 by convention
        self._dist_to[s] = 0.0

        # initialising index min pq to keep track of vertices that are candidates for being relaxed next
        self._pq = IndexMinPQ(G.V())
        self._pq.insert(s, 0.0)

        # iterate until all vertices possibly reachable are reached
        while not self._pq.is_empty():
            v = self._pq.del_min()
            for e in G.adj(v):
                self._relax(e)
Exemplo n.º 3
0
class DijkstraSP:
    def __init__(self, G, s=0):
        for e in G.edges():
            if e.weight() < 0:
                raise ValueError("edge {} has negative weight".format(e))
        self._dist_to = [math.inf] * G.V()
        self._edge_to = [None] * G.V()

        # initialise source distance to 0 by convention
        self._dist_to[s] = 0.0

        # initialising index min pq to keep track of vertices that are candidates for being relaxed next
        self._pq = IndexMinPQ(G.V())
        self._pq.insert(s, 0.0)

        # iterate until all vertices possibly reachable are reached
        while not self._pq.is_empty():
            v = self._pq.del_min()
            for e in G.adj(v):
                self._relax(e)

    def dist_to(self, v):
        return self._dist_to[v]

    def has_path_to(self, v):
        return self._dist_to[v] < float("inf")

    def path_to(self, v):
        if not self.has_path_to(v):
            return None
        path = Stack()
        e = self._edge_to[v]
        while e is not None:
            path.push(e)
            e = self._edge_to[e.from_vertex()]
        return path

    def _relax(self, e):
        v = e.from_vertex()
        w = e.to_vertex()
        if self._dist_to[w] > self._dist_to[v] + e.weight():
            self._dist_to[w] = self._dist_to[v] + e.weight()
            self._edge_to[w] = e
            if self._pq.contains(w):
                self._pq.decrease_key(w, self._dist_to[w])
            else:
                self._pq.insert(w, self._dist_to[w])
Exemplo n.º 4
0
    def __init__(self, G):
        """
        Compute a minimum spanning tree (or forest) of an edge-weighted graph.
        :param G: the edge-weighted graph
        """
        self._edge_to = [None] * G.V(
        )  # self._edge_to[v] = shortest edge from tree vertex to non-tree vertex
        self._dist_to = [
            0.0
        ] * G.V()  # self._dist_to[v] = weight of shortest such edge
        self._marked = [
            False
        ] * G.V()  # self._marked[v] = True if v on tree, False otherwise
        self._pq = IndexMinPQ(G.V())

        for v in range(G.V()):
            self._dist_to[v] = math.inf

        for v in range(G.V()):  # run from each vertex to find
            if not self._marked[v]:
                self._prim(G, v)  # minimum spanning forest

        # check optimality conditions
        assert self._check(G)
Exemplo n.º 5
0
def eccentricity(G, u):
    # initialise data structures
    dist_to = [math.inf] * G.V()
    q = IndexMinPQ(G.V())

    # start from source
    dist_to[u] = 0
    q.insert(u, 0.0)

    while q.is_empty() == False:
        v = q.del_min()
        for e in G.adj(v):
            # relax edge
            w = e.other(v)
            # relax condition if we can make the path shorter
            if dist_to[w] > dist_to[v] + e.weight():
                dist_to[w] = dist_to[v] + e.weight()
                if q.contains(w):
                    q.decrease_key(w, dist_to[w])
                else:
                    q.insert(w, dist_to[w])
    return max(dist_to)
Exemplo n.º 6
0
class DijkstraSP:
    """The DijkstraSP class represents a data type for solving the single-
    source shortest paths problem in edge-weighted digraphs where the edge
    weights are nonnegative.

    This implementation uses Dijkstra's algorithm with a binary heap.
    The constructor takes time proportional to E log V, where V is the
    number of vertices and E is the number of edges. Each call to
    dist_to() and has_path_to() takes constant time. Each call to
    path_to() takes time proportional to the number of edges in the
    shortest path returned.

    """
    def __init__(self, G, s):
        """Computes a shortest-paths tree from the source vertex s to every
        other vertex in the edge-weighted digraph G.

        :param G: The edge-weighted digraph
        :param s: The source vertex
        :raises IllegalArgumentException: if an edge weight is negative
        :raises IllegalArgumentException: unless 0 <= s < V

        """
        for e in G.edges():
            if e.weight() < 0:
                raise IllegalArgumentException(
                    "edge {} has negative weight".format(e))
        self._dist_to = [float("inf")] * G.V()
        self._edge_to = [None] * G.V()
        self._validate_vertex(s)
        self._dist_to[s] = 0.0
        self._pq = IndexMinPQ(G.V())
        self._pq.insert(s, 0.0)
        while not self._pq.is_empty():
            v = self._pq.del_min()
            for e in G.adj(v):
                self._relax(e)

    def dist_to(self, v):
        """Returns the length of a shortest path from the source vertex s to
        vertex v.

        :param v: the destination vertex
        :return: the length of a shortest path from the source vertex s to vertex v
        :rtype: float
        :raises IllegalArgumentException: unless 0 <= v < V

        """
        self._validate_vertex(v)
        return self._dist_to[v]

    def has_path_to(self, v):
        """Returns True if there is a ath from the source vertex s to vertex v.

        :param v: the destination vertex
        :return: True if there is a path from the source vertex
        s to vertex v. Otherwise returns False
        :rtype: bool
        :raises IllegalArgumentException: unless 0 <= v < V

        """
        self._validate_vertex(v)
        return self._dist_to[v] < float("inf")

    def path_to(self, v):
        """Returns a shortest path from the source vertex s to vertex v.

        :param v: the destination vertex
        :return: a shortest path from the source vertex s to vertex v
        :rtype: collections.iterable[DirectedEdge]
        :raises IllegalArgumentException: unless 0 <= v < V

        """
        self._validate_vertex(v)
        if not self.has_path_to(v):
            return None
        path = Stack()
        e = self._edge_to[v]
        while e is not None:
            path.push(e)
            e = self._edge_to[e.from_vertex()]
        return path

    def _relax(self, e):
        """Relaxes the edge e and updates the pq if changed.

        :param e: the edge to relax

        """
        v = e.from_vertex()
        w = e.to_vertex()
        if self._dist_to[w] > self._dist_to[v] + e.weight():
            self._dist_to[w] = self._dist_to[v] + e.weight()
            self._edge_to[w] = e
            if self._pq.contains(w):
                self._pq.decrease_key(w, self._dist_to[w])
            else:
                self._pq.insert(w, self._dist_to[w])

    def _validate_vertex(self, v):
        """Raises an IllegalArgumentException unless 0 <= v < V.

        :param v: the vertex to be validated

        """
        V = len(self._dist_to)
        if v < 0 or v >= V:
            raise IllegalArgumentException(
                "vertex {} is not between 0 and {}".format(v, V - 1))
Exemplo n.º 7
0
class PrimMST:
    """
    The PrimMST class represents a data type for computing a
    minimum spanning tree in an edge-weighted graph.
    The edge weights can be positive, zero, or negative and need not
    be distinct. If the graph is not connected, it computes a minimum
    spanning forest, which is the union of minimum spanning trees
    in each connected component. The weight() method returns the 
    weight of a minimum spanning tree and the edges() method
    returns its edges.
    
    This implementation uses Prim's algorithm with an indexed
    binary heap.
    The constructor takes time proportional to E log V
    and extra space not including the graph) proportional to V,
    where V is the number of vertices and E is the number of edges.
    Afterwards, the weight() method takes constant time
    and the edges() method takes time proportional to V.
    """

    FLOATING_POINT_EPSILON = 1E-12

    def __init__(self, G):
        """
        Compute a minimum spanning tree (or forest) of an edge-weighted graph.
        :param G: the edge-weighted graph
        """
        self._edge_to = [None] * G.V(
        )  # self._edge_to[v] = shortest edge from tree vertex to non-tree vertex
        self._dist_to = [
            0.0
        ] * G.V()  # self._dist_to[v] = weight of shortest such edge
        self._marked = [
            False
        ] * G.V()  # self._marked[v] = True if v on tree, False otherwise
        self._pq = IndexMinPQ(G.V())

        for v in range(G.V()):
            self._dist_to[v] = math.inf

        for v in range(G.V()):  # run from each vertex to find
            if not self._marked[v]:
                self._prim(G, v)  # minimum spanning forest

        # check optimality conditions
        assert self._check(G)

    # run Prim's algorithm in graph G, starting from vertex s
    def _prim(self, G, s):
        self._dist_to[s] = 0.0
        self._pq.insert(s, self._dist_to[s])
        while not self._pq.is_empty():
            v = self._pq.del_min()
            self._scan(G, v)

    def _scan(self, G, v):
        # scan vertex v
        self._marked[v] = True
        for e in G.adj(v):
            w = e.other(v)
            if self._marked[w]: continue  # v-w is obsolete edge
            if e.weight() < self._dist_to[w]:
                self._dist_to[w] = e.weight()
                self._edge_to[w] = e
                if self._pq.contains(w):
                    self._pq.decrease_key(w, self._dist_to[w])
                else:
                    self._pq.insert(w, self._dist_to[w])

    def edges(self):
        """
        Returns the edges in a minimum spanning tree (or forest).
        :returns: the edges in a minimum spanning tree (or forest) as
                an iterable of edges
        """
        mst = Queue()
        for v in range(len(self._edge_to)):
            e = self._edge_to[v]
            if e is not None:
                mst.enqueue(e)

        return mst

    def weight(self):
        """
        Returns the sum of the edge weights in a minimum spanning tree (or forest).
        :returns: the sum of the edge weights in a minimum spanning tree (or forest)
        """
        weight = 0.0
        for e in self.edges():
            weight += e.weight()
        return weight

    def _check(self, G):
        # check optimality conditions (takes time proportional to E V lg* V)

        totalWeight = 0.0  # check weight
        for e in self.edges():
            totalWeight += e.weight()

        if abs(totalWeight - self.weight()) > PrimMST.FLOATING_POINT_EPSILON:
            error = "Weight of edges does not equal weight(): {} vs. {}\n".format(
                totalWeight, self.weight())
            print(error, file=sys.stderr)
            return False

        # check that it is acyclic
        uf = UF(G.V())
        for e in self.edges():
            v = e.either()
            w = e.other(v)
            if uf.connected(v, w):
                print("Not a forest", file=sys.stderr)
                return False
            uf.union(v, w)

        # check that it is a spanning forest
        for e in G.edges():
            v = e.either()
            w = e.other(v)
            if not uf.connected(v, w):
                print("Not a spanning forest", file=sys.stderr)
                return False

        # check that it is a minimal spanning forest (cut optimality conditions)
        for e in self.edges():
            # all edges in MST except e
            uf = UF(G.V())
            for f in self.edges():
                x = f.either()
                y = f.other(x)
                if f != e:
                    uf.union(x, y)

            # check that e is min weight edge in crossing cut
            for f in G.edges():
                x = f.either()
                y = f.other(x)
                if not uf.connected(x, y):
                    if f.weight() < e.weight():
                        error = "Edge {} violates cut optimality conditions".format(
                            f)
                        print(error, file=sys.stderr)
                        return False
        return True
Exemplo n.º 8
0
def solve(h, weights):
    # construct complete binary tree
    weights = [item for sublist in weights for item in sublist]
    h += 1
    V = int((h * (h + 1)) / 2)
    G = EdgeWeightedDigraph(V)

    # construct edges of the graph
    _sum = 0
    added_edges = 0
    for level in range(0, h):
        for i in range(level):
            #print(f'add: {_sum+i}, {_sum+i+level}')
            G.add_edge(
                DirectedEdge(_sum + i, _sum + i + level, weights[added_edges]))
            added_edges += 1
            #print(f'add: {_sum+i}, {_sum+i+level+1}')
            G.add_edge(
                DirectedEdge(_sum + i, _sum + i + level + 1,
                             weights[added_edges]))
            added_edges += 1
        _sum += level

    dist_to = [math.inf for _ in range(V)]

    q = IndexMinPQ(G.V())
    q.insert(0, 0.0)  # enqueue root
    dist_to[0] = 0.0

    # iterate until all vertices possibly reachable are reached
    while not q.is_empty():
        u = q.del_min()
        for e in G.adj(u):
            u = e.from_vertex()
            v = e.to_vertex()
            if dist_to[v] > dist_to[u] + e.weight():
                dist_to[v] = dist_to[u] + e.weight()
                if q.contains(v):
                    q.decrease_key(v, dist_to[v])
                else:
                    q.insert(v, dist_to[v])

    # while not q.is_empty():
    #    curr = q.dequeue()
    #    for adj in G.adj(curr):
    #        u = curr
    #        v = adj.other(u)
    #        if dist_to[v] > dist_to[u] + adj.weight():
    #            dist_to[v] = dist_to[u] + adj.weight()
    #            q.enqueue(v)

    # get minimum of leaves
    return int(min(dist_to[len(dist_to) - h:]))