AISDE ćwiczenie 5. Algorytmy grafowe

Podobne dokumenty
Struktury danych i złożoność obliczeniowa Wykład 5. Prof. dr hab. inż. Jan Magott

a) 7 b) 19 c) 21 d) 34

Ogólne wiadomości o grafach

Zofia Kruczkiewicz, Algorytmu i struktury danych, Wykład 14, 1

Sprawozdanie do zadania numer 2

Reprezentacje grafów nieskierowanych Reprezentacje grafów skierowanych. Wykład 2. Reprezentacja komputerowa grafów

Algorytmy optymalizacji globalnej

Złożoność obliczeniowa klasycznych problemów grafowych

AiSD zadanie trzecie

Wykład 8. Drzewo rozpinające (minimum spanning tree)

Przykłady grafów. Graf prosty, to graf bez pętli i bez krawędzi wielokrotnych.

Drzewa spinające MST dla grafów ważonych Maksymalne drzewo spinające Drzewo Steinera. Wykład 6. Drzewa cz. II

Algorytmy grafowe. Wykład 1 Podstawy teorii grafów Reprezentacje grafów. Tomasz Tyksiński CDV

Grafem nazywamy strukturę G = (V, E): V zbiór węzłów lub wierzchołków, Grafy dzielimy na grafy skierowane i nieskierowane:

TEORETYCZNE PODSTAWY INFORMATYKI

Porównanie algorytmów wyszukiwania najkrótszych ścieżek międz. grafu. Daniel Golubiewski. 22 listopada Instytut Informatyki

Algorytmy grafowe. Wykład 2 Przeszukiwanie grafów. Tomasz Tyksiński CDV

Matematyczne Podstawy Informatyki

Algorytmy Równoległe i Rozproszone Część V - Model PRAM II

Zastosowania drzew binarnych

Algorytmiczna teoria grafów

dodatkowe operacje dla kopca binarnego: typu min oraz typu max:

Zadanie projektowe nr 1

Algorytm Dijkstry znajdowania najkrótszej ścieżki w grafie

Uniwersytet Zielonogórski Wydział Elektrotechniki, Informatyki i Telekomunikacji Instytut Sterowania i Systemów Informatycznych

Algorytmy i struktury danych. Drzewa: BST, kopce. Letnie Warsztaty Matematyczno-Informatyczne

Fragment wykładu z języka C ( )

EGZAMIN - Wersja A. ALGORYTMY I STRUKTURY DANYCH Lisek89 opracowanie kartki od Pani dr E. Koszelew

Rozpoznawanie obrazu. Teraz opiszemy jak działa robot.

Zadanie 1: Piętnastka

Podejście zachłanne, a programowanie dynamiczne

Struktury Danych i Złożoność Obliczeniowa

Algorytmy i struktury danych

Programowanie obiektowe

Digraf. 13 maja 2017

Programowanie obiektowe

Wstęp do Programowania potok funkcyjny

Grafy i Zastosowania. 5: Drzewa Rozpinające. c Marcin Sydow. Drzewa rozpinające. Cykle i rozcięcia fundamentalne. Zastosowania

Wykład 10 Grafy, algorytmy grafowe

Instytut Fizyki Politechniki Łódzkiej Laboratorium Metod Analizy Danych Doświadczalnych Ćwiczenie 3 Generator liczb losowych o rozkładzie Rayleigha.

Segmentacja obrazów cyfrowych z zastosowaniem teorii grafów - wstęp. autor: Łukasz Chlebda

Digraf o V wierzchołkach posiada V 2 krawędzi, zatem liczba różnych digrafów o V wierzchołkach wynosi 2 VxV

Laboratorium z przedmiotu Programowanie obiektowe - zestaw 04

G. Wybrane elementy teorii grafów

Podstawy informatyki. Elektrotechnika I rok. Język C++ Operacje na danych - wskaźniki Instrukcja do ćwiczenia

Matematyczne Podstawy Informatyki

Podstawy Programowania 2 Grafy i ich reprezentacje. Plan. Wstęp. Teoria grafów Graf skierowany. Notatki. Notatki. Notatki. Notatki.

Egzamin, AISDI, I termin, 18 czerwca 2015 r.

ĆWICZENIE 1: Przeszukiwanie grafów cz. 1 strategie ślepe

WYŻSZA SZKOŁA INFORMATYKI STOSOWANEJ I ZARZĄDZANIA

1. Algorytmy przeszukiwania. Przeszukiwanie wszerz i w głąb.

Algorytmy wyznaczania centralności w sieci Szymon Szylko

. Podstawy Programowania 2. Grafy i ich reprezentacje. Arkadiusz Chrobot. 9 czerwca 2016

Zadanie 2: Arytmetyka symboli

Analiza Algorytmów 2018/2019 (zadania na laboratorium)

2. Tablice. Tablice jednowymiarowe - wektory. Algorytmy i Struktury Danych

Matematyka dyskretna - 7.Drzewa

operacje porównania, a jeśli jest to konieczne ze względu na złe uporządkowanie porównywanych liczb zmieniamy ich kolejność, czyli przestawiamy je.

PROJEKT Z TEORETYCZNYCH PODSTAW INFORMATYKI

Badania operacyjne: Wykład Zastosowanie kolorowania grafów w planowaniu produkcji typu no-idle

Podstawowe pojęcia dotyczące drzew Podstawowe pojęcia dotyczące grafów Przykłady drzew i grafów

Ćwiczenie 1 Planowanie trasy robota mobilnego w siatce kwadratów pól - Algorytm A

MATEMATYKA DYSKRETNA - MATERIAŁY DO WYKŁADU GRAFY

Algorytm. a programowanie -

Minimalne drzewa rozpinające

P R Z E T W A R Z A N I E S Y G N A Ł Ó W B I O M E T R Y C Z N Y C H

XML i nowoczesne technologie zarządzania treścią 2007/08

Matematyka dyskretna

KARTA PRZEDMIOTU. 1. Informacje ogólne. 2. Ogólna charakterystyka przedmiotu. Algorytmy i struktury danych, C3

Podstawy programowania. Wykład 7 Tablice wielowymiarowe, SOA, AOS, itp. Krzysztof Banaś Podstawy programowania 1

Przygotowanie kilku wersji kodu zgodnie z wymogami wersji zadania,

Michał Kazimierz Kowalczyk rok 1, semestr 2 nr albumu indeksu: Algorytmy i struktury danych. Problem połączeń

Wykład 3. Złożoność i realizowalność algorytmów Elementarne struktury danych: stosy, kolejki, listy

Suma dwóch grafów. Zespolenie dwóch grafów

Sprawozdanie do 5. Projektu z Algorytmów i struktur danych 1

Wstęp do Programowania potok funkcyjny

Złożoność obliczeniowa zadania, zestaw 2

3. Opracować program kodowania/dekodowania pliku tekstowego. Algorytm kodowania:

Systemy uczące się Lab 4

Zadanie 1. Suma silni (11 pkt)

Backend Administratora

Systemy operacyjne. Laboratorium 8. Perl find

Niezwykłe tablice Poznane typy danych pozwalają przechowywać pojedyncze liczby. Dzięki tablicom zgromadzimy wiele wartości w jednym miejscu.

Ćwiczenie: JavaScript Cookies (3x45 minut)

XQTav - reprezentacja diagramów przepływu prac w formacie SCUFL przy pomocy XQuery

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Rozwiązywanie problemów metodą przeszukiwania

Scenariusz lekcji. podać definicję matematyczną grafu; wymienić podstawowe rodzaje grafów;

Algorytmy i Struktury Danych.

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

7. Teoria drzew - spinanie i przeszukiwanie

Lista, Stos, Kolejka, Tablica Asocjacyjna

Data Mining Wykład 9. Analiza skupień (grupowanie) Grupowanie hierarchiczne O-Cluster. Plan wykładu. Sformułowanie problemu

Przypomnij sobie krótki wstęp do teorii grafów przedstawiony na początku semestru.

Graf. Definicja marca / 1

utworz tworzącą w pamięci dynamicznej tablicę dwuwymiarową liczb rzeczywistych, a następnie zerującą jej wszystkie elementy,

Drzewa poszukiwań binarnych

Przykładowe sprawozdanie. Jan Pustelnik

Transkrypt:

AISDE ćwiczenie 5 Algorytmy grafowe Wstęp Ćwiczenie ma na celu zapoznanie się z przykładowymi implementacjami wybranych algorytmów grafowych. Analizowane będą dwa rodzaje algorytmów: znajdujące najkrótszą drogę w grafie skierowanym (digrafie) oraz tworzące najmniejsze drzewo rozpinające w grafie nieskierowanym Zakłada się znajomość: podstawowej terminologii używanej w teorii grafów, algorytmów: Dijkstry, Floyda, Kruskala, Borůvki i Prima, podstaw języka C++, niniejszej instrukcji; sekcja Dodatek jest nieobowiązkowa, lecz ułatwia zrozumienie kodu np. w celu jego modyfikacji lub oszacowania teoretycznej złożoności obliczeniowej i pamięciowej zaimplementowanych algorytmów. Wskazane jest także zapoznanie się z kodem programów wykorzystywanych w ćwiczeniu, zwłaszcza z treścią funkcji: Floyd, Dijkstra, DijkstraPQ, DijkstraList, DijkstraListPQ i DFS (plik MinpathMain.cpp) oraz Kruskal i Boruvka (plik MST_Algorithms.cpp). W całej instrukcji liczba wierzchołków grafu jest oznaczona jako m, zaś liczba krawędzi jako n. Wykorzystywane narzędzia W ćwiczeniu będą wykorzystywane następujące programy: graphgen generator losowych grafów nieskierowanych, digraphgen generator losowych grafów skierowanych, mst program tworzący najmniejsze drzewo rozpinające (ang. Minimum Spanning Tree, stąd nazwa) w grafie nieskierowanym, minpath program znajdujący najkrótsze ścieżki w grafie skierowanym. Działanie tych narzędzi jest krótko omówione poniżej, natomiast szczegóły ich budowy w końcowym punkcie Dodatek. Wszystkie programy są dostępne w postaci plików źródłowych. Wpisanie polecenia make w katalogu projektu powoduje pojawienie się w nim wspomnianych czterech plików wykonywalnych. Przyjęta została zasada, że każda klasa jest zdefiniowana w osobnym pliku źródłowym o nazwie identycznej jak nazwa klasy. Wyjątkiem są klasy szablonowe List i ListElement, których ciała są z konieczności definiowane w plikach nagłówkowych. Po modyfikacji dowolnego pliku źródłowego należy dokonać rekompilacji programem make. Z kolei po modyfikacji pliku nagłówkowego należy najpierw usunąć 1

wszystkie produkty kompilacji poleceniem make clean, a następnie powtórnie skompilować cały projekt poleceniem make. Generator grafów nieskierowanych graphgen Jest to prosty generator losowych grafów nieskierowanych. Polecenie:./graphgen m n [nazwa_pliku_wynikowego] spowoduje stworzenie grafu o m wierzchołkach i n gałęziach. Zostanie on zapisany w pliku o podanej nazwie. Jeśli nazwa zostanie pominięta, graf będzie zapisany w pliku graf.txt. UWAGA! Jeżeli argument n przekracza liczbę krawędzi pełnego grafu nieskierowanego o m wierzchołkach, zostanie wygenerowany taki właśnie graf pełny, o czym użytkownik jest informowany. Należy to brać pod uwagę podczas analizy zależności czasu wykonania algorytmów grafowych od liczby krawędzi. Natomiast próba wygenerowania grafu, w którym n < m 1, zakończy się błędem, ponieważ graf taki byłby niespójny. Każdy graf wygenerowany przez program graphgen jest spójny zawiera ścieżkę Hamiltona prowadzącą przez wierzchołki o kolejnych numerach. Stopnie wszystkich wierzchołków grafu są w przybliżeniu równe. Wagi krawędzi to wartości bezwzględne zmiennej losowej o rozkładzie normalnym o zerowej wartości oczekiwanej, zaokrąglone w górę do najbliższej liczby naturalnej. Graf wynikowy zostaje zapisany do pliku tekstowego. Każdy wiersz takiego pliku zawiera opis jednej krawędzi: nr_wierzchołka_1 <sp> nr_wierzchołka_2 <sp> waga_krawędzi gdzie <sp> oznacza spację lub znak tabulacji. Na przykład wiersz o zawartości: 2 10 5 opisuje krawędź o wadze 5 wychodzącą z wierzchołka 2 i dochodzącą do wierzchołk a 10. Numeracja wierzchołków rozpoczyna się od zera. Ponieważ graf jest nieskierowany, kolejność wierzchołków w opisie krawędzi nie ma znaczenia jako pierwszy wypisywany jest wierzchołek o mniejszym indeksie. Generator grafów skierowanych digraphgen Działanie tego programu jest bardzo podobne do poprzedniego. Uruchamiany jest poleceniem:./digraphgen m n [nazwa_pliku_wynikowego] Domyślna nazwa pliku wyjściowego to digraf.txt. Wagi krawędzi mają rozkład równomierny na przedziale od 1 do 100. Format tworzonego pliku jest identyczny, jak dla programu graphgen. Ponieważ jednak w tym przypadku mamy do czynienia z grafem skierowanym, zakładamy, że pierwszy z wierzchołków opisujących krawędź jest jej początkiem, a drugi końcem. UWAGA! Jeżeli argument n przekracza liczbę krawędzi pełnego grafu skierowanego o m wierzchołkach, 2

zostanie wygenerowany taki właśnie graf pełny, o czym użytkownik jest informowany. Należy to brać pod uwagę podczas analizy zależności czasu wykonania algorytmów grafowych od liczby krawędzi. Natomiast próba wygenerowania grafu, w którym n < m, zakończy się błędem, ponieważ graf taki byłby niespójny. Każdy graf wygenerowany przez program digraphgen jest spójny zawiera cykl Hamiltona prowadzący przez wierzchołki o kolejnych numerach, tj. 0 1 2 (m 1) 0. Program wyznaczający najmniejsze drzewo rozpinające mst Program ten zawiera przykładowe implementacje algorytmów Kruskala i Borůvki, wyznaczających najmniejsze drzewo rozpinające grafu nieskierowanego. Wywołanie ma postać:./mst nazwa_pliku_z_grafem_nieskierowanym Graf wejściowy może być wygenerowany przez program graphgen. Program m s t podaje czasy wykonania obu algorytmów. Dla grafów o liczbie krawędzi nie przekraczającej wartości MAX_PRINTABLE_E (zdefiniowanej w pliku MST_Utils.h) wyświetla także krawędzie tworzące znalezione drzewo. Są one wypisywane w takiej kolejności, w jakiej były dodawane do drzewa, co może być pomocne przy analizowaniu algorytmu. Program wyznaczający najkrótsze ścieżki w grafie skierowanym minpath Program minpath wyznacza odległości pomiędzy parami węzłów digrafu odczytanego z pliku. Możliwe są dwa sposoby wywołania:./minpath nazwa_pliku_z_digrafem v w./minpath nazwa_pliku_z_digrafem Pierwszy sposób pozwala wyznaczyć długość drogi od wierzchołka v do wierzchołka w, natomiast drugi macierz odległości pomiędzy wszystkimi parami wierzchołków. Element w wierszu i i kolumnie j tej macierzy odpowiada długości drogi prowadzącej od wierzchołk a i do wierzchołk a j. Numeracja wierszy i kolumn zaczyna się od zera. Znak minus w jakiejś pozycji oznacza brak drogi pomiędzy odpowiednimi wierzchołkami. Wyświetlanie działa tylko w przypadku, gdy liczba wierzchołków grafu nie przekracza wartości stałej MAX_PRINTABLE_V, zdefiniowanej w pliku MinpathUtils.h. Niezależnie od sposobu wywołania programu, wyświetla on czas wykonania każdego z algorytmów. Przebieg ćwiczenia Ćwiczenie należy rozpocząć od prawidłowej inicjalizacji generatorów zmiennych losowych w programach tworzących grafy. W tym celu stałej SEED, zdefiniowanej w plikach Graphgen.cpp i Digraphgen.cpp, należy nadać wartość swojego numeru albumu. 3

UWAGA! W oryginalnej wersji programu minpath uruchamiane są po kolei wszystkie wymienione w instrukcji algorytmy. Jeżeli użytkownik zamierza pominą ć któryś z nich, np. z uwagi na nieakceptowalnie długi czas wykonania, może tego dokonać wykomentowując w pliku MinpathMain.cpp wywołanie funkcji FindShortestPath dla tego algorytmu. Przykładowe zadania do wykonania 1. Dla grafu skierowanego o kilku wierzchołkach i kilku-kilkunastu krawędziach obserwujemy efekty działania każdego z algorytmów poszukujących najkrótszej drogi między wszystkimi parami wierzchołków. Jeżeli różne algorytmy dostarczają różnych wyników, należy wyjaśnić przyczynę. Graf, wraz z numerami wierzchołków i wagami krawędzi, należy narysować sprawozdaniu i zaznaczyć na jedną z dróg znalezionych przez algorytmy. 2. Dla grafu nieskierowanego o kilku wierzchołkach i kilku-kilkunastu krawędziach obserwujemy efekty działania każdego z algorytmów wyznaczających najmniejsze drzewo rozpinające (MST). Jeżeli różne algorytmy dostarczają różnych wyników, należy wyjaśnić przyczynę. Graf, wraz z numerami wierzchołków i wagami krawędzi, należy narysować sprawozdaniu a następnie wyróżnić na nim krawędzie znalezionego drzewa rozpinającego. 3. Dla wskazanych przez prowadzącego algorytmów poszukujących najkrótszych dróg w grafie skierowanym badamy zależnoś ć czasu obliczeń od liczby wierzchołków m tego grafu przy ustalonej liczbie krawędzi n i vice versa. Minimalna liczba wierzchołków/krawędzi użyta w analizie powinna pozwalać uzyskać czasy rzędu dziesiątek milisekund. Mniejsze czasy są podatne na fluktuacje, ponadto na wielu maszynach obserwowane czasy są skwantowane co jedną milisekundę (a na niektórych maszynach nawet co 10 milisekund). Maksymalna liczba wierzchołków/krawędzi jest ograniczona wyłącznie cierpliwością osoby wykonującej ćwiczenie. Zaleca się prowadzenie badań przynajmniej do osiągnięcia czasów rzędu kilkunastu/kilkudziesięciu sekund. Liczba krawęd z i n grafu skierowanego musi spełniać oczywiste warunki: m n m(m 1). Górne ograniczenie wynika z liczby krawędzi digrafu pełnego, zaś dolne z wymagania spójności grafu. Sugeruje się, by najmniejsza i największa liczba krawędzi użyta w eksperymencie były zbliżone odpowiednio do dolnego i górnego ograniczenia. W sprawozdaniu należy zamieścić wykresy z wynikami eksperymentów, tj. czasami obliczeń dla różnych wartości m i n. (Wyniki te należy również umieścić w tabelach na końcu sprawozdania.) Skala na obu osiach powinna pozwolić łatwo oszacować postać funkcyjną zależności czasu działania od m lub n. W zależności od spodziewanej klasy złożoności można zastosować na jednej lub obu osiach skalę logarytmiczną. Przy spodziewanej złożoności O(f(m, n)) można także spróbować wykreślić czas w funkcji f(m) lub f(n). Następnie proszę określić złożoność czasową każdej z przebadanych implementacji algorytmu. Należy porównać ją z teoretyczną złożonością wyznaczoną na podstawie analizy wykorzystywanych struktur danych i algorytmów. 4

UWAGA! W punkcie tym program minpath trzeba uruchamiać w trybie każdy do każdego, czyli bez podawania wierzchołka startowego i docelowego. Przy takim ustawieniu algorytm Dijkstry zostanie uruchomiony m razy, startując za każdym razem od innego spośród m wierzchołków i znajdując odległość do wszystkich pozostałych. Dzięki temu łatwiej będzie porównać różne wersje algorytmu Dijkstry z algorytmem Floyda, który z zasady znajduje drogi między wszystkimi parami wierzchołków. Dodatkową korzyścią jest uzyskanie większych a zatem mniej podatnych na fluktuacje czasów działania. Przy szacowaniu złożoności obliczeniowej algorytmów trzeba jednak pamiętać, że zmierzony czas odpowiada m-krotnemu wywołaniu algorytmu Dijkstry i jednokrotnemu wywołaniu algorytmu Floyda. 4. Dla wskazanych przez prowadzącego algorytmów wyznaczających najmniejsze drzewo rozpinające grafu nieskierowanego badamy zależnoś ć czasu obliczeń od liczby wierzchołków m tego grafu przy ustalonej liczbie krawędzi n i vice versa. Ponieważ algorytmy wyznaczania MST są bardzo szybkie, rozmiary największych grafów użytych w tym punkcie mogą (a nawet powinny) osiągać kilka/kilkadziesiąt tysięcy wierzchołków i kilkanaście/kilkadziesiąt milionów krawędzi. Sama generacja grafów tej wielkości i ich zapis na dysk potrwa o wiele dłużej, niż wyznaczanie MST proszę uzbroić się w cierpliwość. Wszystkie uwagi dotyczące użytej liczby krawędzi i wierzchołków, sposobu wykonywania wykresów itp. jak w opisie przykładowego zadania 3. 5. Modyfikujemy dowolną z wersji algorytmu Dijkstry, tak by wypisywała na ekran znalezioną najkrótszą drogę, tj. sekwencję wierzchołków od początkowego do końcowego. Kod należy krótko opisać i koniecznie zamieścić wyniki testowania dla niewielkiego grafu. 6. Ulepszamy algorytm Floyda. Testujemy poprawność działania naszej wersji na grafie na tyle małym, by macierz najkrótszych dróg została wypisana na ekran. Następnie badamy zależność czasu obliczeń od liczby wierzchołków i krawędzi i porównujemy z oryginalną implementacją oraz z jedną z implementacji algorytmu Dijkstry. Wszystkie uwagi dotyczące użytej liczby krawędzi i wierzchołków, sposobu wykonywania wykresów itp. jak w opisie przykładowego zadania 3. 7. Jak można zmodyfikować funkcję Kruskal(), by zmniejszyć jej złożoność obliczeniową? Proszę wprowadzić tę modyfikację, przetestować kod (aby uzyskać pewność, że tworzy drzewo o identycznej wadze sumarycznej, jak wersja oryginalna) i porównać z oryginałem pod względem czasu wykonania. Na tej podstawie oszacować złożoność obliczeniową ulepszonej wersji i porównać ją ze złożonością oszacowaną teoretycznie. Sprawozdanie Sprawozdanie powinno zawierać następujące elementy: Opis przeprowadzonych badań wraz z uzasadnieniem wyboru użytych parametrów, np. liczby wierzchołków i krawędzi grafu. Porównanie złożoności czasowej użytych algorytmów: wykresy w odpowiedniej skali, zależności 5

czasu obliczeń i zajętości pamięci od liczby wierzchołków i krawędzi (notacja O). Należy porównać zaobserwowaną złożoność z teoretyczną, tj. oszacowaną na podstawie analizy kodu. Należy zastanowić się, w jakich sytuacjach uwidaczniają się mocne strony poszczególnych algorytmów (lub wersji tego samego algorytmu). Listingi zmodyfikowanych funkcji (lub ich najistotniejszych fragmentów) z wyraźnie wyróżnionymi własnymi modyfikacjami. Zmodyfikowane funkcje powinny być najpierw przetestowane pod względem poprawności działania, zaś wpływ modyfikacji na działanie (czas wykonania, złożoność obliczeniową i pamięciową itp.) powinien zostać opisany. Dodatek Poniżej opisane są w skrócie ważniejsze pliki, klasy i funkcje tworzące programy minpath i mst. Program minpath najważniejsze klasy AbstractGraph klasa abstrakcyjna opisująca interfejs konkretnych klas reprezentujących graf skierowany. Dzięki polimorfizmowi funkcje operujące na różnych reprezentacjach grafów mogą mieć wspólny interfejs, co znacznie upraszcza kod korzystający z tych funkcji. Klasa Digraph klasa dziedzicząca po AbstractGraph. Implementacja grafu skierowanego jako macierzy przyległości (sąsiedztwa). DigraphL klasa dziedzicząca po AbstractGraph. Jest to również graf skierowany, lecz zaimplementowany jako m-elementowa tablica wskaźników na listy (klasa List) krawędzi (wskaźników na obiekty klasy HalfEdge). Obie klasy są opisane poniżej. HalfEdge krawędź opisana parą liczb naturalnych: indeksem wierzchołka końcowego i wagą. Wierzchołek początkowy jest dany niejawnie odpowiada indeksowi tablicy, pod którym znajduje się wskaźnik na listę, do której należy dany obiekt HalfEdge. List klasa implementująca listę jednokierunkową obiektów klasy ListElement. Obie klasy są szablonami, dzięki czemu element może przechowywać obiekt dowolnego typu (w tym także typy prymitywne, np. int). Lista posiada wskaźnik (current) na bieżący element, początkowo wskazujący na element pierwszy. Funkcja getnext() pobiera wskazywany element (bez usuwania go z listy) i przesuwa wskaźnik o jedną pozycję. Funkcja hasmoreelements() pozwala sprawdzić, czy wskaźnik nie wykroczył poza listę, natomiast rewind() cofa go na jej początek, co pozwala na kolejne przejście listy. Nowy element można dodać do końca listy funkcją append(), której argumentem może być obiekt dowolnej klasy lub typ prymitywny. Vertex struktura reprezentująca wierzchołek grafu, wykorzystywana przez algorytm Dijkstry. Zawiera parę liczb naturalnych: index indeks (numer) wierzchołka i dist jego wagę, tzn. odległość od wierzchołka początkowego. DistanceTable klasa przechowująca informacje o kolejnych wierzchołkach: ich wagi (tablica dists) oraz 6

flagi informujące o tym, czy algorytm Dijkstry traktuje te wagi jako tymczasowe, czy ustalone (tablica fixed). Funkcja getclosest() zwraca indeks wierzchołka o najmniejszej spośród nieustalonych wag. PriorityQueue kolejka priorytetowa wierzchołków, przy czym kluczem jest ich odległość od wierzchołka początkowego. Kolejka składa się z dwóch tablic. Tablica vertices przechowuje kopiec minimalny (tzn. z najmniejszym kluczem w korzeniu) wskaźników na struktury Vertex. Tablica offsets zawiera pozycje kolejnych wierzchołków znajdujących się w tablicy vertices. Odwołanie do k-go elementu tablicy offsets umożliwia więc znalezienie w kopcu wierzchołka o indeksie k w czasie O(1). Funkcja removeclosest() zwraca element o najmniejszym kluczu i usuwa go z kopca. Funkcja decreasekey() realizuje kolejną operację uaktualnienie klucza wierzchołka o podanym indeksie. Po każdej z tych operacji automatycznie przywracana jest własność kopca. MinpathMain główna klasa programu. Zawiera funkcje implementujące cztery wersje algorytmu Dijkstry, algorytm Floyda oraz algorytm przeszukujący całą przestrzeń rozwiązań, tzn. testujący wszystkie możliwe drogi. Te funkcje to: Dijkstra implementacja podstawowa. Operuje na grafie zapisanym w postaci macierzy sąsiedztwa, zaś wagi wierzchołków przechowuje w tablicy (wewnętrznej tablicy obiektu klasy DistanceTable). DijkstraPQ również wykorzystuje graf w postaci macierzy sąsiedztwa, lecz tymczasowe wagi wierzchołków przechowuje w kolejce priorytetowej obiekcie klasy PriorityQueue. Po nadaniu wierzchołkowi wagi ustalonej jest on usuwany z kolejki. DijkstraList funkcja operująca na grafie zapisanym w postaci tablicy list krawędzi (obiekt klasy DigraphL). Wagi wierzchołków są przechowywane w tablicy identycznie, jak w przypadku funkcji Dijkstra. DijkstraListPQ operuje na obiekcie klasy DigraphL, natomiast tymczasowe wagi wierzchołków przechowuje w kolejce priorytetowej. Floyd typowa implementacja. UWAGA! Funkcja nadpisuje macierz przyległości grafu wejściowego, umieszczając w niej ostatecznie macierz odległości między wierzchołkami. DFS funkcja znajdująca wszystkie możliwe drogi w grafie poprzez przeszukiwanie w głąb. Dzięki temu, że obie reprezentacje grafu (tablicowa i listowa) dziedziczą po AbstractGraph, wszystkie wersje algorytmu Dijkstry mają wspólny interfejs: otrzymują wskaźnik na AbstractGraph, indeksy wierzchołków początkowego i końcowego oraz flagę findallpaths decydującą, czy mają być szukane najkrótsze drogi między zadaną parą, czy między wszystkimi parami wierzchołków. Identyczny interfejs dla funkcji Floyd i DFS zapewniają funkcje opakowujące RunFloyd i RunDFS. Dodatkowo plik zawiera funkcje pomocnicze: FindShortestPathDijkstra() oraz FindShortestPath() dla algorytmów Floyda i DFS. Pozwalają one na pomiar czasu wykonania algorytmu i wyświetlenie informacji podanych na wstępie tego punktu: komentarzy, czasu wykonania i długości znalezionej drogi (dróg). 7

Program mst najważniejsze klasy i pliki Klasa Forest implementuje abstrakcyjną strukturę danych przechowującą rozłączne podzbiory, a znaną powszechnie jako Disjoint Subsets lub Union-Find. Dla struktury takiej zdefiniowane muszą być dwie standardowe operacje: określenia podzbioru, do którego należy dany element (Find) oraz łączenia dwóch podzbiorów (operacja Union). W przypadku algorytmów MST wspomniane podzbiory odpowiadają tworzonym poddrzewom, zaś ich elementami są wierzchołki grafu. Sama struktura jest zaś niezbędna, by podczas dodawania do MST kolejnych gałęzi ustrzec się przed powstaniem cyklu. Takie bezpieczne dodawanie gałęzi realizuje metoda addedge(edge). Operacja ta kończy się powodzeniem tylko wtedy, gdy spowoduje połączenie dwóch drzew w skrajnym przypadku składających się z pojedynczych wierzchołków. W przeciwnym przypadku funkcja zwraca wartość false, a gałąź edge nie zostaje dodana, gdyż spowodowałoby to powstanie cyklu w którymś z istniejących drzew. Klasa Edge krawędź grafu nieskierowanego jako trójka liczb naturalnych: indeksy wierzchołków i waga krawędzi. Klasa Graph implementacja grafu nieskierowanego jako tablicy wskaźników na obiekty typu Edge. Plik MST_Algorithms zawiera funkcje realizujące algorytmy Kruskala i Borůvki. Każda z tych funkcji operuje na obiekcie klasy Graph i zwraca wskaźnik na tablicę wskaźników do obiektów klasy Edge krawędzi wchodzących w skład znalezionego drzewa. Plik MST_Utils zawiera m.in. funkcje FindMST i PrintMST. Pierwsza z nich opakowuje algorytm MST (przekazany jako wskaźnik na odpowiednią funkcję), mierząc czas jego wykonania i wyświetlając informacje dodatkowe. Może także wypisywać stworzone drzewo krawędź po krawędzi (za pomocą PrintMST), jeśli użytkownik wywoła ją z ustawioną flagą printtree i jeśli liczba krawędzi drzewa MST nie przekracza MAX_PRINTABLE_E. Ostatnia modyfikacja 18.05.2016 Opracował dr inż. Dominik Kasprowicz dkasprow@imio.pw.edu.pl 8