Zapisz jako PDF
Transkrypt
Zapisz jako PDF
Spis treści 1 Krótki wstęp do teorii grafów 1.1 Graf 1.2 Definicja matematyczna 1.3 Krawędzie skierowane i nieskierowane 1.3.1 Sąsiedztwo 1.3.2 Stopień wierzchołka 1.3.3 Stopień wejściowy 1.3.4 Stopień wyjściowy 1.4 Ścieżka 1.4.1 Ścieżka prosta 1.4.2 Osiągalność 1.5 Cykl 1.5.1 W grafie skierowanym 1.5.1.1 Prosty 1.5.2 W grafie nieskierowanym 1.6 Graf spójny 1.7 Graf pełny 1.8 Graf dwudzielny 1.9 Graf acykliczny 1.10 Graf wolny 1.11 Jak skonstruować graf skierowany z nieskierowanego i odwrotnie 1.12 Zastosowania 1.13 Zadania 1.13.1 Zadanie 1 1.13.2 Zadanie 2 1.13.3 Zadanie 3 1.13.4 Zadanie 4 1.13.5 Zadanie 5 2 Moduł przedstawiający reprezentację abstrakcyjnego grafu 2.1 Zadania 3 Algorytm szukający najkrótszej ścieżki 3.1 Zadania Krótki wstęp do teorii grafów Graf Graf jest to zbiór wierzchołków i zbiór krawędzi . Każdy wierzchołek to po prostu jakiś obiekt, najczęściej po prostu etykieta. Natomiast każda krawędz to para wierzchołków, czyli . Istnienie krawędzi oznacza istnienie relacji pomiędzy tymi dwoma wierzchołkami. Jedna krawędź nie może związać ze sobą więcej niż dwóch wierzchołków. Dwa wierzchołki mogą być ze sobą związane więcej niż jedną krawędzią. Definicja matematyczna Grafem nazywamy parę uporządkowaną przy czym: — zbiór wierzchołków grafu, — zbiór krawędzi grafu, każde to para elementów , . Krawędzie skierowane i nieskierowane Są dwa typy grafów: graf nieskierowany, w którym krawędzie nie mają kierunku, czyli krawędź jest tym samym co krawędź . Przykładem takiego grafu może być graf osób gdzie krawędzie reprezentują spotkania. Ponieważ jeśli A. spotkał B., to i B. spotkał A., więc graf jest nieskierowany. graf skierowany, w którym rozróżniamy krawedź i . Przykładem takiego grafu może być graf osób i tego czy się lubią — jeśli A. lubi B., to B. może też lubić A., ale niestety nie musi. Jeśli jest krawędzią w grafie skierowanym, to mówimy, że krawędź wychodzi z wierzchołka i wchodzi do wierzchołka . Sąsiedztwo Jeżeli jest krawędzią grafu, to mówimy, że wierzchołek jest sąsiadem wierzchołka . Jeżeli graf jest nieskierowany, relacja sąsiedztwa jest symetryczna. Jeżeli graf jest skierowany, relacja sąsiedztwa nie musi być symetryczna. W grafie skierowanym, jeżeli jest wierzchołkiem sąsiadującym z , relacja ta jest często zapisywana jako . Stopień wierzchołka Stopniem wierzchołka jest liczba krawędzi wychodzących lub wchodzących do tego wierzchołka. W przypadku grafu nieskierowanego, kierunek nie ma znaczenia, więc jest to po prostu liczba krawędzi łączących dany wierzchołek z innymi. Stopień wejściowy W grafie skierowanym — liczba krawędzi wchodzących do wierzchołka. Stopień wyjściowy W grafie skierowanym — liczba krawędzi wychodzących z wierzchołka. Oczywiście stopień wejściowy plus stopień wyjściowy muszą w sumie dać stopień wierzchołka. Ścieżka Ścieżka (droga) długości z wierzchołka do wierzchołka wierzchołków , takich że krawędzi dla wszystkich od 1 do . Długość ścieżki jest liczbą krawędzi w ścieżce. i w grafie , a pary jest ciagiem należą do zbioru Ścieżka zawiera wierzchołki , ,…, i krawędzie , ,…, . Ścieżka prosta Ścieżka, której wszystkie wierzchołki są różne. Osiągalność W grafie istnieje ścieżka z o wierzchołku do . możemy powiedzieć, że jest osiągalny z wierzchołka , jeżeli Cykl W grafie skierowanym W grafie skierowanym ścieżka zawiera co najmniej jedną krawędź. tworzy cykl, jeżeli i ścieżka Pętla jest cyklem o długości jeden. Prosty Cykl jest prosty jeżeli są różne. W grafie nieskierowanym W grafie nieskierowanym są różne i ścieżka tworzy cykl, jeżeli , . Graf spójny Graf nieskierowany jest spójny, jeżeli każda para wierzchołków jest połączona ścieżką. Graf skierowany jest silnie spójny, jeżeli każde dwa wierzchołki są osiągalne jeden z drugiego. Graf pełny Graf nieskierowany, w którym każda para wierzchołków połączona jest krawędzią. Graf dwudzielny Graf nieskierowany, którego zbiór wierzchołków można podzielić na dwa rozłączne zbiory tak, że krawędzie nie łączą wierzchołków tego samego zbioru. Pojęcie to można uogólnić na trzy i więcej zbiorów. Graf acykliczny Graf niezawierający cykli. Graf wolny Acykliczny graf nieskierowany. Jak skonstruować graf skierowany z nieskierowanego i odwrotnie W przypadku grafu nieskierowanego, każdą jego krawędź nieskierowaną skierowane gałęzie i zamieniamy na dwie . Konstruując wersję nieskierowaną grafu skierowanego, usuwamy wszystkie pętle i zamieniamy wszystkie skierowane krawędzie na nieskierowane. Zastosowania Grafy ze względu na swoją ogólność są jednymi z najbardziej powszechnych modeli różnych strukur — zarówno tych stworzonych przez człowieka, jak i tych stworzonych przez naturę. Używa się ich do modelowania związków a także procesów dynamicznych w systemach fizycznych, biologicznych i społecznych, ale także i w ligwistyce. Zadania Zadanie 1 Który z przedstawionych grafów na rysunkach Figure 1, Figure 2, Figure 3, Figure 4, Figure 5, Figure 6 jest grafem wolnym? Jakie są cechy grafu wolnego? Zadanie 2 Który z przedstawionych grafów Figure 1, Figure 2, Figure 3, Figure 4, Figure 5, Figure 6 jest grafem pełnym? Jakie są cechy grafu pełnego? Zadanie 3 Wypisz sąsiadów wierzchołka(ów) o najwyższym stopniu w grafach na rysunkach Figure 1, Figure 2, Figure 3, Figure 4, Figure 5, Figure 6. Zadanie 4 Dla grafu na rysunku Figure 1: Wypisz jakie wierzchołki są nieosiągalne dla wierzchołków 3 i 7. Które wierzchołki nie mają żadnego osiągalnego wierdzchołka? Jaka jest największa możliwa długość ścieżki? Dla grafu na rysunku Figure 5: Czy graf ma wierzchołek, dla którego wszystkie inne wierzchołki są osiągalne? Czy graf ma wierzchołek, który nie ma żadnego osiągalnego wierzchołka? Dla grafu na rysunku Figure 6 Wypisz najdłuższą możliwą ścieżkę. Zadanie 5 Które z grafów przedstawionych na rysunkach Figure 1, Figure 2, Figure 3, Figure 4, Figure 5, Figure 6 zawierają cykle? Moduł przedstawiający reprezentację abstrakcyjnego grafu Poniższy tekst programu jest autorstwa Niko Wilberta. #!/usr/bin/env python # -*- coding: utf-8 -*""" This module contains all elements for an abstract graph representation. Written by Niko Wilbert for the G-Node Python Winter School 2010. """ class Node(object): """Base class for nodes. To add additional data attributes to nodes you can derive subclasses. """ def __init__(self): """Initialize the node.""" self._out_edges = [] # outgoing edges self._in_edges = [] # incoming edges @property def out_edges(self): return self._out_edges class Edge(object): """Edge class. This class also registers itself with the affected nodes, so it sits between Node and Graph in the hierarchy. The head and tail can also be None. """ def __init__(self, head, tail): """Initialize the edge. head, tail -- The end and starting node of this edge, or can be None. """ self._head = head if head is not None: head._in_edges.append(self) self._tail = tail if tail is not None: tail._out_edges.append(self) def _set_head(self, head): """Set the head and register this in the nodes as well. The head can also be None, then the edge is unregistered. """ if self._head is not None: self._head._in_edges.remove(self) self._head = head if head is not None: self._head._in_edges.append(self) def _set_tail(self, tail): """Set the tail and register this in the nodes as well. The tail can also be None, then the edge is unregistered. """ if self._tail is not None: self._tail._out_edges.remove(self) self._tail = tail if tail is not None: self._tail._out_edges.append(self) # use these public properties head = property(lambda self: self._head, _set_head) tail = property(lambda self: self._tail, _set_tail) def clear_nodes(self): """Clear the node references.""" self._set_tail(None) self._set_head(None) class GraphException(Exception): """Base Exception for Graph.""" pass class NoPathGraphException(GraphException): """Exception signaling that there is no path between nodes.""" pass class Graph(object): """Class to represent a complete graph with nodes and edges. Note that this class does not support nodes which do not belong to any edge (internally only edges are stored). """ def __init__(self, edges=None): self._edges = set() if edges: for edge in edges: self.add_edge(edge) @property def edges(self): """Return list of the edges in this graph.""" return self._edges @property def nodes(self): """Return set of nodes in this graph.""" nodes = set() for edge in self._edges: nodes.add(edge.head) nodes.add(edge.tail) return nodes def add_edge(self, edge): """Add an edge to the graph.""" self._edges.add(edge) def remove_edge(self, edge): """Remove an edge from the graph.""" edge.clear_nodes() self._edges.remove(edge) Zadania Zadania są autorstwa Niko Wilberta i Bartosza Teleńczuka. Powyżej przedstawiona jest implementacja klas reprezentujących obiekt. Na kartce dokonaj analizy kodu. 1. Wypisz wszystkie nazwy klas, ich metody i własności publiczne (czyli te, które nie zaczynają się od podkreślenia). Spróbuj zrozumieć jakie jest ich wszystkich działanie (przeczytaj docstringi!). Nie zawahaj się zapytać ćwiczeniowca, jeżeli masz jakieś wątpliwości. Znajdź ewentualne błędy implementacji. Jeżeli składnia @property jest dla Ciebie dziwna lub niezrozumiała, przeczytaj o dekoratorach i property. 2. Zastanów się, jak poszczególne klasy są ze sobą związane (dziedziczenie kontra wykorzystanie explicite cudzych metod). Narysuj prosty diagram. 3. Korzystając z klasy Graph zaimplementuj graf przedstawiony na rysunku Figure 5 (nr wierzchołka zmień na nazwę np. 1 na N1) i Figure 7. Algorytm szukający najkrótszej ścieżki Autor Niko Wilbert Krótkie wprowadzenie do problemu poszukiwania najkrótszej ścieżki można znaleźć tu #!/usr/bin/env python #coding=utf-8 from heapq import heappop, heappush def shortest_path(start_node, end_node, weight_func=lambda edge: 1): """Return the shortest path and its overall weight. weight_func -- Functions that maps an edge to a weight value, the default function maps all edges to 1. """ ## this is basically Dijkstra's algorithm # store the shortest path to all nodes, # the value tuples contain the path length and the previous node shortest_paths = {start_node: (, None)} # we use this list as a priority cue with heapq # the tuples contain the overall path length and the edge itself edge_heap = [] for edge in start_node.out_edges: heappush(edge_heap, (weight_func(edge), edge)) while edge_heap: path_weight, edge = heappop(edge_heap) if ((edge.head not in shortest_paths) or (shortest_paths[edge.head][] > path_weight)): shortest_paths[edge.head] = (path_weight, edge) # if we already visited this node then there may be edge # duplicates in the heap, but this is no problem because # the newer edges are guaranteed to come first for out_edge in edge.head.out_edges: heappush(edge_heap, (path_weight + weight_func(out_edge), out_edge)) if end_node not in shortest_paths: err = ("The is no connection from node %s" % str(start_node) + " to node %s." % str(end_node)) raise NoPathGraphException(err) # assemble the shortest path from end to start path_weight = shortest_paths[end_node][] path_edges = [shortest_paths[end_node][1]] current_node = path_edges[-1].tail while current_node is not start_node: path_edges.append(shortest_paths[current_node][1]) current_node = path_edges[-1].tail return path_edges[::-1], path_weight Zadania Korzystając z kodu klasy Graph oraz shortest_path() napisz aplikację do planowania podróży. W tej aplikacji poszczególne miasta bedą reprezentowane jako węzły w grafie, a krawędzie pomiędzy węzłami będą reprezentowały możliwe środki komunikacji. 1. Napisz klasę CityNode, która roszerza klasę Node o nazwę (miasta), która definiowana jest przy inicjalizacji klasy. 2. Napisz klasę TransportationEdge rozszerzającą klasę Edge. Krawędzie powinny być skierowane i obarczone dwoma rodzajami wag — czasem podróży i kosztem; klasa powinna też zawierać pole description zawierające krótki opis środka transportu. 3. Zaimplementuj graf pokazany na rysunku Figure 8. Zastanów się pierw jakie operacje powinny być udostępnione przez ten graf, jakie może być jego zastosowanie. 4. Chciałbyś znaleźć najkrótszą drogę pomiędzy Berlinem a Kolonią. Funkcja shortest_path() znajduje najkrótszą drogę w grafie. Korzystając z tej funkcji rozszerz klasę Graph o metody poszukujące najkrótszej i najtańszej drogi. 5. Znajdź najkrótszą drogę między Berlinem a Kolonią.