Wstęp do Programowania potok funkcyjny

Podobne dokumenty
Wstęp do Programowania potok funkcyjny

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

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

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

Wstęp do Programowania potok funkcyjny

Ogólne wiadomości o grafach

Wstęp do Programowania potok funkcyjny

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

Wstęp do Programowania potok funkcyjny

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

Algorytmy i Struktury Danych.

Matematyczne Podstawy Informatyki

Sortowanie topologiczne skierowanych grafów acyklicznych

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

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

Algorytmiczna teoria grafów

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

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

Wstęp do programowania. Drzewa. Piotr Chrząstowski-Wachtel

Wstęp do Programowania potok funkcyjny

prowadzący dr ADRIAN HORZYK /~horzyk tel.: Konsultacje paw. D-13/325

Digraf. 13 maja 2017

Programowanie obiektowe

Wstęp do programowania

Literatura. 1) Pojęcia: złożoność czasowa, rząd funkcji. Aby wyznaczyć pesymistyczną złożoność czasową algorytmu należy:

Wstęp do Programowania potok funkcyjny

Wstęp do programowania. Zastosowania stosów i kolejek. Piotr Chrząstowski-Wachtel

Wykład 10 Grafy, algorytmy grafowe

Programowanie obiektowe

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

TEORETYCZNE PODSTAWY INFORMATYKI

. Podstawy Programowania 2. Algorytmy dfs i bfs. Arkadiusz Chrobot. 2 czerwca 2019

Wstęp do Programowania potok funkcyjny

Wstęp do Programowania potok funkcyjny

Algorytmy i Struktury Danych

Algorytm DFS Wprowadzenie teoretyczne. Algorytm DFS Wprowadzenie teoretyczne. Algorytm DFS Animacja. Algorytm DFS Animacja. Notatki. Notatki.

Sztuczna Inteligencja i Systemy Doradcze

Rozwiązywanie problemów metodą przeszukiwania

Programowanie dynamiczne cz. 2

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

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

Wykład 7. Algorytmy grafowe

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

Algorytmy i Struktury Danych.

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

Wysokość drzewa Głębokość węzła

Struktury danych (I): kolejka, stos itp.

Matematyka dyskretna

Drzewa rozpinajace, zbiory rozłaczne, czas zamortyzowany

Wykład 5 Wybrane zagadnienia programowania w C++ (c.d.)

Temat: Struktury danych do reprezentacji grafów. Wybrane algorytmy grafowe.

Wstęp do Programowania potok funkcyjny


Algorytmy Grafowe. dr hab. Bożena Woźna-Szcześniak, prof. UJD. Wykład 1,2,3. Uniwersytet Humanistyczno-Przyrodniczy im. Jana Długosza w Częstochowie

Podstawy programowania 2. Temat: Drzewa binarne. Przygotował: mgr inż. Tomasz Michno

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

Podstawowe własności grafów. Wykład 3. Własności grafów

Kolejka priorytetowa. Często rozważa się kolejki priorytetowe, w których poszukuje się elementu minimalnego zamiast maksymalnego.

Grafy (3): drzewa. Wykłady z matematyki dyskretnej dla informatyków i teleinformatyków. UTP Bydgoszcz

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

Struktury danych: stos, kolejka, lista, drzewo

Stos LIFO Last In First Out

Wstęp do programowania

Grafy w MATLABie. LABORKA Piotr Ciskowski

Algorytmy i Struktury Danych

Egzaminy i inne zadania. Semestr II.

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

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

Wstęp do Programowania potok funkcyjny

TEORETYCZNE PODSTAWY INFORMATYKI

Wykład 8. Moduły. Zdzisław Spławski Programowanie funkcyjne 1

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

Znajdowanie wyjścia z labiryntu

Matematyczne Podstawy Informatyki

Dynamiczny przydział pamięci w języku C. Dynamiczne struktury danych. dr inż. Jarosław Forenc. Metoda 1 (wektor N M-elementowy)

Wprowadzenie do Sztucznej Inteligencji

Drzewa poszukiwań binarnych

Drzewo. Drzewo uporządkowane ma ponumerowanych (oznaczonych) następników. Drzewo uporządkowane składa się z węzłów, które zawierają następujące pola:

E: Rekonstrukcja ewolucji. Algorytmy filogenetyczne

Abstrakcyjne struktury danych - stos, lista, drzewo

Lista 0. Kamil Matuszewski 1 marca 2016

Graf. Definicja marca / 1

Wykłady z Matematyki Dyskretnej

Programowanie w VB Proste algorytmy sortowania

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

Algorytmy i Struktury Danych.

Wstęp do programowania. Listy. Piotr Chrząstowski-Wachtel

INFORMATYKA DANE.

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

ZASADY PROGRAMOWANIA KOMPUTERÓW ZAP zima 2014/2015. Drzewa BST c.d., równoważenie drzew, kopce.

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

E S - uniwersum struktury stosu

Algorytmy i Struktury Danych.

Algorytmy z powracaniem

Algorytmy i Struktury Danych

Drzewa binarne. Drzewo binarne to dowolny obiekt powstały zgodnie z regułami: jest drzewem binarnym Jeśli T 0. jest drzewem binarnym Np.

Wprowadzenie do Sztucznej Inteligencji

Siedem cudów informatyki czyli o algorytmach zdumiewajacych

Wstęp do sieci neuronowych, wykład 11 Łańcuchy Markova

Algorytmy i Struktury Danych.

Transkrypt:

Wstęp do Programowania potok funkcyjny Marcin Kubica 2010/2011

Outline 1 BFS DFS Algorytm Dijkstry Algorytm Floyda-Warshalla

Podstawowe pojęcia Definition Graf = wierzchołki + krawędzie. Krawędzie muszą mieć różne końce. Między dwoma wierzchołkami może być co najwyżej jedna krawędź. Inne warianty: grafy z pętelkami, multi-grafy. Nieskierowane i skierowane. Krawędź nieskierowaną utożsamiamy z parą krawędzi skierowanych w obu kierunkach. Zakładamy, że wierzchołki są ponumerowane liczbami całkowitymi od 0 do n 1. m = liczba krawędzi.

Implementacja grafów Disclaimer Nie róbcie tego sami w domu! Albo róbcie, ale tylko dla przyjemności. Są gotowe biblioteki do przetwarzania grafów, np. OCamlgraph.

Implementacja grafów Interfejs modułu implementującego grafy nieskierowane z etykietowanymi krawędziami: module type GRAPH_LABELS = sig type label end;; module type GRAPH = sig type label type graph val init : int -> graph val size : graph -> int val insert_directed_edge : graph -> int -> int -> label -> unit val insert_edge : graph -> int -> int -> label -> unit val neighbours : graph -> int -> (int*label) list end;; module type GRAPH_FUNC = functor (L : GRAPH_LABELS) -> GRAPH with type label = L.label;;

Sposoby implementacji grafów Macierz sąsiedztwa: Macierz n n wartości logicznych. E[v, w] istnieje krawędź z v do w. nieskierowane macierz jest symetryczna. Złożoność pamięciowa Θ(n 2 ). Dodanie, usunięcie, sprawdzenie krawędzi czas Θ(1). Lista sąsiadów czas Θ(n). Wolniejsza alternatywa: Map + Set

Sposoby implementacji grafów Listy incydencji: Lista wszystkich sąsiadów danego wierzchołka. Graf = tablica list sąsiedztwa. Dodanie nowej krawędzi czas Θ(1). Złożoność pamięciowa Θ(n + m). Usunięcie, sprawdzenie krawędzi czas Θ(stopień). Lista sąsiadów czas Θ(1). Zastosujemy listy sąsiedztwa.

Implementacja grafów module Graph : GRAPH_FUNC = functor (L : GRAPH_LABELS) -> struct type label = L.label type graph = {n : int; e : (int*label) list array} let init s = {n = s; e = Array.make s []} let size g = g.n let insert_directed_edge g x y l = g.e.(x) <- (y,l)::g.e.(x) let insert_edge g x y l = insert_directed_edge g x y l; insert_directed_edge g y x l let neighbours g x = g.e.(x) end;;

Definition Problem przeszukiwania grafu: Odwiedzamy wierzchołki grafu w określonej kolejności. Po kolei przechodzimy spójne składowe grafu. Kolejności w obrębie składowej przyjrzymy się dalej. W każdym wierzchołku wywołujemy procedurę visit, która jest parametrem procedury przeszukującej. Każdą składową oznaczamy innym kolorem liczbą całkowita od 0 wzwyż. Kolor ów jest parametrem procedury visit. Wynikiem procedury przeszukującej graf jest liczba spójnych składowych.

Przeszukiwanie grafu możemy zrealizować jako funktor: module GraphSearch (Q : QUEUE) (G : GRAPH) = struct open G let search visit g = let visited = Array.make (size g) false in let walk x color =... in let k = ref 0 in for i = 0 to size g - 1 do if not visited.(i) then begin walk i (!k); k :=!k+1 end; done;!k end;;

let walk x color = let q = ref Q.empty in begin q := Q.insert!q x; visited.(x) <- true; while not (Q.is_empty!q) do let v = Q.front!q in begin visit v color; q := Q.remove!q; List.iter (fun (y, _) -> if not (visited.(y)) then begin q := Q.insert!q y; visited.(y)<- true end) (neighbours g v); end done end

n = liczba wierzchołków w grafie. m = liczba krawędzi w grafie. Każdy wierzchołek jest rozpatrywany tyle razy ile wychodzi z niego krawędzi plus co najwyżej jedno wywołanie search. Rozmiar kolejki nie przekroczy n. Złożoność pamięciowa (nie licząc grafu) = Θ(n). Złożoność czasowa zależy od czasu, w jakim działają operacje na kolejce. Zakładając, że działają w czasie stałym (zamortyzowanym) mamy złożoność czasową Θ(n + m).

BFS Przeszukiwanie grafu wszerz: Idea algorytmu przypomina pożar prerii. Jeśli ogień dojdzie do jakiegoś miejsca, to rozprzestrzenia się na sąsiednie miejsca, na których jest jeszcze niewypalona trawa. Przyłożenie ognia w jednym punkcie, spala cały spójny obszar suchej trawy. Źródło pierwszy odwiedzony wierzchołek w każdej spójnej składowej. Kolejność odwiedzania: najpierw źródło, potem jego sąsiedzi, potem ich sąsiedzi itd.

BFS Wierzchołki są odwiedzane w kolejności rosnącej odległości od źródła. Wierzchołki trzymamy w kolejce FIFO. module BFS = GraphSearch (Fifo);;

BFS Operacje na kolejce FIFO są wykonywane w stałym czasie zamortyzowanym. Tak więc złożoność czasowa przeszukiwania = Θ(n + m). W każdej chwili, w kolejce q znajdują się: albo wierzchołki położone w tej samej odległości l od źródła, albo najpierw wierzchołki położone w odległości l a dalej w odległości l + 1 od źródła. W każdej spójnej składowej wierzchołki są odwiedzane zgodnie z rosnącymi odległościami od źródła.

DFS Przeszukiwanie grafu w głąb: Przeszukiwanie z nawrotami. Procedurę przeszukującą wywołujemy dla źródła. Następnie jest ona rekurencyjnie wywoływana dla jego sąsiadów, ich sąsiadów itd. Przy pierwszym wywołaniu wierzchołek jest odwiedzany. Implementacja tej procedury nie musi być rekurencyjna. Może być iteracyjna, jeżeli zastosujemy stos, czyli kolejkę LIFO (ang. last in, first out).

DFS module Lifo : QUEUE = struct exception EmptyQueue type a queue = a list let empty = [] let is_empty q = q = [] let insert q x = x::q let front q = if q = [] then raise EmptyQueue else hd q let remove q = if q = [] then raise EmptyQueue else tl q end;; module DFS = GraphSearch (Lifo);;

DFS Algorytm DFS ma wiele ciekawych własności. Pozwala on na znalezienie w grafie cykli prostych, a także na podział grafu na dwuspójne składowe. Wyobraźmy sobie drzewa zbudowane z tych krawędzi, które łączą odwiedzane wierzchołki i wierzchołki, które przy tej okazji są wstawiane do kolejki. Są to drzewa rozpinające spójne składowe grafu. Korzeniem drzewa jest źródło. Zastanówmy się jak mogą być położone względem takiego drzewa pozostałe krawędzie grafu.

DFS Krawędzie spoza drzewa muszą prowadzić w górę drzewa. Krawędź spoza drzewa nie może łączyć dwóch gałęzi drzewa. Gdyby tak było, to w trakcie obchodzenia pierwszej gałęzi powinniśmy wstawić do kolejki wierzchołek leżący na drugim końcu krawędzi.

DFS Z każdą krawędzią spoza drzewa związany jest cykl, złożony z niej samej oraz ścieżki w drzewie łączącej jej końce. Okazuje się, że dowolny cykl prosty w grafie możemy przedstawić jednoznacznie, jako zbiór krawędzi spoza drzewa rozpinającego DFS.

Sortowanie topologiczne Definition Problem sortowania topologicznego: Mamy do wykonania n różnych czynności. Niektóre z nich muszą być wykonane wcześniej, a niektóre później. Mamy danych m par zależności postaci: czynność a musi być wykonana wcześniej niż czynność b. Należy ustalić taką kolejność wykonywania czynności, żeby wszystkie zależności były spełnione, lub stwierdzić, że nie jest to możliwe. Dane możemy przedstawić jako graf skierowany. Czynności to wierzchołki, a zależności to krawędzie.

Sortowanie topologiczne Do rozwiązania problemu możemy użyć zmodyfikowanej wersji algorytmu DFS. Możemy przechodzić wzdłuż krawędzi tylko zgodnie z ich kierunkiem. Każdy wierzchołek odwiedzamy dwa razy: najpierw do niego wchodzimy, następnie odwiedzamy wszystkich jego sąsiadów, na koniec z niego wychodzimy. Obejście kartezjańskie drzewa. Wzdłuż każdej krawędzi przechodzimy dwa razy.

Sortowanie topologiczne Jak mogą być położone krawędzie spoza drzewa rozpinającego DFS? Krawędzie drzewa rozpinającego DFS są zorientowane od korzenia w dół. Jeśli istnieje krawędź prowadzącą w górę drzewa, to istnieje cykl w grafie i rozwiązanie nie istnieje. Krawędź taka prowadzi do wierzchołka, do którego weszliśmy, ale z którego jeszcze nie wyszliśmy. Jeśli napotkamy krawędź prowadzącą do wierzchołka, z którego już wyszliśmy, to jest to wierzchołek leżący na innej gałęzi drzewa rozpinającego.

Sortowanie topologiczne Reprezentowaną przez niego czynność należy wykonać później. Dotyczy to również wszystkich innych wierzchołków, do których można z niego dojść. Zauważmy, że one również zostały już odwiedzone i z nich wyszliśmy. Jeśli przechodzimy krawędź i odwiedzamy nowy wierzchołek, to wejdziemy do niego później, ale wyjdziemy z niego wcześniej, niż z wierzchołka, w którym jesteśmy.

Sortowanie topologiczne Z wierzchołków reprezentujących czynności, które należy wykonać później wyjdziemy wcześniej niż z danego wierzchołka. Wystarczy wykonywać czynności w odwrotnej kolejności wychodzenia z wierzchołków.

Algorytm Dijkstry Graf nieskierowany, w którym krawędzie mają długości. Długości krawędzi są nieujemnymi liczbami rzeczywistymi. Algorytm Dijkstry służy do wyznaczania odległości od źródła do pozostałych wierzchołków, Odwiedzane wierzchołki trzymamy w kolejce priorytetowej. Priorytety odpowiadają odległości od źródła.

Algorytm Dijkstry Wierzchołki zaznaczamy jako odwiedzone dopiero po wyjęciu z kolejki. Wyjmując upewniamy się, czy wcześniej już danego wierzchołka nie odwiedziliśmy. Wierzchołki mogą być wstawiane do kolejki priorytetowej wielokrotnie, jednak nie więcej niż łącznie m razy. Może się zdarzyć, że wstawiając wierzchołek do kolejki po raz kolejny, nadamy mu mniejszą wagę i zostanie on wcześniej wyjęty z kolejki. Patrząc na pierwsze wyjęcia z kolejki wierzchołków, wyjmujemy je w kolejności rosnącej odległości od źródła.

Algorytm Dijkstry module FloatLabel = struct type label = float end;; module FloatGraph = Graph(FloatLabel);;

Algorytm Dijkstry module Dijkstra : sig val search : FloatGraph.graph -> int -> float array end = struct open FloatGraph module Order = struct type t = int * float let porownaj (v1, d1) (v2, d2) = if (d1, v1) < (d2, v2) then Wieksze else if (d1, v1) = (d2, v2) then Rowne else Mniejsze end module Q = PriQueue (Order)

Algorytm Dijkstry let search g v = let visited = Array.make (size g) false and dist = Array.make (size g) infinity and q = ref Q.empty in begin q := Q.put (v, 0.0)!q; while not (Q.is_empty!q) do let (v, d) = Q.getmax!q in begin q := Q.removemax!q; if not visited.(v) then begin List.iter (fun (w, l) -> q := Q.put (w, d +. l)!q) (neighbours g v); dist.(v) <- d; visited.(v)<- true end; end; done; dist end end;;

Algorytm Dijkstry Koszt czasowy operacji na kolejce priorytetowej jest rzędu Θ(log n). Koszt czasowy algorytmu jest rzędu Θ((n + m) log n). Koszt pamięciowy jest rzędu Θ((n + m)).

Algorytm Floyda-Warshalla Zastosowanie: znalezienie odległości między wszystkimi parami wierzchołków. Graf dany jako macierz długości krawędzi. Graf może być skierowany. Długości krawędzi mogą być ujemne. Brak cykli ujemnej długości. Poszerzamy zbiór możliwych pośrednich wierzchołków, od pustego, do wszystkich wierzchołków.

Algorytm Floyda-Warshalla let distances g = let open Array in let n = length g in let dist = init n (fun i -> copy (g.(i))) in begin assert (length g.(0) = n); for k = 0 to n - 1 do for i = 0 to n - 1 do for j = 0 to n - 1 do dist.(i).(j) <- min dist.(i).(j) (dist.(i).(k) + dist.(k).(j)) done done done; dist end;;

Algorytm Floyda-Warshalla a mnożenie macierzy Złożoność czasowa Θ(n 3 ), pamięciowa Θ(n 2 ). Odległości w grafie a mnożenie macierzy (założenie: nieujemne wagi) Zamiast mnożenia, dodawanie, zamiast dodawania, min, zero jest jedynką, jest zerem. Potrzeba log n mnożeń macierzy n n. Używając algorytmu mnożenia macierzy szybszego niż O(n 3 ), można uzyskać algorytm szybszy niż Floyda-Warshalla. Algorytm Strassena Θ(n 2.807 ) lepsze (tylko teoretycznie) algorytmy są znane Θ(n 2.3... )

Gotowa implementacja OCamlgraph (http://ocamlgraph.lri.fr/)