VB - algorytmy Zadanie4_4 autor: Janusz Białowąs 90 min Szukanie minimalnego drzewa rozpinającego – algorytm Prima OPIS ZADANIA Algorytm Prima to zadanie polegające na utworzeniu prostej aplikacji szukającej minimalnego drzewa rozpinającego dla grafu ważonego wprowadzonego z pliku tekstowego. Celem zadania jest rozwiązanie jednego z podstawowych problemów algorytmicznych teorii grafów, wykorzystując algorytm Prima. EFEKT KOŃCOWY Efektem końcowym zadania jest aplikacja przedstawiona na ilustracji poniżej. Pokazuje ona listy sąsiedztwa grafu wprowadzonego z pliku tekstowego oraz minimalne drzewo rozpinające dla niego wraz z jego kosztem. - Aby wykonać zadanie, niezbędne jest wykorzystanie plików graf.txt, graf1.txt. graf2.txt REALIZACJA ZADANIA Wykonując to zadanie, dowiesz się, jak odszukać minimalne drzewo rozpinające grafu za pomocą algorytmu Prima. Wykorzystasz struktury danych ArrayList oraz Structure do przechowywania danych. Podczas realizacji zadania skoncentruj się na implementacji algorytmu oraz wykorzystanych strukturach danych. Uwaga: W kolejnych krokach scenariusza nowo dodany kod oznaczony został czcionką pogrubioną. Niepogrubione linie kodu mają służyć wyłącznie do oznaczenia miejsca, w którym należy wpisać nowy kod. Algorytmika i programowanie 1 VB - algorytmy autor: Janusz Białowąs Krok 1: Tworzenie projektu aplikacji i formularza 1. Utwórz nowy projekt aplikacji Windows o nazwie Grafy-Prim w folderze wskazanym przez nauczyciela. 2. Ustaw następujące właściwości formularza. Właściwość Wartość Text Minimalne drzewo rozpinające - algorytm Prima 3. Wstaw do formularza dwa pola tekstowe TextBox., dwie etykiety Label oraz dwa przyciski Button. Dla pól TextBox ustaw wartości właściwości MultiLine oraz ReadOnly na True. Ustaw właściwości Name i Text wstawionych formantów według tabeli poniżej. Typ kontrolki Name Text Label Label1 Koszt drzewa Label lblKosztDrzewa TextBox txtListy_s TextBox txtDrzewoMinimalne Button btnDrzewoSzukaj Szukaj minimalnego drzewa rozpinającego Button btnKoniec Wyjście 4. Ustaw dla pól typu TexBox właściwość ScrollBars na Both. 5. Rozmieść kontrolki podobnie jak na rysunku powyżej, pamiętając, że kontrolka txtListy_s znajduje się po lewej stronie formularza. 6. Skopiuj do folderu rozwiązania plik graf.txt, graf1.txt, graf2.txt z lokalizacji wskazanej przez nauczyciela. Krok 2: Deklaracja zmiennych globalnych w aplikacji 1. Przejdź do edytora kodu (F7) i zadeklaruj tablicę Listy_s typu ArrayList przechowującą listy sąsiedztwa grafu. Public Class form1 Inherits System.Windows.Forms.Form Dim Listy_s() As ArrayList 2. Zadeklaruj i zainicjuj obiekt o nazwie kopiec typu ArrayList, służący do przechowywania krawędzi, wśród których będziemy szukać najtańszej. Inherits System.Windows.Forms.Form Dim Listy_s() As ArrayList Dim kopiec As New ArrayList() 3. Zadeklaruj tablicę odwiedzone o zerowym rozmiarze typu Boolean, służącą do odznaczania wierzchołków, które zostały już odwiedzone („zrobione”). Dim Listy_s() As ArrayList Dim kopiec As New ArrayList() Dim odwiedzone() As Boolean Algorytmika i programowanie 2 VB - algorytmy autor: Janusz Białowąs 4. Zadeklaruj i zainicjuj obiekt drzewo_minialne typu ArrayList, służący do zapamiętania minimalnego drzewa rozpinającego grafu. Dim kopiec As New ArrayList() Dim odwiedzone() As Boolean Dim drzewo_minimalne As New ArrayList() 5. Zadeklaruj zmienną IleWierzcholkow, typu Integer służącą do zapamiętania informacji o ilości wierzchołków w grafie. Dim odwiedzone() As Boolean Dim drzewo_minimalne As New ArrayList() Dim ileWierzcholkow As Integer 6. Zadeklaruj zmienną w typu krawedz_g (typ krawedz_g jest strukturą, która będzie utworzona w dalszej części programu). Dim drzewo_minimalne As New ArrayList() Dim ileWierzcholkow As Integer Dim w As krawedz_g 7. Zadeklaruj stałą o nazwie plik, przechowującą informacje o lokalizacji i nazwie pliku tekstowego zawierającego opis grafu (lokalizacja wskazana w stałej odwołuje się do folderu rozwiązania). Dim ileWierzcholkow As Integer Dim w As krawedz_g Const plik As String = "../graf.txt" Krok 3: Utworzenie struktur do przechowywania danych o grafie 1. Zadeklaruj strukturę o nazwie krawedz_g. Private Structure krawedz_g End Structure 2. Zadeklaruj wewnątrz struktury pola, które będą ją tworzyły. Private Structure krawedz_g Dim wierzcholek As Integer Dim koszt_k As Integer 3. Zadeklaruj strukturę o nazwie e_kopca. Private Structure e_kopca End Structure 4. Zadeklaruj wewnątrz struktury pola, które będą ją tworzyły. Private Structure e_kopca Dim w_a As Integer Dim w_b As Integer Dim koszt_k As Integer Algorytmika i programowanie 3 VB - algorytmy autor: Janusz Białowąs Krok 4: Dodanie procedur odczytujących opis grafu z pliku tekstowego 1. Utwórz procedurę o nazwie odczyt_z_pliku. Sub odczyt_z_pliku() End Sub 2. Zadeklaruj zmienne potrzebne w procedurze. Sub Dim Dim Dim Dim Dim odczyt_z_pliku() nrPliku As Integer wierzcholki() As String wiersz As String i, j As Integer temp As krawedz_g 3. Otwórz blok kodu chronionego pozwalający na obsługę błędów, mogących wystąpić przy odczycie danych z pliku. Dim w1, w2 As Integer Dim temp As krawedz_g Try 4. Otwórz plik tekstowy, którego nazwa jest zapamiętana pod stałą o nazwie plik. Dim w1, w2 As Integer Dim temp As krawedz_g Try nrPliku = FreeFile() FileOpen(nrPliku, plik, OpenMode.Input) 5. Odczytaj z pliku liczbę wierzchołków i zmień rozmiar tablicy o nazwie Listy_s według tej wartości. nrPliku = FreeFile() FileOpen(nrPliku, plik, OpenMode.Input) ileWierzcholkow = LineInput(nrPliku) ReDim Listy_s(ileWierzcholkow) 6. Zainicjuj elementy tablicy Listy_s. ileWierzcholkow = LineInput(nrPliku) ReDim Listy_s(ileWierzcholkow) For i = 1 To ileWierzcholkow Listy_s(i) = New ArrayList() Next 7. Utwórz listy sąsiedztwa grafu, wczytując kolejne wiersze pliku tekstowego zawierające opis krawędzi i jej koszt. Wykorzystaj do zapamiętania sąsiedniego wierzchołka oraz kosztu krawędzi strukturę krawedz_g. Listy_s(i) = New ArrayList() Next Algorytmika i programowanie 4 VB - algorytmy autor: Janusz Białowąs Do While Not EOF(nrPliku) wiersz = LineInput(nrPliku) wierzcholki = wiersz.Split(" ", Chr(13)) w.wierzcholek = wierzcholki(1) w.koszt_k = wierzcholki(2) Listy_s(CInt(wierzcholki(0))).Add(w) w.wierzcholek = wierzcholki(0) w.koszt_k = wierzcholki(2) Listy_s(CInt(wierzcholki(1))).Add(w) Loop 8. Zamknij plik tekstowy oraz wyczyść i uaktywnij pole txtListy_s. Listy_s(CInt(wierzcholki(1))).Add(w) Loop FileClose(nrPliku) txtListy_s.Clear() txtListy_s.Visible = True 9. Wyświetl w polu tekstowym listy sąsiedztwa grafu wraz z kosztami krawędzi. txtListy_s.Clear() txtListy_s.Visible = True For i = 1 To ileWierzcholkow txtListy_s.Text = txtListy_s.Text & " wierzchołek (" & i & ") = " j = 0 Do While Listy_s(i).Count > j temp = Listy_s(i).Item(j) txtListy_s.Text = txtListy_s.Text & temp.wierzcholek & "(" & temp.koszt_k & ") " j += 1 Loop txtListy_s.Text = txtListy_s.Text & vbCrLf Next 10. Obsłuż typowe błędy mogące wystąpić podczas odczytu danych z pliku i zakończ blok kodu chronionego poleceniem End Try. txtListy_s.Text = txtListy_s.Text & vbCrLf Next Catch ex As Exception MessageBox.Show("Błąd odczytu z pliku", "Błąd", MessageBoxButtons.OK, MessageBoxIcon.Error) btnDrzewoSzukaj.Enabled = False txtListy_s.Text = "Błąd odczytu z pliku" End Try 11. Utwórz procedurę obsługi zdarzenia Load formularza: z listy rozwijalnej Class Name wybierz opcję (Base Class Events), zaś z listy Method Name wybierz zdarzenie Load. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load End Sub Algorytmika i programowanie 5 VB - algorytmy autor: Janusz Białowąs 12. Wywołaj procedurę odczyt_z_pliku oraz ustaw właściwość TabIndex przycisku btnDrzewoSzukaj na 0. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load odczyt_z_pliku() btnDrzewoSzukaj.TabIndex = 0 Krok 5: Tworzenie procedury tworzącej kopiec krawędzi sąsiadujących z odwiedzonymi już wierzchołkami. 1. Przejdź do edytora kodu i utwórz procedurę o nazwie na_kopiec z jednym parametrem przekazywanym przez wartość. Sub na_kopiec(ByVal nr_wezla As Integer) End Sub 2. Zadeklaruj zmienne potrzebne w procedurze. Sub na_kopiec(ByVal nr_wezla As Integer) Dim j As Integer = 0 Dim element As e_kopca 3. Dołącz do kopca wszystkie elementy listy sąsiedztwa wskazanego w parametrze wierzchołka. Dim j As Integer = 0 Dim element As e_kopca For j = 0 To Listy_s(nr_wezla).Count - 1 element.w_a = nr_wezla w = Listy_s(nr_wezla).Item(j) element.w_b = w.wierzcholek element.koszt_k = w.koszt_k kopiec.Add(element) Next Krok 6: Tworzenie procedury szukającej najtańszej krawędzi w kopcu 1. Przejdź do edytora kodu i utwórz procedurę o nazwie szukaj_krawedzi z jednym parametrem przekazywanym przez referencję. Sub szukaj_krawedzi(ByRef wezel As Integer) End Sub 2. Zadeklaruj zmienne potrzebne w procedurze. Sub Dim Dim Dim Dim Dim szukaj_krawedzi(ByRef wezel As Integer) element As New e_kopca() min_koszt As Integer = 0 w1, w2 As Integer j, indeks As Integer wpis As Boolean = True Algorytmika i programowanie 6 VB - algorytmy autor: Janusz Białowąs 3. Rozpocznij pętlę Do…Loop, która wykona dodanie nowego elementu do minimalnego drzewa rozpinającego. Dim j, indeks As Integer Dim wpis As Boolean = True Do 4. Wybierz pierwszy element kopca jako krawędź o minimalnym koszcie. Dim wpis As Boolean = True Do element = kopiec.Item(0) min_koszt = element.koszt_k w1 = element.w_a w2 = element.w_b 5. Wyszukaj najtańszą krawędź na kopcu, wykorzystując algorytm szukania minimum w zbiorze. w1 = element.w_a w2 = element.w_b For j = 1 To kopiec.Count - 1 element = kopiec.Item(j) If element.koszt_k < min_koszt Then min_koszt = element.koszt_k w1 = element.w_a w2 = element.w_b indeks = j End If Next 6. Sprawdź, czy wyszukana krawędź nie tworzy cyklu w wyszukanym drzewie minimalnym (cykl wystąpi, gdy obydwa wierzchołki krawędzi były już odwiedzione). End If Next If Not (odwiedzone(w1) And odwiedzone(w2)) Then 7. Zaznacz kolejny wierzchołek jako odwiedzony i dodaj wyszukaną minimalną krawędź do drzewa minimalnego. Next If Not (odwiedzone(w1) And odwiedzone(w2)) Then rtbTekst.Cut() If Not odwiedzone(w1) Then odwiedzone(w1) = True wezel = w1 End If If Not odwiedzone(w2) Then odwiedzone(w2) = True wezel = w2 End If drzewo_minimalne.Add(kopiec.Item(indeks)) wpis = False End If Algorytmika i programowanie 7 VB - algorytmy autor: Janusz Białowąs 8. Usuń wyszukaną krawędź z kopca i zakończ pętlę Do…Loop, gdy został dokonany wpis kolejnej krawędzi tworzącej minimalne drzewo rozpinające. wpis = False End If kopiec.RemoveAt(indeks) Loop While wpis Krok 8: Tworzenie funkcji sprawdzającej czy wszystkie wierzchołki zostały odwiedzone („zrobione”) 1. Przejdź do edytora kodu i utwórz funkcję o nazwie czy_wszystkie zwracającą wartość typu Boolean. Function czy_wszystkie() As Boolean End Function 2. Zadeklaruj zmienne potrzebne w procedurze. Function czy_wszystkie() As Boolean Dim i As Integer = 1 3. Sprawdź kolejne wierzchołki, czy są ”zrobione”; jeżeli któryś nie był jeszcze odwiedzony, przerwij działania funkcji i zwróć wartość False. Function czy_wszystkie() As Boolean Dim i As Integer = 1 Do If Not odwiedzone(i) Then Return False i += 1 Loop Until i > ileWierzcholkow 4. Jeżeli wszystkie wierzchołki grafu zostały „zrobione”, zakończ działanie funkcji i zwróć wartość True. i += 1 Loop Until i > ileWierzcholkow Return True Krok 9: Tworzenie procedury szukającej minimalnego drzewa rozpinającego grafu. 1. W widoku projektu kliknij dwukrotnie przycisk btnDrzewoSzukaj, aby przejść do edytora kodu i procedury obsługi zdarzenia kliknięcia tej kontrolki. Private Sub btnDrzewoSzukaj_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDrzewoSzukaj.Click End Sub 2. Zadeklaruj zmienne lokalne w procedurze. Private Sub btnDrzewoSzukaj_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDrzewoSzukaj.Click Dim wezel As Integer = 1 Algorytmika i programowanie 8 VB - algorytmy autor: Janusz Białowąs Dim i As Integer Dim element As e_kopca Dim Koszt As Integer = 0 3. Zmień rozmiar tablicy o nazwie odwiedzone. Dim element As e_kopca Dim Koszt As Integer = 0 ReDim odwiedzone(ileWierzcholkow) 4. Zaznacz wszystkie wierzchołki grafu jako jeszcze nieodwiedzone (False). Dim Koszt As Integer = 0 ReDim odwiedzone(ileWierzcholkow) For i = 1 To ileWierzcholkow odwiedzone(i) = False Next 5. Wyczyść kontrolki txtDrzewoMinimalne oraz lblKosztDrzewa oraz wyzeruj zmienną przechowującą koszt drzewa minimalnego. odwiedzone(i) = False Next txtDrzewoMinimalne.Clear() lblKosztDrzewa.Text = "" Koszt = 0 6. Odwiedź pierwszy wierzchołek i odznacz to w tablicy o nazwie odwiedzone. lblKosztDrzewa.Text = "" Koszt = 0 odwiedzone(1) = True 7. Rozpocznij pętlę Do…Loop, w której wywoływane są procedury tworzące kopiec oraz szukające najtańszej krawędzi. Koszt = 0 odwiedzone(1) = True Do na_kopiec(wezel) szukaj_krawedzi(wezel 8. Zakończ działanie pętli, gdy wszystkie wierzchołki zostaną odwiedzone. na_kopiec(wezel) szukaj_krawedzi(wezel Loop Until czy_wszystkie() 9. Wyświetl minimalne drzewo rozpinające oraz jego koszt w odpowiednich kontrolkach. na_kopiec(wezel) szukaj_krawedzi(wezel Loop Until czy_wszystkie() For i = 0 To drzewo_minimalne.Count - 1 Algorytmika i programowanie 9 VB - algorytmy autor: Janusz Białowąs element = drzewo_minimalne.Item(i) txtDrzewoMinimalne.Text = txtDrzewoMinimalne.Text & element.w_a & " " & element.w_b & " (" & element.koszt_k & ")" & vbCrLf Koszt += element.koszt_k Next lblKosztDrzewa.Text = CInt(Koszt) Krok 10: Tworzenie procedury kończącej pracę programu 1. W widoku projektu kliknij dwukrotnie przycisk Wyjście, aby przejść do edytora kodu i procedury obsługi zdarzenia kliknięcia tej kontrolki. Private Sub btnKoniec_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnKoniec.Click End Sub 2. Zadeklaruj zmienną pytanie typu DialogResult. Private Sub btnKoniec_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnKoniec.Click Dim pytanie As DialogResult 3. Wykorzystując metodę MessageBox.Show, zadaj pytanie użytkownikowi, czy chce zakończyć pracę aplikacji.. Private Sub btnKoniec_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnKoniec.Click Dim pytanie As DialogResult pytanie = MessageBox.Show("Czy chcesz zakończyć pracę aplikacji?", "Wyjście", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) 4. Jeśli uzyskałeś pozytywną odpowiedź, zakończ pracę aplikacji. Dim pytanie As DialogResult pytanie = MessageBox.Show("Czy chcesz zakończyć pracę programu?", "Wyjście", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) If pytanie = DialogResult.Yes Then Close() End If Krok 11: Testowanie aplikacji 1. Uruchom aplikację. 2. Sprawdź, jak program wyszukuje minimalne drzewo rozpinające. 3. Sprawdź działanie aplikacji dla grafów zapisanych w plikach graf1.txt i graf2.txt. 4. Zapisz dowolny graf ważony w pliku tekstowym i przetestuj działanie programu dla tego grafu. Algorytmika i programowanie 10