Analiza Algorytmów Moduł 1 Podstawowe pojecia ˛ analizy algorytmów Aleksandra Orpel Spis treści podstawowe 1 Pojecia ˛ 1.1 Co to jest algorytm? . . . . . . . . . . . . . 1.2 Co to jest analiza algorytmów . . . . . . . . 1.3 Ocena przydatności algorytmu . . . . . . . . 1.3.1 Czas działania - złożoność czasowa . 1.3.2 Analiza przypadku pesymistycznego . 1.3.3 Analiza przypadku średniego . . . . . . . . . . . matematyczne 2 Podstawowe narzedzia ˛ 2.1 Funkcje podłogi i sufitu . . . . . . . . . . . . . 2.2 Szacowanie sum za pomoca˛ całek . . . . . . . 2.3 Notacja asymptotyczna . . . . . . . . . . . . . 2.3.1 Notacja ”o-małe” . . . . . . . . . . . . 2.3.2 Notacja ”O-duże” . . . . . . . . . . . . 2.3.3 Inne notacje asymptotyczne . . . . . . 3 Bibliografia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 4 5 6 9 10 . . . . . . 11 11 12 12 13 14 17 18 1 Pojecia podstawowe ˛ 1 1.1 Co to jest algorytm? Algorytm jest ściśle określona˛ procedura˛ (na przykład obliczeniowa), ˛ która dla właściwych danych wejściowych generuje żadane dane wyjściowe. Algorytm ˛ możemy również traktować jako sposób rozwiazywania konkretnego problemu. ˛ Postawienie problemu polega na sprecyzowaniu wymagań dotyczacych relacji ˛ miedzy danymi wejściowymi i wyjściowymi, a algorytm opisuje właściwa˛ procedure, ˛ ˛ która zapewnia, że ta relacja zostanie osiagni Dokładnie: algorytmem ˛ eta. ˛ nazywamy metode˛ (schemat) rozwiazywania danego problemu, która spełnia ˛ nastepuj ˛ ace ˛ warunki: 1. - istnieje określony stan poczatkowy, tj. operacja od której rozpoczyna sie˛ ˛ jego działanie; 2. - ilość operacji potrzebnych do rozwiazania problemu jest skończona - warunek ˛ dyskretności; 3. - musi istnieć możliwość stosowania metody do rozwiazania całej klasy ˛ zagadnień, a nie tylko pojedynczego egzemplarza - warunek uniwersalności; 4. - interpretacja poszczególnych kroków metody musi być jednoznaczna warunek jednoznaczości; 5. - cel musi być osiagany w pewnym skończonym i możliwym do przyjecia ˛ ˛ przez użytkownika czasie - warunek efektywności; 6. - istnieje wyróżniony koniec. Niektóre problemy nie posiadaja˛ algorytmicznych rozwiaza ˛ ń. Klasycznym przykładem jest problem zatrzymania (halting problem), w którym algorytm miałby orzekać, czy dany program komputerowy kiedykolwiek wykona wszystkie procedury i zatrzyma sie. ˛ Danymi wejściowymi takiego algorytmu byłyby: program komputerowy oraz dane wejściowe dla tego programu, zaś danymi wyjściowymi prosta odpowiedź "tak" (program zatrzyma sie˛ po wykonaniu wszystkich zawartych w nim poleceń) lub "nie" (program nie zatrzyma sie). ˛ Można pokazać, że nie istnieje algorytm, który w skończonym czasie orzeknie, czy dany program dla określonych danych wejściowych zapetli ˛ sie˛ czy nie. Musimy przyja˛ć pewne ustalenia dotyczace ˛ zapisu badanych algorytmów. Nie chcemy wykorzystywać jakiegoś wybranego jezyka programowania, gdyż ˛ mogłoby to budzić watpliwo ści, czy przedstawiamy analize˛ samego algorytmu, ˛ czy też jego konkretnej implementacji. Wobec tego posługiwać sie˛ bedziemy ˛ 2 pewnym pseudojezykiem (zbliżonym do Pascala), składajacym sie˛ zarówno z ˛ ˛ wyrażeń potocznych, jak i instrukcji, które powszechnie sa˛ używane w literaturze poświeconej algorytmice. Oto one: ˛ 1. instrukcja podstawienia, np. nazwa := a (zmienna nazwa przyjmuje wartość zmiennej a); 2. instrukcja warunkowa if warunek then begin instrukcje realizowane, gdy warunek jest spełniony end else begin instrukcje realizowane, gdy warunek nie jest spełniony end 3. instrukcja petli ˛ do-while oraz while-do (a) do begin ciag ˛ instrukcji end while warunek oraz (b) while warunek do begin ciag ˛ instrukcji end 4. instrukcja petli ˛ for for i := 1 to 50 do begin ciag ˛ instrukcji (powtarzanych 50 razy) end Gdy do wykonania jest tylko jedna instrukcja czesto ˛ pomijamy begin i end. 3 1.2 Co to jest analiza algorytmów Analiza algorytmów jest działem informatyki teoretycznej zajmujacym sie˛ ˛ poszukiwaniem najlepszych i najbardziej efektywnych algorytmów dla zadań komputerowych. W powyższej definicji nie zostało jednak sprecyzowane, jaki algorytm nazwiemy najefektywniejszym. Czy zawsze oznacza to da˛żenie do znalezienia algorymu najszybszego, czy też wymagajacego najmniejszych zasobów ˛ pamieci? ˛ Czy ważna jest również prostota algorytmu? Kryteria, jakimi kierujemy sie˛ wybierajac innymi ˛ najlepszy algorytm, zależa˛ od wielu czynników, miedzy ˛ od przeznaczenia i oczekiwań użytkowników. Analiza algorytmu polega na określeniu zasobów, jakie sa˛ potrzebne do jego wykonania. Zasobem zasadniczym jest dla nas czas działania, jednakże innymi zasobami moga˛ być: pamie˛ć czy szerokość kanału komunikacyjnego. Za pomoca˛ teoretycznych rozważań chcemy zbadać zachowanie algorytmu bez konieczności implementacji na komputerze. Oczywiście z reguły nie jesteśmy w stanie precyzyjnie przewidzieć ilości zasobów koniecznych do realizacji algorytmu, gdyż jest zbyt dużo zmiennych czynników. Zamiast tego, staramy sie˛ wyodrebni ˛ ć główne parametry charakteryzujace ˛ algorytm i poddać je analizie. Przy takim podejściu pomijamy wiele szczegółów dotyczacych dokładnej implementacji, a nasza analiza jest ˛ jedynie przybliżona. Należy jednak pamieta ˛ ć, że naszym celem nie jest dokładne określenie potrzebnych zasobów, a jedynie umożliwienie obiektywnego porównania różnych algorytmów rozwiazania tego samego problemu i wybranie spośród nich ˛ najlepszego dla naszych celów. Badajac ˛ zadany problem zastanawiamy sie˛ najpierw, czy w ogóle jest możliwe rowiazanie go na komputerze w skończonym i możliwym do zaakceptowania ˛ przez programiste˛ (lub potencjalnych użytkowników) czasie oraz czy już istnieje algorytm, który może zostać w danych okolicznościach zastosowany do rozważanego zadania. Jeśli odpowiedzi na powyższe pytania sa˛ pozytywne, to należy rozstrzygna˛ć, algorytmów jest czy mamy jeszcze czego szukać, tj. czy któryś z istniejacych ˛ optymalny w naszym przypadku. Jeśli nie, to pracujac ˛ nad lepszym rozwiazaniem, ˛ należy zwrócić uwage˛ na to, w jaki sposób uzasadnić, że proponowany przez nas nowy algorytm rozwiazuje problem. Najważniejszym bowiem pytaniem, które ˛ pojawia sie, ˛ gdy zaczynamy prace˛ z nowym algorytmem jest pytanie o jego poprawność. Intuicyjnie rozumiemy, że algorytm jest poprawny, gdy dla każdego zestawu danych wejściowych algorytm zatrzymuje sie˛ i daje dobry wynik. Algorytm niepoprawny może sie˛ nigdy nie zatrzymać lub dać zły wynik (np. niepoprawny algorytm sortowania może zwrócić jako wynik swego działania ciag, ˛ który nie jest posortowany). Okazuje sie, ˛ że precyzyjne dowody poprawności bardziej 4 skomplikowanych algorytmów sa˛ niezwykle złożone i wykraczaja˛ poza ramy tego skryptu. Choć oczywiście omówimy wybrane metody badania poprawności prostszych algorytmów. Głównym narzedziem wykorzystywanym do tego celu ˛ bedzie twierdzenie o niezmiennikach petli. (Wiecej ˛ ˛ ˛ o poprawności algorytmów powiemy w Module 2.) 1.3 Ocena przydatności algorytmu Ocena przydatności algorytmu zależy w dużej mierze od kontekstu, w jakim bedzie on użyty. Okazuje sie, ˛ ˛ że czasem warto wykorzystać proste podejście i nie konstruować bardziej skomplikowanych, choć zużywajacych mniej zasobów ˛ schematów rozwiaza ˛ ń. Dzieje sie˛ tak na przykład wtedy, gdy algorytm ma być użyty tylko kilka razy lub też, gdy jest on przeznaczony do pracy z danymi wejściowymi o małym rozmiarze. W przypadku, gdy dana metoda ma być stosowana wielokrotnie dla danych o dużych rozmiarach, warto szukać efektywniejszy algorytmów. Podstawowa˛ wielkościa,˛ która bedzie stanowiła miare˛ przydatności algorytmu ˛ jest tzw. złożoność obliczeniowa, obejmujaca z ˛ zarówno problemy zwiazane ˛ jego implementacja,˛ jak i sama˛ efektywność algorytmu. Pojecie ˛ to definiujemy nastepuj ˛ aco: ˛ Definicja 1. Złożonościa˛ obliczeniowa˛ algorytmu nazywamy ilość zasobów komputerowych, potrzebnych do jego realizacji, przy czym podstawowymi zasobami rozważanymi w analizie algorytmów jest czas działania oraz ilość potrzebnej pamieci. ˛ Da˛żymy do tego, aby złożoność obliczeniowa algorytmu była wyrażona jako funkcja zależna od danych wejściowych, a dokładnie od rozmiaru danych wejściowych - rozumianego jako liczba pojedynczych danych wchodzacych ˛ w skład całego zestawu danych wejściowych. Na przykład rozważajac ˛ problem wyznaczania wartości wielomianu w punkcie, jako rozmiar danych wejściowych przyjmujemy stopień tego wielomianu, w problemie wyznaczania n. potegi ˛ (n 2 to wykładnik tej potegi, a gdy mówimy N) liczby rzeczywistej a - bedzie ˛ ˛ o mnożeniu dwóch liczb całkowitych, rozmiarem jest całkowita liczba bitów potrzebnych do reprezentacji tych danych w postaci binarnej. Kiedy natomiast zajmujemy sie˛ sortowaniem ciagu ˛ elementów, rozmiarem danych wejściowych bedzie liczba wyrazów ciagu, a w przypadku zagadnienia przechodzenia drzewa ˛ ˛ - liczba jego wezłów. ˛ W analizie algorytmów czesto terminami: złożoność ˛ posługiwać sie˛ bedziemy ˛ 5 pamieciowa P(n) (lub pamieć, ˛ ˛ przestrzeń) w odniesieniu do ilości zasobów pamieci ˛ potrzebnych do realizacji danego algorytmu oraz złożoność czasowa T(n) (lub czas). Obie te wielkości składaja˛ sie˛ na złożoność obliczeniowa˛ algorytmu. Ilość przestrzeni zajmowanej przez algorytm jest zdeterminowana przez liczbe˛ i rozmiar zmiennych oraz rodzaj danych. Natomiast czas zależy od liczby operacji, które musza˛ być wykonane podczas działania algorytmu. Czesto zdarza sie, ˛ ˛ że miedzy czasem i przestrzenia˛ istnieje zależność odwrotnie proporcjonalna, tzn. ˛ jesteśmy w stanie zredukować rozmiar wymaganej przestrzeni kosztem wzrostu ilości potrzebnego czasu i odwrotnie: redukcja czasu wymaga zwiekszenia ilości ˛ pamieci. ˛ 1.3.1 Czas działania - złożoność czasowa Jednym ze sposobów mierzenia czasu działania algorytmu mógłby być pomiar za pomoca˛ zegarka czasu realizacji zaimplementowanego algorytmu. Takie podejście daje nam pewne wyobrażenie o tym, jak zmienia sie˛ czas działania algorytmu w zależności od rozmiaru danych wejściowych, ale z wielu powodów nie może to być wielkość decydujaca ˛ o ocenie przydatności danego algorytmu. Przede wszystkim dlatego, że uzyskany wynik zależy od szybkości maszyny, na której wykonywany jest program, jak również od zdolności jego autora. Nasza miara szybkości algorytmu powinna być niezależna od oprogramowania i sprzetu, ˛ na którym implementujemy algorytm oraz umiejetno ˛ ści programisty. Wobec tego powinna być ona cecha˛ samego algorytmu, jako metody rozwiazania problemu. ˛ Wprowadzamy zatem pojecie ˛ operacji dominujacych: ˛ nazywamy operacje charakterystyczne Definicja 2. Operacjami dominujacymi ˛ dla danego algorytmu, których łaczna liczba jest proporcjonalna do liczby ˛ wykonań wszystkich operacji jednostkowych w dowolnej komputerowej realizacji algorytmu. Dla przykładu w algorytmach wyznaczajacych wartość wielomianu w zadanym ˛ punkcie operacjami dominujacymi bed ˛ ˛ a˛ operacje arytmetyczne, w algorytmach sortowania - porównania elementów ciagu wejściowego, a czasem także przestawienia elementów tego ciagu. Jeśli natomiast badamy algorytm przegladania ˛ ˛ grafu, to jako operacje˛ dominujac miedzy ˛ a˛ przyjmiemy przejście dowiazania ˛ ˛ wezłami w drzewie. ˛ Definicja 3. Za jednostke˛ złożoności czasowej przyjmujemy czas potrzebny do wykonania jednej operacji dominujacej. ˛ Oczywiśnie nie zawsze jesteśmy w stanie określić dokładna˛ liczbe˛ operacji dominujacych. Czasem możemy jedynie ja˛ oszacować z dokładnościa˛ do stałego ˛ 6 czynnika. Ponadto nie bedziemy zajmować sie˛ zachowaniem algorytmów dla ˛ danych wejściowych o małych rozmiarach, gdyż w takich przypadkach z reguły najlepsze sa˛ najprostsze rozwiazania. Najważniejsza˛ cecha˛ prezentowanego przez ˛ nas podejścia jest ignorowanie stałych czynników i skupienie sie˛ na badaniu zachowania algorytmu w przypadku, gdy rozmiar danych wejściowych da˛ży do nieskończoności. Jeżeli na przykład dany algorytm wykonuje 100n operacji dominujacych, to ˛ pominiemy liczbe˛ 100 i powiemy, że jego złożoność czasowa jest rzedu ˛ n. Jeśli natomiast w innym algorytmie liczba operacji dominujacych jest równa 2n2 +50, ˛ to pominiemy 2 i 50 określajac ˛ rzad ˛ czasu działania jako n2 . Zatem podajemy najprostsza˛ funkcje˛ charakteryzujac ˛ a˛ rzad ˛ wielkości T(n). Porównujac ˛ teraz złożoność (czasowa) ˛ obu algorytmów rozważymy przypadek "dużego" n, a ponieważ 2 od n stwierdzimy, że drugi algorytm jest wolniejszy, chociaż na n jest wieksze ˛ przykład dla n = 3 mamy 2 ¢ 32 + 50 < 100 ¢ 3. (Oczywiście n oznacza liczbe˛ naturalna.) ˛ Stad ˛ powyższy wniosek jest słuszny dla "dostatecznie dużych" n, w naszym przypadku wystarczy przyja˛ć n ¸ 50. Wobec tego posługiwać sie˛ bedziemy pojeciem asymptotycznego czasu działania, dla podkreślenia, iż ˛ ˛ chodzi nam o czas działania algorytmu dla danych wejściowych, których rozmiar da˛ży do nieskończoności. Wiekszo ść badanych przez nas algorytmów ma złożoność czasowa˛ (tj. asymptotyczn ˛ czas działania) proporcjonalna˛ do jednej z podanych poniżej funkcji: 1. złożoność logarytmiczna: lg n, 2. złożoność liniowa: n, 3. złożoność liniowo-logarytmiczna: n lg n, 4. złożoność kwadratowa: n2 , 5. dalsze złożoności wielomianowe: n3 , n4 , ..., 6. złożoność podwykładnicza: 2 nlg n = 2lg n , 7 7. złożoność wykładnicza typu 2n : 2n , 8. złożoność wykładnicza typu n! : n! . Warto zauważyć, iż algorytm o złożoności wykładniczej może być stosowany jedynie dla danych wejściowych o małych rozmiarach, co wynika z własności funkcji wykładniczej. Okazuje sie˛ bowiem, że istnieje pewna progowa wartość argumentu, od której funkcja wykładnicza zaczyna rosna˛ć na tyle szybko, że implementacja algorytmu na komputerze staje sie˛ niemożliwa. Dla przykładu rozważmy algorytm, który dla danych wejściowych o rozmiarze n wykonuje 2n operaci dominujacych. Poniżej podajemy tabelke˛ przedstawiajac ˛ ˛ a˛ czas potrzebny do realizacji tego algorytmu przez dwa komputery przy założeniu, że jedna operacja na każdym z nich zajmuje odpowiednio 10¡6 i 10¡9 sekund. Rozmiar danych wejściowych (n) 20 50 Czas działania na pierwszym 1.04 s 35, 7 lat komputerze (2n ¢ 10¡6 ) Czas działania na drugim 10¡3 s 13 dni komputerze (2n ¢ 10¡9 ) 100 4 ¢ 1014 wieków 4 ¢ 1011 wieków 200 5 ¢ 1044 wieków 5 ¢ 1041 wieków Oczywiście zawsze chcemy znaleźć najszybsze rozwiazanie zadanego problemu. ˛ Wobec tego majac ˛ pewien algorytm pytamy, czy można jeszcze szybciej osiagn ˛ a˛ć cel, czy też złożoność czasowa naszego algorytmu już osiagn ˛ eła ˛ wartość najmniejsza˛ z możliwych. W wielu przypadkach potrafimy znaleźć teoretyczne dolne oszacowa liczby operacji dominujacych potrzebnych do rozwiazania problemu. Na przykład ˛ ˛ pokażemy, że dla algorytmów sortowania opartych na porównywaniu elementów takie oszacowanie jest rzedu ˛ n lg n, gdzie n jest liczba˛ sortowanych elementów. Zwykle wynika ono z analizy teoretycznych ograniczeń zwiazanych z danym ˛ problemem. Można także rozważać tzw. trywialne dolne oszacowanie. Dla powyższego probelmu sortowania opartego na porównaniach rzad ˛ trywialnego dolnego oszacowania jest równy n. Natomiast problem mnożenia dwóch macierzy wymiaru n £ n ma trywialne dolne oszacowanie rzedu ˛ kwadratowego. Mówimy również o tzw. zakresie (przedziale) czasów działania algorytmów. W ten sposób określamy przedział miedzy teoretycznym dolnym oszacowaniem, ˛ a najlepszym znanym algorytmem: 8 " j czas j j j czas działania najlepszego znanego algorytmu l teoretyczne dolne oszacowanie złożoności czasowej trywialne dolne oszacowanie złożoności czasowej Jeżeli wiemy, że dany algorytm osiaga ˛ w asymptotycznym sensie dolne ograniczenie to przynajmniej dla "dużych" danych wejściowych żaden algorytm nie może być szybszy. Nie oznacza to jeszcze, że nie można poprawić takiego rozwiazania. ˛ Może sie˛ bowiem zdarzyć, że w jego asymptotycznym czasie kryje sie˛ bardzo duża stała lub też, że asymptotyczne oszacowanie można zastosować tylko wtedy, gdy dane wejściowe sa˛ bardzo duże. Dla niektórych ważnych problemów teoretyczne dolne oszacowanie nie zostało jeszcze wyznaczone. W takim przypadku punktem odniesienia staje sie˛ najszybszy istniejacy ˛ algorytm rozwiazuj ˛ acy ˛ dany problem. Wprowadzimy teraz pewne oznaczenia, które pozwola˛ nam precyzyjnie zdefiniować pesymistyczna˛ i średnia˛ (oczekiwana) ˛ złożoność czasowa:˛ 1. ZDWn - zbiór zestawów danych wejściowych rozmiaru n; 2. l(zdw) - liczba operacji dominujacych dla zestawu danych wejściowych ˛ zdw 2 ZDWn ; 3. Xn - zmienna losowa, której wartościa˛ jest l(zdw) dla zdw 2 ZDWn; 4. pnk - rozkład prawdopodobieństwa zmiennej losowej Xn , tj. prawdo- podobieństwo że dla danych wejściowych rozmiaru n algorytm wykona k operacji dominujacych ˛ 1.3.2 Analiza przypadku pesymistycznego W wielu algorytmach czas działania zależy nie tylko od rozmiaru danych wejściowych i może on przyjmować różne wartości dla różnych danych wejściowych o tym samym rozmiarze. Wówczas interesuje nas złożoność obliczeniowa w najgorszym przypadku określona nastepuj ˛ aco: ˛ Definicja 4. Pesymistyczna˛ złożoność czasowa˛ Tmax (n) algorytmu definiujemy jako maksymalny czas działania algorytmu po wszystkich możliwych danych wejściowych o rozmiarze n Tmax (n):= sup fl(zdw),zdw 2 ZDWn g. Czesto ˛ analizujac ˛ zachowanie algorytmu posługujemy sie˛ właśnie przypadkiem pesymistycznym określajac ˛ czas działania algorytmu, co gwarantuje, że algorytm 9 nigdy nie bedzie działał dłużej. W niektórych problemach przypadek pesymistyczny ˛ pojawia sie˛ dosyć czesto. Warto jednak pamieta ˛ ˛ ć, że może sie˛ zdarzyć, iż algorytm o pesymistycznej złożoności czasowej rzedu dla wiekszo ści danych ˛ n lg n bedzie ˛ ˛ wejściowych działał wolniej niż algorytm o pesymistycznym czasie działania rzedu ˛ n2 . Istnieja˛ algorytmy o pesymistycznej złożoności czasowej rzedu ˛ wykładniczego, które dla przeważajacej cze˛ści danych wejściowych bed ˛ ˛ a˛ działać szybciej niż algorytm o złożoności wielomianowej. Na przykład metoda simplex programowania liniowego, której pesymistyczna złożoność czasowa jest wykładnicza, dla wiekszo ści ˛ pojawiajacych sie˛ w praktyce danych wejściowy działa w czasie wielomianowym, ˛ a nawet liniowym. Zatem analiza przypadku pesymistycznego nie daje nam pełnego obrazu zachowania sie˛ algorytmu. Aby dokładniej zbadać na ile wielkość Tmax (n) odzwierciedla rzeczywisty czas działania algorytmu dla wszystkich danych wejściowych rozważa sie˛ miary wrażliwości algorytmu: Definicja 5. Miara˛ wrażliwości pesymistycznej algorytmu nazywamy wielkość ∆(n) = supfl(zdw1 ) ¡ l(zdw2 ), zdw1 , zdw2 2 ZDWn g. 1.3.3 Analiza przypadku średniego Niezwykle ważnym jest pytanie o zachowanie algorytmu dla losowych danych wejściowych. Pojawia sie˛ wówczas pojecie ˛ średniej (lub oczekiwanej) złożoności. Definicja 6. Oczekiwana˛ złożonościa˛ czasowa˛ algorytmu nazywamy wartość oczekiwana˛ zmiennej losowej Xn X Ts¶r (n) = E [Xn ] = kpnk . k¸0 Definicja 7. Miara˛ wrażliwości oczekiwanej algorytmu nazywamy wielkość δ(n) bed ˛ ac ˛ a˛ odchyleniem standardowym zmiennej losowej Xn , tj. δ(n) = dev [Xn ] . p Przypomnijmy, że dev [Xn ] = var [Xn ], gdzie wariancja zmiennej losowej Xn dana jest nastepujaco ˛ var [Xn ] = E[(Xn ¡ E [Xn ])2 ]. Uwaga: Wielkości ∆(n) i δ(n) informuja˛ nas o tym, jak bardzo zachowanie algorytmu dla rzeczywistych danych wejściowych może odbiegać od zachowania opisanego za pomoca, ˛ odpowiednio pesymistycznej i oczekiwanej złożoności czasowej. Im wieksze sa˛ wartości ∆(n) i δ(n) tym algorytm jest bardzej wrażliwy ˛ na dane wejściowe. 10 2 2.1 Podstawowe narzedzia matematyczne ˛ Funkcje podłogi i sufitu W wielu przypadkach wyznaczenie liczby operacji dominujacych zwiazane jest z ˛ ˛ wykorzystaniem własności cze˛ści całkowitych liczb rzeczywistych. Przypomnijmy zatem definicje funkcji podłogi i sufitu oraz podstawowe ich własności. Definicja 8. Funkcja˛ podłogi nazywamy funkcje˛ x 2 R ! bxc 2 Z przyporzadkow ˛ liczbie rzeczywistej x najwieksz ˛ a˛ liczbe˛ całkowita˛ nie wieksz ˛ a˛ niż x. y 4 2 0 -5 -2.5 0 2.5 5 x -2 -4 Rys. 1. Wykres funkcji f (x) = bxc Definicja 9. Funkcja˛ sufitu nazywamy funkcje˛ x 2 R ! dxe 2 Z przyporzadkowuja ˛ liczbie rzeczywistej x najmniejsza˛ liczbe˛ całkowita˛ nie mniejsza˛ niż x. y 4 2 0 -5 -2.5 0 2.5 5 x -2 -4 Rys. 2. Wykres funkcji f (x) = dxe 11 Stwierdzenie 10. Podstawowe własności funkcji sufitu i podłogi. Dla dowolnych liczb x 2 R oraz n, a, b 2 Z takich, że a 6= 0 i b 6= 0 zachodza˛ nastepuj ˛ ace ˛ zależności: 1. x ¡ 1 < bxc · x · dxe < x + 1. 2. bxc = n wtedy i tylko wtedy, gdy n · x < n + 1. 3. dxe = n wtedy i tylko wtedy, gdy n ¡ 1 < x · n. ¥ ¦ § ¨ 4. n2 + n2 = n. 5. ddn/ae /be = dn/(ab)e . 6. bbn/ac /bc = bn/(ab)c . 2.2 Szacowanie sum za pomoca˛ całek Podamy teraz proste i bardzo szykie kryterium szacowania sum za pomoca˛ całek. Należy tu zwrócić uwage˛ na fakt, iż w czasie wyznaczania dokladnych oszacowań niektórych wielkości (np. n. liczby harmonicznej) możemy w poniższych wzorach otrzymać całki niewłaściwe rozbiezne, co strywializuje otrzymane oszacoawan dajac ˛ np. ograniczenie górne sumy poprzez +1. W cze˛ści drugiej modułu pokażemy jak należy postepowa ć w takich sytuacjach. ˛ Twierdzenie 11. Niech dany bedzie przedział domkniety ˛ ˛ [a, b], 0 < a < b, m < n, takie, że m ¡ 1, n + 1 2 [a, b], oraz funkcja f : [a, b] ! [0, +1) ciagła. ˛ Wówczas (i) jeżeli f jest rosnaca ˛ w (a, b), to Zn m¡1 f (x)dx · n X k=m f (k) · n+1 Z f (x)dx; Zn f (x)dx. m (ii) jeżeli f jest malejaca ˛ w (a, b), to n+1 Z m 2.3 f (x)dx · n X k=m f (k) · m¡1 Notacja asymptotyczna Implementujac ˛ algorytm możemy zauważyć, że jego rzeczywista złożoność czasowa w chwili użycia jako programu różni sie˛ od wyliczonej teoretycznie współczynnikiem 12 proporcjonalności, zwiazanym ze sposobem realizacji: zastosowanym jezykiem ˛ ˛ programowania, jakościa˛ sprzetu ˛ i umiejetno ˛ ściami programisty. Zatem określajac ˛ pesymistyczna˛ lub średnia˛ złożoność chcemy podawać tylko naj- ważniejsza˛ cze˛ść informacji pochodzacych z teoretycznych wyliczeń, a mianowicie rzad ˛ ˛ wielkości. Określa on zachowanie funkcji opisujacej ˛ złożoność czasowa˛ algorytmu dla dużych wartości argumentu (dla danych o dużym rozmiarze). Mówimy wtedy o asymptotycznym czasie (złożoności czasowej). Ponieważ badane przez a,˛ musza˛ przyjmować nas funkcje maja˛ określać złożoność czasowa˛ lub pamieciow ˛ wartości dodatnie, przynajmniej dla dostatecznie dużych argumentów. Takie funkcje nazwiemy asymptotycznie dodatnimi. Oto dokładna definicja: Definicja 12. Funkcje˛ f : N ! R nazywamy asymptotycznie dodatnia,˛ gdy istnieje liczba n0 2 N taka, że dla wszystkich n > n0 zachodzi nierówność f (n) > 0. Założenie: W całym rozdziale zakładamy, że rozważamy funkcje asymptotycznie dodatnie o argumentach bed liczbami naturalnymi lub zerem. Założenia ˛ acych ˛ tego nie bedziemy powtarzać w dalszej cze˛ści tego skryptu. ˛ 2.3.1 Notacja ”o-małe” Definicja 13. Piszemy f (n) = o(g(n)), gdy dla dowolnej stałej c > 0 istnieje n0 2 N takie, że 0 < f (n) · cg(n) dla wszystkich n ¸ n0 . Intuicyjnie, zapis f (n) = o(g(n)) oznacza to, że f (n) jest pomijalna wzgledem ˛ g, gdy n da˛ży do nieskończoności. f (n) n!1 g(n) Twierdzenie 14. Jeżeli lim = 0, to f (n) = o(g(n)). Dowód: Niech c > 0. Z założenia istnieje n0 , takie, że dla n > n0 f (n) < c. g(n) Ponieważ obie funkcje sa˛ asymptotycznie dodatnie, istnieje n1 takie, że dla n > n1 f (n) > 0 i g(n) > 0. Ostatecznie otrzymujemy nierówność 0 < f (n) · cg(n) 13 dla n > maxfn0 , n1 g. ¥ p Przykład 1. lg n = o( n), istotnie limn!1 lgpnn = 0, co ilustruje rysunek y 10 5 0 0 25 50 75 100 x -5 -10 Rys. 3. Wykresy funkcji f(x)= p lgx (czerwony) i g(x)= x (zielony) Gdy zwiekszymy zakres argumentów "pomijalność" funkcji f (x) = lg x wzgledem ˛ p˛ g(x) = x dla "dużych" argumentów jest jeszcze bardziej widoczna. y 100 75 50 25 0 0 2500 5000 7500 1e+4 x Rys. 4. 2.3.2 Wykresy funkcji f(x)= p lgx (czerwony) i g(x)= x (zielony) Notacja ”O-duże” Definicja 15. Piszemy f(n) = O(g(n)), gdy istniej a˛ c > 0, n0 2 N takie, że 0 < f (n) · cg(n) dla wszystkich n¸ n0 . (Mówimy wówczas, że f jest co najwyżej rzedu ˛ g.) 14 (n) Twierdzenie 16. Jeżeli lim fg(n) = k ¸ 0, to f (n) = O(g(n)). n!1 (n) Dowód. Założenie lim fg(n) = k ¸ 0 można zapisać nastepuj ˛ aco: ˛ dla dowolnego n!1 ε > 0 istnieje n0 2 N takie, że dla dowolnych n > n0 zachodzi nierówność ¡ε · f (n) ¡ k · ε. g(n) (1) Przypomnijmy jednocześnie, że funkcje f i g sa˛ asymptotycznie dodatnie, zatem istnieje n1 2 N takie, że dla dowolnych n > n1 f (n) > 0 i g(n) > 0. Wobec tego kładac ˛ c = ε + k i n = maxfn0 , n1 g, mamy dla n > n (tj. n na tyle dużych, aby f i g przyjmowały dla nich wartości dodatnie i aby zachodziała nierówność (1)) otrzymujemy 0 < f (n) · cg(n), a to z definicji oznacza, że f (n) = O(g(n)).¥ n = 1, co ilustruje rysunek Przykład 2. n+lg n = O(n), istotnie limn!1 n+lg n 5. y 150 125 100 75 50 25 0 0 12.5 25 37.5 50 x Rys. 5. Wykresy funkcji f(x)=x+ lgx (czerwony) i g(x)=x (zielony) Czesto ˛ w literaturze informatycznej podaje sie˛ powyższe twierdzenie zamiast definicji 15 "O-dużego". Należy jednak zauważyć, że definicja 15 obejmuje (n) , wystepuj również przypadki, w których założenie istnienia granicy ilorazu fg(n) ˛ ace ˛ w twierdzeniu, nie jest spełnione. Ilustruje to nastepuj ˛ acy ˛ przykład: można wykazać, że jcos nj = O(1). Istotnie, połóżmy c = 1 n0 = 1. Wówczas dla dowolnego n > n0 zachodzi nierówność j cos nj · 1, 15 co należało wykazać. W tym przypadku nie można było jednak zastosować twierdzenia 16, gdyż granica f (n) j cos nj = lim n!1 g(n) n!1 1 lim nie istnieje. Twierdzenie 17. Jeśli f (n)=o(g(n)) , to f (n) = O(g(n)). Dowód: Uzasadnienie tego faktu wynika bezpośrednio z definicji obu notacji. Twierdzenie 18. Własności notacji ”O-duże” 1. Jeżeli f (n) = O(g(n)) i c>0, to cf (n) = O(g(n)). 2. Jeżeli f (n) = O(g(n)) i h(n) = O(g(n)), to f (n) + h(n) = O(g(n)). 3. Jeżeli f (n) = O(g(n)) i h(n) = O(z(n)),to f (n) ¢ h(n) = O(g(n) ¢ z(n)). 4. Jeżeli f (n) = O(g(n)) i g(n) = O(z(n)),to f (n) = O(z(n)). 5. Jeżeli f (n) = O(g(n)) i h(n) = O(z(n)),to f (n) + h(n) = O(maxfg(n), z(n)g) oraz f (n) + h(n) = O(g(n) + z(n)). Dowód: Dowody własności 1-3 pozostawiamy jako ćwiczenia. (4) Wobec założeń istnieja˛ c1 > 0, n1 2 N oraz c2 > 0, n2 2 N takie, że 0 < f (n) · c1 g(n) dla wszystkich n ¸ n1 0 < g(n) · c2 z(n) dla wszystkich n ¸ n2 . Weźmy n0 = maxfn1 , n2 g oraz c = c1 c2 . Wówczas dla n > n0 zachodzi 0 < f (n) · c1 g(n) · cz(n). 16 (5) Z założenia istnieja˛ c1 > 0, n1 2 N oraz c2 > 0, n2 2 N takie, że 0 < f (n) · c1 g(n) dla wszystkich n ¸ n1 0 < h(n) · c2 z(n) dla wszystkich n ¸ n2 . Połóżmy n0 = maxfn1 , n2 g. Wówczas dla n > n0 0 < f (n) + h(n) · c1 g(n) + c2 z(n) · (c1 + c2 ) maxfg(n), z(n)g ¥ Należy pamieta dodawania ˛ ć, że nie można przenieść własności 3. i 5. (dotyczacych ˛ i mnożenia rzedów wielkości) na operacje odejmowania i dzielenia. Rozważmy ˛ 6 funkcje f(n) = n , g(n) = n6 , h(n) = n2 , z(n) = n3 . Wówczas f (n) = O(g(n)) (n) = O( g(n) i h(n) = O(z(n)), ale zależność fh(n) z(n) ) nie jest prawdziwa, gdyż jest ona równoważna stwierdzeniu n4 = O(n3 ), które jest fałszywe. Oczywiście możemy podać również funkcje, dla których odpowiednie równości zachodza.˛ Dla przykładu połóżmy f, g, h jak wyżej i z1 (n) = n2 . Wówczas uzyskujemy już (n) = O( zg(n) ). Zatem w przypadku dzielenia trzeba być prawdziwa˛ równość fh(n) 1 (n) ostrożnym w posługiwaniu sie˛ notacja˛ "O-duże". 2.3.3 Inne notacje asymptotyczne Definicja 19. Piszemy f (n) = Θ(g(n)), gdy istnieja˛ c1 > 0, c2 > 0, n0 2 N takie, że c1 g(n) · f (n) · c2 g(n) dla wszystkich n ¸ n0 . Mówimy wtedy, że g(n) jest asymptotycznie dokładnym oszacowaniem funkcji f(n) lub f jest dokłanie rzedu ˛ g. Podamy teraz kilka twierdzeń opisujacych własności wprowadzonych notacji i ˛ zależności miedzy nimi. Proste dowody tych faktów pozostawiamy czytelnikowi ˛ jako ćwiczenie. Twierdzenie 20. Własności 1¡5 notacji "O¡duże" przenosza˛ sie˛ bezpośrednio na notacje˛ Θ. Twierdzenie 21. Jeżeli lim f (n) n!1 g(n) = k > 0, to f(n) = Θ(g(n)). Definicja 22. Piszemy f (n) = Ω(g(n)) gdy istnieja˛ c > 0, n0 2 N takie, że 0 < cg(n) · f (n) dla wszystkich n ¸ n0 . (Mówimy wówczas, że f jest co najmniej rzedu ˛ g.) 17 f (n) n!1 g(n) Twierdzenie 23. Jeżeli lim Twierdzenie 24. Θ(g(n)). = 1, to f (n) = Ω(g(n)). Jeżeli f (n) = Ω(g(n)) i f (n) = O(g(n)), to f (n) = Definicja 25. Mówimy, ze f i g sa˛ funkcjami asymptotycznie równymi, gdy g(n) ¡ f (n) = o(f (n)). Piszemy wówczas g(n) » f (n). 3 Bibliografia 1. Banachowski L., Diks K., Algorytmy i struktury danych; Wydawnictwo Naukowo-Techniczne, Warszawa 1996. 2. Banachowski L., Kreczmar A., Elementy analizy algorytmów, Wydawnictwo Naukowo-Techniczne, Warszawa 1982. 3. Cormen T.H., Leiserson Ch.E., Rivest R.L., Wprowadzenie do algorytmów, Wydawnictwo Naukowo-Techniczne, Warszawa, 2001. 4. Knuth D.E., Sztuka programowania, t. 1, Wydawnictwo Naukowo-Techniczne, Warszawa 2002. 5. Manber U., Introduction to Algorithms, Addison-Wesley Publishing Company, New York, 1989. 6. Sedgewick R., Flajolet P., An introduction to the Analysis of Algorithms, Addison-Wesley Publishing Company, New York, 1996. 18