Podstawy
programowania I
Wykład nr 2 Algorytmy
autor: dr inż. Michał Łabowski
▪ michal.labowski@wat.edu.pl
▪ www.mlabowski.wel.wat.edu.pl
wersja 1.0
Algorytm
2
Algorytm – ściśle określony ciąg czynności, których wykonanie prowadzi do rozwiązania jakiegoś zadania.
Sposoby zapisu algorytmów:
▪ opis słowny – ogólny opis stosowany najczęściej w początkowej fazie analizy zagadnienia, zapisany w postaci tekstu
▪ lista kroków – każda czynność, którą należy wykonać definiowana jest w postaci numerowanego kroku; pozwala na dokładny opis zagadnienia
▪ pseudokod – opis słowny algorytmu przypominający program zapisany z użyciem języka naturalnego i danego języka programowania; jego struktura odzwierciedla strukturę kodu programu, jednak przeznaczony jest do analizy przez człowieka; pomija szczegóły implementacji, takie jak deklarowanie zmiennych, przykład:
jeżeli aktualna godzina jest mniejsza od 19 to wyświetl komunikat „Dzień dobry”
w przeciwnym wypadku
wyświetl komunikat „Dobry wieczór”
▪ schemat blokowy – operacje do wykonania przedstawiane są w postaci
graficznej z użyciem standardowych symboli
Algorytm – zapis graficzny
3
Reprezentacja graficzna Opis operacji Uwagi
Początek algorytmu
Reprezentuje początek algorytmu, ma jedno wyjście (połączenie wychodzące), brak wejść (połączeń wchodzących), w jednym schemacie może funkcjonować tylko jeden taki blok.
Zakończenie algorytmu
Reprezentuje zakończenie algorytmu, ma jedno połączenie wchodzące, brak połączeń wychodzących, na schemacie może być wiele takich bloków.
Blok wejścia (wprowadzanie
danych)
Reprezentuje mechanizm wprowadzania danych, ma jedno połączenie
wchodzące i jedno połączenie
wychodzące, na schemacie może być wiele takich bloków.
Blok wyjścia (wyprowadzanie
danych)
Reprezentuje mechanizm
wyprowadzania danych, ma jedno połączenie wchodzące i jedno
połączenie wychodzące, na schemacie
może być wiele takich bloków.
Algorytm – zapis graficzny
4
Reprezentacja graficzna Opis operacji Uwagi
Blok operacji
Wykonywane są w nim operacje, m.in.
obliczenia, ma jedno połączenie wchodzące i jedno połączenie
wychodzące, w jednym bloku może istnieć więcej niż jedno wyrażenie, w jednym schemacie może istnieć więcej niż jeden blok operacji.
Blok decyzyjny
Blok odpowiedzialny za podejmowanie decyzji. Ma jedno połączenie wchodzące i dwa połączenia wychodzące: z napisem Tak jeśli warunek jest spełniony, z
napisem Nie jeśli warunek nie jest spełniony. W jednym schemacie może być wiele takich bloków.
Blok procesu Proces zdefiniowany na zewnątrz, który może być wykorzystywany wielokrotnie
Komentarz
Wprowadza komentarz ułatwiający
zrozumienie wykonywanych działań
Algorytm – zapis graficzny
5
Reprezentacja graficzna Opis operacji Uwagi
Łącznik wewnętrzny
Stosowany do łącznia fragmentów algorytmu zapisanych na tej samej
stronie. Numer powinien być taki sam w obu łączonych częściach.
Łącznik zewnętrzny
Stosowany, gdy schemat rysujemy na różnych stronach. Numer powinien być taki sam w obu łączonych częściach.
Połączenie
Łączy bloki, może być linią prostą
lub łamaną, może dochodzić do
innego połączenia
Algorytm
6
Podstawowe cechy algorytmów:
• poprawność – algorytm powinien zwracać poprawne wyniki
• deterministyczność – powinien zwracać takie samy wyniki dla tych samych danych wejściowych
• skończoność – wynik powinien być osiągany w skończonej liczbie kroków
• efektywność – wynik powinien być osiągany w jak najkrótszym czasie i z wykorzystaniem minimalnej liczby zasobów
• posiada dane wejściowe i ewentualne zmienne pomocnicze
• produkuje wynik (niekoniecznie numeryczny)
• abstrakcja – powinien nadawać się do rozwiązywania klasy zagadnień, a nie tylko konkretnego zadania (algorytm sortowania powinien równie dobrze działać na tablicy liczb całkowitych, jak i na tablicy obiektów złożonych)
• operowanie na zmiennych a nie na konkretnych wartościach
Klasyfikacja algorytmów ze względu na sposób tworzenia algorytmu:
• dziel i zwyciężaj (ang. divide and conquer) – problem dzielony jest rekurencyjnie na podproblemy tego samego typu, tak długo, aż staną się one łatwe do rozwiązania. Rozwiązania otrzymane z podproblemów łączy się, uzyskując rozwiązanie całego zadania.
• metody zachłanne – nie jest wykonywana analiza problemu, wybierane jest
rozwiązanie, które w danym momencie wydaje się najkorzystniejsze.
Algorytm
7
Klasyfikacja algorytmów ze względu na sposób tworzenia algorytmu:
• heurystyka – na podstawie niepełnych danych tworzony jest algorytm, który jest najbardziej prawdopodobny (jego wynik może nie być optymalny i poprawny). Metoda stosowana także wtedy, gdy korzystanie z pełnego algorytmu byłoby zbyt kosztowne. Często stosowana w celu znalezienia przybliżonego rozwiązania, które stanowi wejście dla algorytmu dokładnego (pełnego).
Klasyfikacja algorytmów ze względu na kolejność wykonywania działań:
• liniowe (sekwencyjne) – kroki wykonywane są w ściśle określonej kolejności, żadne krok nie może być pominięty ani powtórzony
• warunkowe – wykonanie pewnych kroków zależy od spełnienia warunku
• cykliczne – występuje w nich grupa poleceń wykonywanych wielokrotnie (w pętli). Liczba powtórzeń może być z góry ustalona, lub wynikać z oczekiwania na spełnienie warunku.
Algorytm liniowy
Dowodzenie poprawności algorytmu:
• sprawdzanie stanu punktów kontrolnych za pomocą debuggera – odczytujemy wartości pewnych ważnych zmiennych i sprawdzamy, czy zachowują się poprawnie dla pewnych reprezentacyjnych danych wejściowych (proces ten można zautomatyzować za pomocą zewnętrznych narzędzi, np. framework Google Test)
• formalne udowodnienie zachowania niezmienników dla dowolnych danych wejściowych.
Algorytm
8
Algorytm warunkowy Algorytm cykliczny
Algorytmy
9
Algorytmy iteracyjne:
• iteracja polega na n-krotnym wykonywaniu algorytmów w taki sposób, aby wyniki uzyskane podczas poprzednich iteracji (przebiegów) służyły jako dane wejściowe dla kolejnych. Sterowanie iteracjami zapewniają instrukcje pętli (for, while).
Iteracyjny algorytm wyznaczania silni
Iteracja (w każdym
obiegu pętli
zmniejszamy,
dekrementujemy,
wartość zmiennej a)
Wady podejścia rekurencyjnego:
• przepełnienie stosu – (ang. stack overflow) zmienne pomocnicze tworzone w każdym wywołaniu rekurencyjnym przechowywane są na stosie (ang. stack) do momentu zakończenia tego wywołania. Jeśli takich zmiennych jest dużo lub dużo jest wywołań rekurencyjnych mogą one zająć całą pamięć przeznaczoną na stos i w efekcie doprowadzić do zawieszenia aplikacji,
• łatwo zapomnieć o zdefiniowaniu warunku sprawdzającego przypadek bazowy – niebezpieczeństwo wywołań nieskończonych
Algorytmy
10
Algorytmy rekurencyjne:
• łac. recurrere – przybiec z powrotem
• rekurencja działa podobnie do iteracji, lecz zapętlanie obliczeń realizowane jest przez wywoływanie się tej samej procedury (funkcji) przez siebie samą, z innymi argumentami.
• kolejne wywołania rekurencyjne są mniejszym wystąpieniem tego samego problemu
• aby algorytm rekurencyjny zadziałał konieczne jest istnienie przypadku bazowego (elementarnego), dla którego rozwiązanie problemu jest znane,
• w każdym wywołaniu sprawdzane jest, czy zachodzi przypadek bazowy – jeśli
nie zachodzi, wówczas realizowane kolejne wywołanie rekurencyjne, jeśli
przypadek bazowy zachodzi wówczas zwraca jest wartość wynikająca z
przypadku bazowego.
Algorytmy
11
𝑛! = 𝑛 ∙ 𝑛 − 1 ! 0! = 1
𝑛 ∈ 𝑁, 𝑛 ≥ 1 Przykład algorytmu rekurencyjnego - silnia
• Tutaj przypadkiem bazowym jest 0! = 1, wywołanie funkcji silnia z argumentem 0 jest ostatnim wywołaniem rekurencyjnym, od niego zaczynają się powroty -
zwracanie wartości przez kolejne poziomy rekurencji, zaczynając od poziomu najgłębszego – tego związanego z przypadkiem bazowym
• po wyjściu z danego poziomu funkcja z nim związana kończy swoje działanie i
zmienne z nią związane są usuwane ze stosu.
Algorytmy
12
Analiza złożoność algorytmu:
• odpowiedź na pytanie: który z algorytmów wykonujący to samo zadanie (ale odmiennymi metodami) jest efektywniejszy?
Złożoność czasowa:
• miara użyta do oceny musi być niezależna od platformy sprzętowej
• definiuje się n jako najbardziej znaczący parametr algorytmu, wpływający na czas jego wykonywania
• definiuje się funkcję T(n) opisującą złożoność praktyczną algorytmu
W przypadku analizy algorytmu wyznaczania silni można przyjąć, że najbardziej czasochłonną operacją jest sprawdzenie, czy osiągnęliśmy warunek bazowy, czas ten nazwijmy t
c, zatem
𝑇 𝑛 = 𝑡
𝑐+ 𝑇 𝑛 − 1 𝑇 0 = 𝑡
𝑐𝑇 𝑛 = 𝑛 + 1 𝑡
𝑐Złożoność czasowa szuka odpowiedzi na pytanie: jaki typ funkcji matematycznej
występującej w funkcji T(n) wpływa najbardziej na czas wykonywania programu. Tę
funkcję nazywa się klasą algorytmu, oznacza jest jako O().
Złożoność czasowa algorytmów
13
Klasy algorytmów
T(n) O(n)
6 O(1)
3n + 1 O(n)
n
2– n + 1 O(n
2)
2
n+ n
2+ 4 O(2
n)
Klasa O(1) – oznacza, że liczba operacji wykonywanych przez algorytm jest niezależna od rozmiaru problemu, np. algorytm wyszukiwania oparty na metodzie transformacji kluczowej.
Klasa O(n) – oznacza, że algorytm wykonuje się w czasie proporcjonalnym ro rozmiaru problemu. Przykład: przetwarzanie sekwencyjne ciągu znaków. Każdej z n danych wejściowych algorytm musi poświęcić czas
Klasa O(log n) – jeśli rozmiar problemu rośnie geometrycznie (np. o rząd wielkości,
ze 100 na 1000), wzrost złożoności algorytmu będzie arytmetyczny (tutaj 2-krotny)
Klasa O(n
2) – spotykana w wyrażeniach, gdzie zachodzi relacja „każdy z każdym”,
np. dodawanie macierzy o rozmiarze nxn
Złożoność pamięciowa algorytmów
14
Złożoność pamięciowa algorytmów:
• określa wielkość pamięci operacyjnej, która jest potrzebna do przechowywania danych wejściowych, zmiennych pomocniczych i wyników obliczeń,
• ze względu na rozwój technologii produkcji pamięci kryterium to w wielu przypadkach straciło na znaczeniu
• odgrywa istotną rolę w przypadku systemów wbudowanych (ang. embedded
systems), gdzie występują znaczne ograniczenia dostępnej pamięci.
15