Пример #1
0
def prim(graph: UndirectedGraph) -> UndirectedGraph:
    """
    Prim's algorithm is a greedy algorithm that finds a minimum spanning tree for a weighted undirected graph.
    This means it finds a subset of the edges that forms a tree that includes every vertex
    O(E + V log V)
    """
    current_vertex = graph.get_vertices()[0]

    new_graph = UndirectedGraph()

    heap = FibonacciHeap(map(lambda x: (x, current_vertex), graph.get_outgoing_edges(current_vertex)))

    vertices = {current_vertex}

    while not heap.is_empty():

        edge, current_vertex = heap.pop()

        next_vertex = edge.a if edge.a != current_vertex else edge.b

        if next_vertex in vertices:
            continue

        # Add edge to the minimum spanning tree
        vertices.add(next_vertex)
        new_graph.add_edge(current_vertex, next_vertex, edge.weight)

        # Add all edges from this new point to the heap
        [heap.insert((edge, next_vertex)) for edge in graph.get_outgoing_edges(next_vertex)]

    return new_graph
Пример #2
0
class TestFibonacciHeap(unittest.TestCase):
    def setUp(self):
        self.list = [random.randint(1, 20) for _ in range(10)]
        self.heap = FibonacciHeap(self.list)

        self.list2 = [random.randint(1, 20) for _ in range(10)]
        self.heap2 = FibonacciHeap(self.list2)

    def test_pop(self):
        start = 0

        while True:
            try:
                value = self.heap.pop()
                assert value >= start
                assert value in self.list
                self.list.remove(value)
                start = value
            except IndexError:
                break
        assert len(self.list) == 0

    def test_insert(self):
        heap = FibonacciHeap()

        heap.insert(4)
        heap.insert(7)
        heap.insert(1)
        heap.insert(2)
        heap.insert(10)

        assert heap.pop() == 1

        heap.insert(3)

        assert heap.pop() == 2
        assert heap.pop() == 3
        assert heap.pop() == 4
        assert heap.pop() == 7
        assert heap.pop() == 10

    def test_merge(self):

        self.heap.merge(self.heap2)

        assert len(self.heap) == 20

        start = 0
        combined_list = self.list + self.list2
        while True:
            try:
                value = self.heap.pop()
                assert value >= start
                assert value in combined_list
                combined_list.remove(value)
                start = value
            except IndexError:
                break
        assert len(combined_list) == 0
Пример #3
0
def kruskal(graph: UndirectedGraph) -> UndirectedGraph:
    """
    Kruskal's algorithm is a minimum-spanning-tree algorithm which finds an edge of the least possible weight
    that connects any two trees in the forest. It is a greedy algorithm in graph theory as it finds a minimum
    spanning tree for a connected weighted undirected graph adding increasing cost arcs at each step.
    Time complexity: O(E log V)
    """
    spanning_tree = UndirectedGraph()

    union_find = UnionFind(max(graph.get_vertices()) + 1)

    heap = FibonacciHeap(graph.get_all_edges())

    while not heap.is_empty():

        edge = heap.pop()

        # Only add the edge if it will not create a cycle
        if union_find.find(edge.a) != union_find.find(edge.b):
            spanning_tree.add_edge(edge.a, edge.b, edge.weight)
            union_find.union(edge.a, edge.b)

    return spanning_tree
Пример #4
0
def dijkstra(graph: Graph, start_node, end_node) -> (int, List):
    """
    Dijkstras shortest path algorithm implementation
    Works for directed and undirected graphs with non negative weights
    Time complexity: O(EV + V^2 * logV)
    :return Returns a tuple of the distance of the shortest path and the nodes to reach the shortest path
    """

    # Min heap
    pq = FibonacciHeap()
    pq.insert((0, start_node))

    # Final distances and paths from the start node
    distances = {key: sys.maxsize for key in graph.get_vertices()}
    paths = {key: [] for key in graph.get_vertices()}

    # Finished nodes
    visited_nodes = set()

    # Set start node distance to 0
    distances[start_node] = 0

    while not pq.is_empty():
        distance, vertex = pq.pop()

        if distance == sys.maxsize or end_node in visited_nodes:
            # Only unconnected vertices are left or we found the minimal distance
            break

        for outgoing_edge in graph.get_outgoing_edges(vertex):

            next_vertex = outgoing_edge.a if outgoing_edge.a != vertex else outgoing_edge.b

            if next_vertex in visited_nodes:
                continue

            if distance + outgoing_edge.weight < distances[next_vertex]:
                distances[next_vertex] = distance + outgoing_edge.weight
                paths[next_vertex] = paths[vertex] + [next_vertex]

                pq.insert((distance + outgoing_edge.weight, next_vertex))

        visited_nodes.add(vertex)

    return distances[end_node], paths[end_node]
Пример #5
0
def dijkstra(graph: Graph, start_node, end_node) -> (int, List):
    """
    Dijkstras shortest path algorithm implementation
    Works for directed and undirected graphs with non negative weights
    Time complexity: O(EV + V^2 * logV)
    :return Returns a tuple of the distance of the shortest path and the nodes to reach the shortest path
    """

    # Min heap
    pq = FibonacciHeap()
    pq.insert((0, start_node))

    # Final distances and paths from the start node
    distances = {key: sys.maxsize for key in graph.get_vertices()}
    paths = {key: [] for key in graph.get_vertices()}

    # Finished nodes
    visited_nodes = set()

    # Set start node distance to 0
    distances[start_node] = 0

    while not pq.is_empty():
        distance, vertex = pq.pop()

        if distance == sys.maxsize or end_node in visited_nodes:
            # Only unconnected vertices are left or we found the minimal distance
            break

        for outgoing_edge in graph.get_outgoing_edges(vertex):

            next_vertex = outgoing_edge.a if outgoing_edge.a != vertex else outgoing_edge.b

            if next_vertex in visited_nodes:
                continue

            if distance + outgoing_edge.weight < distances[next_vertex]:
                distances[next_vertex] = distance + outgoing_edge.weight
                paths[next_vertex] = paths[vertex] + [next_vertex]

                pq.insert((distance + outgoing_edge.weight, next_vertex))

        visited_nodes.add(vertex)

    return distances[end_node], paths[end_node]
Пример #6
0
    def setUp(self):
        self.list = [random.randint(1, 20) for _ in range(10)]
        self.heap = FibonacciHeap(self.list)

        self.list2 = [random.randint(1, 20) for _ in range(10)]
        self.heap2 = FibonacciHeap(self.list2)
Пример #7
0
    def test_insert(self):
        heap = FibonacciHeap()

        heap.insert(4)
        heap.insert(7)
        heap.insert(1)
        heap.insert(2)
        heap.insert(10)

        assert heap.pop() == 1

        heap.insert(3)

        assert heap.pop() == 2
        assert heap.pop() == 3
        assert heap.pop() == 4
        assert heap.pop() == 7
        assert heap.pop() == 10