Algorytmy i struktury danych - Letnie Warsztaty Matematyczno

Transkrypt

Algorytmy i struktury danych - Letnie Warsztaty Matematyczno
Letnie Warsztaty Matematyczno-Informatyczne
Algorytmy i struktury danych
Mariusz Różycki
University of Cambridge
Zajęcia będą mieć formę wykładową.
Slajdy można znaleźć na stronie kursu:
http://lw.mi.edu.pl/informatyka/algorytmy.
Pomocne będzie pobieżne zapoznanie się z nimi przed zajęciami.
Wersja zaanotowana będzie umieszczana po każdych zajęciach.
Po piątkowych zajęciach opublikowany zostanie test, który
pozostanie otwarty do soboty do godziny 18.00.
Na osoby, które poradzą sobie z nim najlepiej, czekają drobne
nagrody.
Ciekawostka: w Anglii nadal można kupić w sklepach gwiazdki
Milky Way.
Tak tylko mówię.
Letnie Warsztaty Matematyczno-Informatyczne
Algorytmy i struktury danych
Wprowadzenie
Wprowadzenie
Definicja algorytmu
algorytm (ang. algorithm) — w matematyce skończony ciąg jasno
zdefiniowanych czynności, koniecznych do wykonania pewnego
rodzaju zadań.
— Wikipedia
Wprowadzenie
Algorytmy w codziennym życiu
Przykładem algorytmu jest przepis kucharski.
Przepis na naleśniki
1. Do miski wrzucić jajka, wsypać mąkę i wlać mleko.
2. Mieszać do uzyskania jednolitej masy.
3. Wlać porcję ciasta na rozgrzaną patelnię.
4. Gdy masa się zetnie, odwrócić naleśnik.
5. Zdjąć naleśnik z patelni.
6. Powtarzać do wyczerpania ciasta.
Wprowadzenie
Sposoby zapisywania algorytmów
– lista kroków
– schemat blokowy
– język programowania
– pseudokod
Wprowadzenie
Pseudokod
Metoda pośrednia między językiem programowania a listą kroków.
Posiada zalety obu rozwiązań:
– przejrzysty nawet dla skomplikowanych algorytmów
– wymaga dokładności przy zapisie
– nie wymaga znajomości detali implementacyjnych języków
programowania
– łatwy do odczytania nawet dla osób nie potrafiących
programować
Wprowadzenie
Pseudokod
Przypisanie i wartości:
n←5
T ← [1, 2, 3]
R ← 1..5
T [1] ← n
� Liczba
� Lista
� Zakres (czyli lista), [1, 2, 3, 4], bez 5
� Teraz T = [1, 5, 3]
Blok if:
if warunek then
zrób coś
else
zrób coś innego
end if
� Jeżeli warunek jest spełniony
� wykonaj ten fragment kodu.
� W przeciwnym razie
� wykonaj ten fragment kodu.
Wprowadzenie
Pseudokod
Pętla for all:
for all t ∈ T do
zrób coś
end for
Pętla while:
while warunek do
zrób coś
end while
� Dla każdego t należącego do T
� wykonaj ten fragment kodu.
� Dopóki warunek jest spełniony
� powtarzaj ten fragment kodu.
Wprowadzenie
Pseudokod
Procedura:
procedure Sum(T )
sum ← 0
for all t ∈ T do
sum ← sum + t
end for
return sum
end procedure
� Procedura Sum przyjmuje T .
� Przypisz 0 do sum.
� Dla każdego t
� zwiększ sum o t.
� Zwróć wartość sum.
Wprowadzenie
Znajdowanie minimum
Spróbujmy zapisać przy użyciu pseudokodu jakiś prosty algorytm,
jak znajdowanie minimum w ciągu liczb.
Proste, prawda? Wystarczy spojrzeć na wszystkie liczby i wybrać
najmniejszą.
Wprowadzenie
Znajdowanie minimum
Spróbujmy zapisać przy użyciu pseudokodu jakiś prosty algorytm,
jak znajdowanie minimum w ciągu liczb.
Proste, prawda? Wystarczy spojrzeć na wszystkie liczby i wybrać
najmniejszą.
Problem polega na tym, że komputer tak nie potrafi. Ty w sumie
też nie, jeżeli liczb jest kilka tysięcy (a nawet milionów).
Więc jak to zrobić?
Wprowadzenie
Znajdowanie minimum
Spróbujmy zapisać przy użyciu pseudokodu jakiś prosty algorytm,
jak znajdowanie minimum w ciągu liczb.
Proste, prawda? Wystarczy spojrzeć na wszystkie liczby i wybrać
najmniejszą.
Problem polega na tym, że komputer tak nie potrafi. Ty w sumie
też nie, jeżeli liczb jest kilka tysięcy (a nawet milionów).
Więc jak to zrobić?
Musimy spojrzeć na każdą wartość w ciągu po kolei, zapamiętując
wartość tej najmniejszej spośród dotychczas napotkanych.
Wprowadzenie
Znajdowanie minimum
procedure Minimum(T )
min ← ∞
� Każda liczba jest mniejsza od ∞.
for all t ∈ T do
if t < min then
� Jeżeli t jest mniejsze od min
min ← t
� zmień dotychczasowe minimum.
end if
end for
return min
� Zwróć najmniejszą napotkaną liczbę.
end procedure
Wprowadzenie
Algorytm Euklidesa
Przykładem nieco mniej oczywistego algorytmu jest algorytm
Euklidesa.
Przy jego użyciu możemy znaleźć najmniejszy wspólny dzielnik
dwóch liczb (ang. Greatest Common Divisor, w skrócie GCD).
Mając dane dwie liczby odejmujemy od większej mniejszą, aż jedna
z nich będzie równa 0. Wtedy druga z liczb będzie wynikiem
algorytmu.
Wprowadzenie
Algorytm Euklidesa
procedure GCD(a, b)
if a < b then
Swap(a, b)
end if
while b > 0 do
c ←a−b
a←b
b←c
end while
return a
end procedure
� Jeżeli a jest mniejsze od b
� zamień je miejscami.
� Teraz a będzie zawsze większe od b.
� Dopóki b nie jest zerem
� odejmuj b od a.
� Przy okazji zamień je miejscami,
� żeby a było nadal większe od b.
� b jest zerem, więc a jest wynikiem.
procedure GCD(a,b):
while b > 0:
if a < b then
Swap(a,b)
end if
a <- a-b
end while
return a
end procedure
Wprowadzenie
Algorytm Euklidesa
Algorytm Euklidesa można przyspieszyć, używając dzielenia
modulo:
procedure Fast-GCD(a, b)
while b > 0 do
c ← amodb
a←b
b←c
end while
return a
end procedure
Wprowadzenie
Zadania
1. Napisz, używając pseudokodu, procedurę Length(T ), która
zwróci długość (liczbę elementów) listy przekazanej jako
argument.
2. Napisz procedurę Average(T ), która dla danej listy T
obliczy i zwróci sumę arytmetyczną jej elementów.
Podpowiedź: w jednej pętli możesz policzyć zarówno sumę
elementów jak i liczbę elementów listy. Wynikiem procedury
będzie wynik ich dzielenia.
Wprowadzenie
Zadania
3. Jaki wynik zwróci procedura GCD, jeżeli oba jej argumenty
będą równe 0? Czy jest to poprawny wynik?
4. Procedura Fast-GCD nie sprawdza, czy a jest większe od b.
Dlaczego nie jest to konieczne?
Podpowiedź: jeżeli początkowo a < b, jakie będą wartości a i
b po jednym przebiegu pętli while?
Letnie Warsztaty Matematyczno-Informatyczne
Algorytmy i struktury danych
Złożoność obliczeniowa
Złożoność obliczeniowa
Czas wykonania algorytmu
GCD(100, 1):
1. a = 100, b = 1
2. a = 99, b = 1
...
99. a = 1, b = 1
100. a = 1, b = 0
Wynik: 1
Złożoność obliczeniowa
Czas wykonania algorytmu
GCD(100, 1):
1. a = 100, b = 1
Fast-GCD(100, 1):
2. a = 99, b = 1
...
1. a = 100, b = 1
99. a = 1, b = 1
100. a = 1, b = 0
Wynik: 1
2. a = 1, b = 1
3. a = 1, b = 0
Wynik: 1
Złożoność obliczeniowa
Czas wykonania algorytmu
Pomysł: uznać, że każda operacja zajmuje 1 jednostkę czasu.
Policzyć ile jednostek otrzymamy.
Złożoność obliczeniowa
Czas wykonania algorytmu
Pomysł: uznać, że każda operacja zajmuje 1 jednostkę czasu.
Policzyć ile jednostek otrzymamy.
Problem: w praktyce różne operacje wymagają różnej ilości czasu.
Złożoność obliczeniowa
Czas wykonania algorytmu
Pomysł: uznać, że każda operacja zajmuje 1 jednostkę czasu.
Policzyć ile jednostek otrzymamy.
Problem: w praktyce różne operacje wymagają różnej ilości czasu.
Pomysł: przypisać każdej operacji jakąś stałą liczbę jednostek
czasu.
Złożoność obliczeniowa
Czas wykonania algorytmu
Pomysł: uznać, że każda operacja zajmuje 1 jednostkę czasu.
Policzyć ile jednostek otrzymamy.
Problem: w praktyce różne operacje wymagają różnej ilości czasu.
Pomysł: przypisać każdej operacji jakąś stałą liczbę jednostek
czasu.
Problem: czas wykonania pojedynczej operacji zależy od wielu
czynników i nie jest stały.
Złożoność obliczeniowa
Czas wykonania algorytmu
Patrzeć będziemy na to, jak czas wykonania zmienia się w
zależności od wielkości danych wejściowych.
Złożoność obliczeniowa
Czas wykonania algorytmu
Patrzeć będziemy na to, jak czas wykonania zmienia się w
zależności od wielkości danych wejściowych.
Na przykład czas wykonania procedury Minimum zmienia się
liniowo względem liczby elementów T . To jest: jeżeli zwiększymy
liczbę elementów T dwukrotnie, czas wykonania zwiększy się
dwukrotnie.
Złożoność obliczeniowa
Czas wykonania algorytmu
Patrzeć będziemy na to, jak czas wykonania zmienia się w
zależności od wielkości danych wejściowych.
Na przykład czas wykonania procedury Minimum zmienia się
liniowo względem liczby elementów T . To jest: jeżeli zwiększymy
liczbę elementów T dwukrotnie, czas wykonania zwiększy się
dwukrotnie.
Czas wykonania procedury GCD zmienia się liniowo względem
ilorazowi większej i mniejszej z liczb a i b.
Złożoność obliczeniowa
Czas wykonania algorytmu
Patrzeć będziemy na to, jak czas wykonania zmienia się w
zależności od wielkości danych wejściowych.
Na przykład czas wykonania procedury Minimum zmienia się
liniowo względem liczby elementów T . To jest: jeżeli zwiększymy
liczbę elementów T dwukrotnie, czas wykonania zwiększy się
dwukrotnie.
Czas wykonania procedury GCD zmienia się liniowo względem
ilorazowi większej i mniejszej z liczb a i b.
Czas wykonania procedury Fast-GCD zmienia się proporcjonalnie
do logarytmu większej z liczb a i b.
Złożoność obliczeniowa
Kres górny i dolny
Ponieważ z reguły ciężko jest okreslić dokładną zależność między
wielkością danych a czasem wykonania algorytmu, będziemy
posługiwać się kresem górnym i dolnym (najczęściej tylko górnym).
Brace yourselves, winter maths is coming.
Złożoność obliczeniowa
Kres górny i dolny
Funkcja g jest kresem dolnym funkcji f , jeżeli dla każdego x ∈ Df
zachodzi g (x) � f (x).
Podobnie funkcja h jest kresem górnym funkcji f , jeżeli dla
każdego x ∈ Df zachodzi h(x) � f (x).
Oczywiście zarówno g jak i h muszą mieć tę samą dziedzinę
i przeciwdziedzinę co f .
Złożoność obliczeniowa
Notacja wielkiego „O” (i podobne)
Kresy górny i dolny nie stanowią same w sobie rozwiązania
problemu.
Jeżeli czas wykonania procedury rośnie liniowo, to jaki jest kres
górny? g (x) = 2x? g (x) = 1000x? g (x) = G64 x?
Złożoność obliczeniowa
Notacja wielkiego „O” (i podobne)
Kresy górny i dolny nie stanowią same w sobie rozwiązania
problemu.
Jeżeli czas wykonania procedury rośnie liniowo, to jaki jest kres
górny? g (x) = 2x? g (x) = 1000x? g (x) = G64 x?
Z pomocą przychodzi notacja wielkiego „O” (i podobne).
Złożoność obliczeniowa
Notacja wielkiego „O” (i podobne)
Kresy górny i dolny nie stanowią same w sobie rozwiązania
problemu.
Jeżeli czas wykonania procedury rośnie liniowo, to jaki jest kres
górny? g (x) = 2x? g (x) = 1000x? g (x) = G64 x?
Z pomocą przychodzi notacja wielkiego „O” (i podobne).
Więcej matematyki!
Złożoność obliczeniowa
Notacja wielkiego „O” (i podobne)
Funkcja f należy do O (g (n)), jeżeli istnieje takie K i x0 , że dla
każdego x � x0 zachodzi f (x) � K · g (x).
Funkcja f należy do Ω (g (n)), jeżeli istnieje takie K i x0 , że dla
każdego x � x0 zachodzi f (x) � K · g (x).
W praktyce głównie używa się tego pierwszego zapisu.
Złożoność obliczeniowa
Notacja wielkiego „O” (i podobne)
Funkcja f należy do O (g (n)), jeżeli istnieje takie K i x0 , że dla
każdego x � x0 zachodzi f (x) � K · g (x).
Co daje nam notacja wielkiego „O”?
1. Nie musimy martwić się stałymi. Jeżeli funkcja należy do
O(2x), to należy też to O(100x) i O(x). Stałe będziemy więc
pomijać.
2. Nie musimy przejmować się nieistotnymi składnikami. Jeżeli
funkcja należy do O(2x + x 3 ), to należy również do O(2x ).
Wolniej rosnące składniki sumy będziemy pomijać.
Złożoność obliczeniowa
Notacja wielkiego „O” (i podobne)
Jeżeli więc czas wykonania algorytmu rośnie liniowo względem
jakiegoś n, to mówimy, że ma złożoność obliczeniową O(n).
Inne możliwości to między innymi O(2n ), O(n2 ), O(n log n), O(n),
O(log n), O(1). . .
Złożoność obliczeniowa
Notacja wielkiego „O” (i podobne)
Jeżeli więc czas wykonania algorytmu rośnie liniowo względem
jakiegoś n, to mówimy, że ma złożoność obliczeniową O(n).
Inne możliwości to między innymi O(2n ), O(n2 ), O(n log n), O(n),
O(log n), O(1). . .
Zwróć uwagę, że O opisuje kres górny. Zatem jeżeli algorytm ma
złożoność O(n), to możemy powiedzieć również, że ma złożoność
O(n2 ). W praktyce będziemy starali się znajdować jak
najciaśniejszy kres, ponieważ niesie on ze sobą najwięcej informacji.

Podobne dokumenty