def johnson(g: Graph): g.vertices["s"] = Vertex("s") g.neighbors["s"] = [] for vertex in g.vertices: g.edges.append(Edge("s", vertex, 0)) g.neighbors["s"] += [(vertex, 0)] bf = bellman_ford(g, "s") if not bf: print("Ujemny cykl") return distances_b_f, _ = bf for edge in g.edges: edge.weight = edge.weight + \ distances_b_f[edge.start] \ - distances_b_f[edge.end] s_edges = [edge for edge in g.edges if edge.start == "s"] for edge in s_edges: g.edges.remove(edge) g.vertices.pop("s") g.neighbors.pop("s") distances = {} path = {} for vertex in g.vertices: distances[vertex], path[vertex] = \ dijkstra(g, vertex) for v_1 in g.vertices: for v_2 in g.vertices: distances[v_1][v_2] += \ distances_b_f[v_2] - distances_b_f[v_1] for edge in g.edges: edge.weight = edge.weight + \ distances_b_f[edge.start] \ - distances_b_f[edge.end] return distances, path
def floyd_warshall(g: Graph): """ Funkcja algorytmu Floyda-Warshalla :param g: Obiekt klasy Graph, badany graf :type g: Graph :return: Najkrótsze ścieżki między wszystkimi wierzchołkami oraz listę poprzedników Zwracana jest krotka: (distances, path) zawierająca macierz odległości między wszystkimi wierzchołkami oraz macierz poprzedników oprócz zwracania wyników, algorytm zapisuje wyniki do obiektów wierzchołków w grafie (klasa Vertex) """ # sprawdzamy czy podany graf nie zawiera ujemnych cykli, przechodzimy przez wszystkie wierzchołki dlatego, # że dla wierzchołka izolowanego lub takiego, który nie ma krawędzi wychodzących algorytm bellmana_forda # nie zwróci False, tylko dla każdego wierzchołka odległość będzie inf for vertex in g.vertices: # code może być wartością kodu błędu (-1 lub -2) lub też poprawnym wynikiem algorytmu code = bellman_ford(g, vertex) # -1 oznacza ujemny cykl wtedy wychodzimy z funkcji if code == -1: print("Ujemny cykl") return False # -2 oznacza że wierzchołek nie zawiera krawędzi wychodzących więc nie można stwierdzić czy występuje # ujemny cykl, więc przechodzimy do następnego elif code == -2: continue # jeśli nie nastąpił żaden błąd to graf nie zawiera ujemnych cykli - można przejść do algorytmu else: break # wyznaczamy wielkość macierzy n = len(g.vertices) # główna pętla algorytmu for k in range(n): for i in range(n): for j in range(n): if g.distance_matrix[i][ j] > g.distance_matrix[i][k] + g.distance_matrix[k][j]: g.distance_matrix[i][ j] = g.distance_matrix[i][k] + g.distance_matrix[k][j] g.path_matrix[i][j] = g.path_matrix[i][k] # przypisujemy wyniki do grafu for vertex in g.vertices: g.vertices[vertex].distance = { v: g.distance_matrix[g.indexes[vertex]][g.indexes[v]] for v in g.vertices } g.vertices[vertex].path = { v: g.path_matrix[g.indexes[vertex]][g.indexes[v]] for v in g.vertices } # zwracamy krotkę zawierającą macierz z odległościami oraz macierz poprzedników return g.distance_matrix, g.path_matrix
def matrix_multiply(g: Graph): for vertex in g.vertices: code = bellman_ford(g, vertex) if code == -1: print("Ujemny cykl") return False elif code == -2: continue else: break n = len(g.vertices) distance = deepcopy(g.distance_matrix) for _ in range(n - 2): old_distance = deepcopy(distance) for i in range(n): for j in range(n): distance[i][j] = inf for k in range(n): distance[i][j] = min( distance[i][j], old_distance[i][k] + g.distance_matrix[k][j]) # wyniki przypisujemy do grafu g.distance_matrix = deepcopy(distance) return distance
def generate(vertices_number, edges_number): vertices = [] for i in range(vertices_number): name = "" first_letter = int(i / 26) second_letter = i % 26 if first_letter != 0: first_letter = chr(97 + first_letter - 1) name += first_letter second_letter = chr(97 + second_letter) name += second_letter vertices.append(Vertex(name)) edges = [] graph_edges = [] available_edges = [] for v_start in vertices: for v_end in vertices: if v_start == v_end: continue available_edges.append((v_start, v_end)) for i in range(edges_number): random_edge = random.choice(available_edges) edges.append(random_edge) available_edges.remove(random_edge) for edge in edges: start_vertex, end_vertex = edge weight = random.randint(0, 10) graph_edges.append(Edge(start_vertex.name, end_vertex.name, weight)) graph_edges.sort(key=operator.attrgetter('start', 'end')) graph = Graph(vertices={vertex.name: vertex for vertex in vertices}, edges=graph_edges) return graph
def faster_matrix_multiply(g: Graph): """ Funkcja algorytmu z iloczynem odległości - wersja ulepszona :param g: Obiekt klasy Graph, graf na którym wykonujemy algorytm :type g: Graph :return: Najkrótsze ścieżki między wszystkimi wierzchołkami w postaci macierzy Zwracana jest macierz z odległościami, macierz jest listą list oprócz zwracania wyników, algorytm przypisuje wyniki do obiektu macierzy w grafie (distance_matrix) """ # dodatkowe sprawdzanie czy graf nie zawiera ujemnego cyklu for vertex in g.vertices: code = bellman_ford(g, vertex) if code == -1: print("Ujemny cykl") return False elif code == -2: continue else: break # wyznaczamy n - wielkość macierzy n = len(g.vertices) # wykonujemy tak zwaną głęboką kopię bazowego grafu distance = deepcopy(g.distance_matrix) # wykonujemy główne działanie algorytmu log(V) razy m = 1 while n - 1 > m: # działamy na macierzy distance ale potrzebować będziemy starych wartości więc tworzymy kopię old_distance = deepcopy(distance) for i in range(n): for j in range(n): distance[i][j] = inf for k in range(n): distance[i][j] = min( distance[i][j], old_distance[i][k] + old_distance[k][j]) m = 2 * m # wyniki przypisujemy do grafu g.distance_matrix = deepcopy(distance) return distance
from described.bellman_ford import bellman_ford, \ print_bellman_ford from described.graph import Edge, Graph, Vertex G = Graph(vertices={ "a": Vertex("a"), "b": Vertex("b"), "c": Vertex("c"), "d": Vertex("d"), "e": Vertex("e") }, edges=[ Edge("a", "b", 4), Edge("a", "c", 5), Edge("b", "a", -3), Edge("b", "c", -4), Edge("c", "d", 7), Edge("d", "b", 9), Edge("d", "e", 10), Edge("e", "b", 8) ]) print_bellman_ford(G, "e", bellman_ford(G, "e"))
from described.dijkstra import dijkstra, print_dijkstra from described.graph import Edge, Graph, Vertex G = Graph(vertices={ "a": Vertex("a"), "b": Vertex("b"), "c": Vertex("c"), "d": Vertex("d"), "e": Vertex("e"), "f": Vertex("f") }, edges=[ Edge("a", "b", 7), Edge("a", "c", 9), Edge("a", "f", 14), Edge("b", "c", 10), Edge("b", "d", 15), Edge("c", "d", 11), Edge("c", "f", 2), Edge("d", "e", 6), Edge("e", "f", 9) ]) dijkstra(G, "a") print_dijkstra(G, "a")
from described.bellman_ford import bellman_ford, print_bellman_ford from described.graph import Edge, Graph, Vertex G = Graph(vertices={ "a": Vertex("a"), "b": Vertex("b"), "c": Vertex("c"), "d": Vertex("d"), "e": Vertex("e") }, edges=[ Edge("a", "c", 10), Edge("a", "b", 3), Edge("b", "a", 2), Edge("c", "d", -9), Edge("d", "e", 7), Edge("e", "a", 1), Edge("e", "c", -5) ]) print_bellman_ford(G, "a", bellman_ford(G, "a"))
def johnson(g: Graph): """ Funkcja algorytmu Johnsona :param g: Obiekt klasy Graph, graf na którym wykonujemy algorytm :type g: Graph :return: Najkrótsze ścieżki między wszystkimi wierzchołkami oraz listę poprzedników Zwracana jest krotka: (distances, path) zawierająca odległości między wszystkimi wierzchołkami oraz macierz poprzedników oprócz zwracania wyników, algorytm przypisuje wyniki do obiektów wierzchołków grafu (klasa Vertex) """ # sprawdzanie na wstępie czy graf zawiera ujemny cykl, nadmiarowe - usunięcie przyśpieszy w pewnym stopniu algorytm # przechodzimy przez wszystkie wierzchołki dlatego, że dla wierzchołka izolowanego lub takiego, który nie ma # krawędzi wychodzących algorytm bellmana_forda nie zwróci False, tylko dla każdego wierzchołka odległość będzie inf for vertex in g.vertices: # code może być wartością kodu błędu (-1 lub -2) lub też poprawnym wynikiem algorytmu code = bellman_ford(g, vertex) # -1 oznacza ujemny cykl wtedy wychodzimy z funkcji if code == -1: print("Ujemny cykl") return False # -2 oznacza że wierzchołek nie zawiera krawędzi wychodzących więc nie można stwierdzić czy występuje # ujemny cykl, więc przechodzimy do następnego elif code == -2: continue # jeśli nie nastąpił żaden błąd to graf nie zawiera ujemnych cykli - można przejść do algorytmu else: break # tworzymy pomocniczy wierzchołek "-s" oraz krawędzie z wierzchołka "-s" do każdego innego w grafie g.vertices["-s"] = Vertex("-s") g.neighbors["-s"] = [] for vertex in g.vertices: g.edges.append(Edge("-s", vertex, 0)) g.neighbors["-s"] += [(vertex, 0)] # po utworzeniu wierzchołka "-s" obliczamy odległości z wierzchołka "-s" do pozostałych wierzchołków # dodatkowo sprawdzamy czy graf nie zawiera ujemnych cykli, jeśli tak to wychodzimy z funkcji result = bellman_ford(g, "-s") # jeśli algorytm Bellmana-Forda zwróci False, również algorytm Johnsona zwraca False - ujemny cykl if not result: return False distances_b_f, _ = result # zmieniamy wagi dla każdej krawędzi uwzględniając odległości wyliczone przez algorytm Bellmana-Forda # przez co likwidujemy ujemne wagi i umożliwiamy użycie algorytmu Dijkstry for edge in g.edges: edge.weight = edge.weight + distances_b_f[edge.start] - distances_b_f[edge.end] # usuwamy wierzchołek "-s" oraz wszystkie krawędzie wyprowadzone z tego wierzchołka s_edges = [edge for edge in g.edges if edge.start == "-s"] for edge in s_edges: g.edges.remove(edge) g.vertices.pop("-s") g.neighbors.pop("-s") # wyliczamy algorytmem Dijkstry odległości i poprzedniki dla każdego wierzchołka distances = {} path = {} for vertex in g.vertices: distances[vertex], path[vertex] = dijkstra(g, vertex) # ponownie zmieniamy wagi dla każdej krawędzi oraz poprawiamy odległości uwzględniając poprzednio zmienione # ujemne wagi for v_1 in g.vertices: for v_2 in g.vertices: distances[v_1][v_2] += distances_b_f[v_2] - distances_b_f[v_1] for edge in g.edges: edge.weight = edge.weight + distances_b_f[edge.start] - distances_b_f[edge.end] # zwracamy krotkę zawierającą odległości oraz macierz poprzedników return distances, path
from described.graph import Edge, Graph, Vertex from described.matrix_multiply\ import faster_matrix_multiply G = Graph(vertices=["a", "b", "c", "d", "e"], edges=[ Edge("a", "c", -3), Edge("a", "d", 2), Edge("b", "a", -5), Edge("b", "c", 4), Edge("c", "d", 6), Edge("d", "e", 1), Edge("e", "b", 9), Edge("e", "c", -4) ]) for line in faster_matrix_multiply(G): print(line)