Złożoność obliczeniowa
Transkrypt
Złożoność obliczeniowa
ZŁOŻONOŚĆ OBLICZENIOWA Wyznaczanie złożoności obliczeniowej dokładnej i asymptotycznej Złożoność obliczeniowa algorytmów Chcemy podać miarodajną ocenę efektywności algorytmu, abstrahując od komputera, techniki (języka) programowania, szczegółów technicznych implementacji. W tym celu dążymy do podania zależności funkcyjnej wiążącej złożoność (pracochłonność) algorytmu z rozmiarem problemu. Dlatego definiujemy następujące pojęcia: Operacja podstawowa – pozwala ocenić złożoność czasową algorytmu, abstrahując od komputera i języka programowania (czas wykonania nie jest miarodajny). Na ogół jest to operacja wykonywana najczęściej (najbardziej zagnieżdżona w strukturze algorytmu). Złożoność obliczeniową algorytmu wiążemy ze złożonością jego najbardziej czasochłonnego fragmentu. Rozmiar danych wejściowych n Krotność wykonania operacji podstawowej Z = f(n) Rozsądny i nierozsądny czas działania W analizie złożoności algorytmów mamy do czynienia z dwoma kategoriami funkcji wiążących rozmiar danych wejściowych z czasem wykonania algorytmu. funkcje rozsądne (wielomianowe) – problemy algorytmiczne łatwo rozwiązywalne: log(n), n, nlog(n), n2,n3, nk funkcje nierozsądne (niewielomianowe, wykładnicze i ponadwykładnicze) – problemy algorytmiczne trudno rozwiązywalne: nlog(n), 2n, 3n, kn, n!, nn Mówimy tak mimo oczywistej względności tych problemów. Np. „rozsądny” algorytm nk, k=20 ma gorszą złożoność obliczeniową niż „nierozsądny” algorytm kn, k=1,05 dla rozmiaru problemu n<3324. Jednakże zawsze znajdziemy taką wartość graniczną rozmiaru problemu obliczeniowego, dla której algorytm trudno rozwiązywalny będzie rozwiązywany dłużej niż algorytm łatwo rozwiązywalny. Problem ułożenia 4 puzzli (2x2): (14)(24)(34)(44) = 4!44 = 6144 warianty W najgorszym przypadku dla n puzzli jest do sprawdzenia n!4n przypadków (n – ilość puzzli) Problem ułożenia 9 puzzli (3x3): (14)(24)(34) ... (84) (94) = 9!49 = 95 miliardów wariantów Gdyby puzzli było 25 (tylko 5x5) to do sprawdzenia będzie już: (14)(24)(34) ... (244)(254) = 25!425 = liczba 41-cyfrowa Rys. 1. Porównanie wartości osiąganych przez funkcje wielomianowe i wykładnicze Rys. 2. Porównanie wartości osiąganych przez funkcje wielomianowe i wykładnicze Rzędy wielkości funkcji, rzędy złożoności obliczeniowej, symbole oszacowań asymptotycznych (notacja Landaua) Poniższe notacje opisują asymptotyczne zachowanie funkcji. a) Notacja duże O Określa ograniczenie funkcji od góry. Funkcja f(n) jest rzędu g(n), gdy istnieje dodatnia wartość stałej c dla której począwszy od pewnej wartości n0 wartości funkcji f(n) cg(n). Zapisujemy to jako f(n)=O(g(n)) (można także używać zapisu f(n)O(g(n))) Rys. 3. Ograniczenie górne funkcji - notacja O(n) np: log(n) = O(n log(n)) n3+5n+12 = O(n3) Zależność pomiędzy funkcjami f i g można wyznaczyć obliczając granicę ich ilorazu: f(n) = O(g(n)) gdy f ( n) c0 n g ( n) lim Notacja małe o Określa ograniczenie funkcji od góry. Funkcja f(n) jest rzędu g(n), gdy istnieje dodatnia wartość stałej c dla której począwszy od pewnej wartości n0 wartości funkcji f(n) < cg(n). Zapisujemy to jako f(n)=o(g(n)) np: n = o(n) 1/n = o(1) Zależność pomiędzy funkcjami f i g można wyznaczyć obliczając granicę ich ilorazu: f(n) = o(g(n)) gdy lim n f ( n) 0 g ( n) b) Notacja Określa ograniczenie funkcji od dołu. Funkcja f(n) jest rzędu g(n), gdy istnieje dodatnia wartość stałej c dla której począwszy od pewnej wartości n0 wartości funkcji f(n) cg(n). Zapisujemy to jako f(n)=(g(n)) Rys. 4. Ograniczenie dolne funkcji - notacja np: n3 = (n2) 2n2+1 = (n2) Zależność pomiędzy funkcjami f i g można wyznaczyć obliczając granicę ich ilorazu: f(n) = (g(n)) gdy f ( n) f ( n) c0 lub lim n g ( n) n g ( n) lim Notacja mała Określa ograniczenie funkcji od dołu. Funkcja f(n) jest rzędu g(n), gdy istnieje dodatnia wartość stałej c dla której począwszy od pewnej wartości n0 wartości funkcji f(n) > cg(n). Zapisujemy to jako f(n)=(g(n)) np: 3n = (2n) n2 = (n ln(n)) Zależność pomiędzy funkcjami f i g można wyznaczyć obliczając granicę ich ilorazu: f(n) = (g(n)) gdy lim n f ( n) g ( n) c) Notacja Określa ograniczenie funkcji zarówno od dołu jak i od góry. Funkcja f(n) jest rzędu g(n), gdy istnieją takie dodatnie wartości stałych c1 i c2 dla których począwszy od pewnej wartości n0 wartości funkcji f(n) c1g(n) i zarazem f(n) c2g(n). Zapisujemy to jako f(n)= (g(n)) Rys. 5. Ograniczenie górne i dolne funkcji - notacja np: 3n3 + 5n = (n3) (1+1/n)n = (1) Zależność pomiędzy funkcjami f i g można wyznaczyć obliczając granicę ich ilorazu: f(n) = (g(n)) gdy f ( n) c0 n g ( n) lim Rząd złożoności obliczeniowej jest najważniejszym czynnikiem określającym przydatność algorytmu. Podsumowując: dla f(n) = O(g(n)) dla f(n) = o(g(n)) dla f(n) = (g(n)) dla f(n) = (g(n)) dla f(n) = (g(n)) f ( n) n g (n) f ( n) lim 0 n g ( n) f ( n) lim 0 n g ( n) f ( n) lim n g ( n) f ( n) 0 lim n g ( n) lim Wzajemne zależności notacji oszacowań asymptotycznych Tab. 1. Klasy funkcji opisujących złożoność obliczeniową algorytmów Klasa funkcji subliniowa wielomianowa f(n)=(nc) c>0 niewielomianowa Przykładowe funkcje Typ funkcji stała f(n)=O(1) polilogarytmiczna f(n)=(1) i f(n)=(logcn), liniowa f(n)=(n) quasi-liniowa f(n)=(n) i f(n)=O(n logn) kwadratowa f(n)= (n2) superwielomianowa f(n)=(nc) i f(n)=o(dn), wykładnicza f(n)=(en) i f(n)=O(fn) superwykładnicza f(n)=(cn) sin(n), 1/n c>0 log(log(n)), log2(n) n, 3n(1+1/n)n n log(n), n log(log(n)) n2, n(n-1)+3 c>0, d>1 nlog(n), en e>1, f>1 2n, n2 3n c>1 2n n, n!, nn Funkcje polilogarytmiczne spotyka się przy analizie algorytmów równoległych. Funkcje quasi-liniowe i kwadratowe określają złożoność obliczeniową algorytmów sortujących. Funkcje niewielomianowe spotyka się przy rozwiązywaniu trudnych problemów kombinatorycznych. Algorytmy niewielomianowe są użyteczne tylko dla bardzo małych wartości n Właściwości relacji asymptotycznych Jeżeli f(n)=O(g(n)) i g(n)= (h(n)) to f(n)=O(h(n)) Jeżeli f(n)= (p(n)) i g(n)= (q(n)) to f(n) g(n)= (p(n) q(n)) Jeżeli f(n)=O(g(n)) to f(n)+g(n)= (g(n)) Jeżeli f(n)=O(g(n)) i g(n)= (h(n)) to f(n)+g(n)= (h(n)) Kategorie złożoności Kategorie złożoności opisuje się notacją . Dowolna funkcja należąca do pewnej kategorii funkcji może ją (kategorię) reprezentować. Na ogół kategorię funkcji reprezentuje najprostsza funkcja należąca do niej. Dlatego kategorię funkcji liniowych reprezentuje funkcja (n). Właściwości rzędów wielkości funkcji g(n) = (f(n)) g(n) = (f(n)) dowolna funkcja należąca do pewnej kategorii funkcji może ją reprezentować Jeżeli a>1 i b>1 loga(n) = (logb(n) i kategorię tę możemy reprezentować przez funkcję (log(n)) wszystkie funkcje logarytmiczne należą do tej samej kategorii złożoności Jeżeli a>1 i b>1 an = o(bn) nie wszystkie funkcje wykładnicze należą do tej samej kategorii złożoności Dla każdego a>0 an = o(n!) funkcja n! jest gorsza obliczeniowo od dowolnej funkcji wykładniczej Jeżeli c0 i d>0 oraz f(n) = O(g(n)) i h(n) = (g(n)) cf(n) + dh(n) = (g(n)) Dla podstawowych kategorii złożoności: (log(n)), (n), (nlog(n)), (n2), (na), (nb), (cn), (dn), (n!), (nn) b>a>2 i d>c>1 funkcja leżąca na prawo jest ograniczeniem górnym kategorii sąsiedniej z lewej strony fl(n) = o(gp(n)) f(n) = O(g(n)) f(n) = (g(n)) Wykorzystanie granic do określenia rzędu funkcji c to f (n) ( g (n)), dla c 0 f ( n) lim 0 to f (n) o( g (n)) n g ( n) to f (n) ( g (n)) reguła L’Hospitala Jeżeli funkcje f(n) i g(n) są różniczkowalne i ich pochodne wynoszą f’(n) i g’(n) oraz jeżeli lim f (n) lim g (n) n n to lim n f ( n) f ' ( n) lim n g ( n) g ' ( n) Przykład: f(n)= n3 ln(n)=O(n2) ? Czy 3 lim n n ln (n) ln (n) 1/n 2 n 2 lim lim lim lim 0 2 n n n n n H n 1 /( 2 n ) n n TAK Przykłady „Przyspieszanie” algorytmu badającego planarność grafu Poniższa tabela prezentuje efekty polepszania złożoności algorytmu, a co za tym idzie zmiany klasy funkcji opisującej jego złożoność. Tab. 2. Przykład postępu jaki dokonał się w dziedzinie projektowania algorytmów badania planarności grafu Rok opracowania algorytmu Złożoność algorytmu Czas obliczeń dla grafu o n=100 wierzchołkach Rozmiar analizowanego grafu możliwy do weryfikacji w czasie 1 minuty 1930 O(n6) 325 lat 4 1963 O(n3) 2 godz. 48 min. 18 1967 O(n2) 100 s 77 1971 O(n log(n)) 7s 643 1974 O(n) 1s 6000 Zwiększanie mocy komputerów a złożoność obliczeniowa Tab. 3. Wpływ poprawy szybkości komputerów na zwiększenie rozmiarów problemów algorytmicznych możliwych do rozwiązania w zadanym czasie Moc obliczeniowa komputera 1 sekunda 1godzina 1 rok złożoność obliczeniowa n2 2n n! n2 2n n! n2 2n n! 1Gflops (109), 1985 ns=3,1104 ns=29 ns=12 ng=1,8106 ng=41 ng=15 nr=1,7108 nr=54 nr=18 1 Tflops (1012), 1997 32ns 1,33ns 1,17ns 32ng 1,24ng 1,13ng 32nr 1,18nr 1,11nr 1000ns 1,67ns 1,42ns 1000ng 1,48ng 1,33ng 1000nr 1,36nr 1,28nr 1 Pflops (1015), 2008 wg listy TOP 500 (500 najszybszych superkomputerów) „MilkyWay 2” – najszybszy superkomputer na świecie w czerwcu 2014. Składa się z 48 000 koprocesorów Intel Xeon Phi oraz 32 000 procesorów Intel Xeon i działa ze szczytową wydajnośc3ią 54,9 petaflopsów (54,9 biliarda operacji zmiennoprzecinkowych na sekundę) „Sunway” – najszybszy superkomputer na świecie w czerwcu 2016. Działa ze szczytową wydajnością 125,4 petaflopsów (125,4 biliarda operacji zmiennoprzecinkowych na sekundę) Moc obliczeniowa komputera a typ funkcji złożoności algorytmu Tab. 4. Porównanie czasów obliczeń algorytmów: wielomianowego i liniowego dla tego samego problemu wykonywanych na superkomputerze i komputerze domowym złożoność obliczeniowa algorytmu rozmiar problemu n algorytm 1 wielomianowy: 3n3 ns (3 ns dla problemu o rozmiarze n=1) algorytm 2 liniowy: 107 n ns (10 ms dla problemu o rozmiarze n=1) czas obliczeń superkomputer komputer PC 10 3 s 0,1 s 100 3 ms 1s 1000 3s 10 s 104 50 min. 1 min. 40 s 106 95 lat 2 godz. 47 min. Złożoność czasowa algorytmów operacja podstawowa – pozwala ocenić złożoność czasową algorytmu, abstrahując od komputera i języka programowania (czas wykonania nie jest miarodajny). Na ogół jest to operacja wykonywana najczęściej. rozmiar danych wejściowych pesymistyczna złożoność obliczeniowa – złożoność obliczeniowa dla najgorszego przypadku wystąpienia danych wejściowych W(n) = max(t(I)) IDn (I jest elementem zbioru danych o rozmiarze n) t(I) jest liczbą operacji wykonywanych przez algorytm na danych I optymistyczna złożoność obliczeniowa – złożoność obliczeniowa dla najlepszego przypadku wystąpienia danych wejściowych w(n) = min(t(I)) oczekiwana złożoność obliczeniowa – bierzemy pod uwagę prawdopodobieństwo występowania różnych przypadków danych wejściowych. Często najgorszy przypadek pojawia się bardzo rzadko. Dlatego istotniejsze jest rozważanie przypadku średniego. A(n) = p( I )t ( I ) I Dn p(I) jest prawdopodobieństwem występowania danych I Gdy W(n)=A(n) to algorytm jest niewrażliwy czasowo, a jego złożoność czasowa zależy tylko od rozmiaru danych. Przykład: Wyszukiwanie elementu w tablicy – operacja podstawowa: porównanie elementu z kolejną wartością w tablicy – rozmiar danych wejściowych: n – pesymistyczna złożoność obliczeniowa: W(n) = max (i) = n = (n) (ostatni element) i1..n – optymistyczna złożoność obliczeniowa: w(n) = min (i ) = 1 = (1) (pierwszy element) i1..n – oczekiwana złożoność obliczeniowa: 1 n(n 1) n 1 1 n = = (n) i = n 2 2 n i 1 i 1 i 1 (średnio połowa listy będzie przejrzana) n A(n) = p(I )t ( I ) = n 1 ni = Wrażliwość algorytmów Określa na ile funkcje W(n) i A(n) są reprezentatywne dla wszystkich danych wejściowych. wrażliwość pesymistyczna (najgorszego przypadku): (n) = max(t(I1)-t(I2)) I1,I2Dn wrażliwość oczekiwana (średniego przypadku): (n) = w(n) t ( I ) A(n) 2 p( I ) IDn (n) jest odchyleniem standardowym zmiennej losowej w(n) jest wariancją zmiennej losowej n A(n) = p(I )t ( I ) jest oczekiwaną złożonością obliczeniową i 1 Im większe wartości (n) i (n), tym dany algorytm jest bardziej wrażliwy na dane wejściowe. Przykład – c.d.: Wyszukiwanie elementu w tablicy – c.d. – wrażliwość pesymistyczna: (n) = max(t(I1)-t(I2)) = t(n)-t(1) = n-1 (w najgorszym przypadku element jest na ostatniej pozycji na liście, w najlepszym przypadku jest na pierwszej pozycji na liście) – wrażliwość oczekiwana: (n) = t ( I ) A(n) 2 n 1 1 = i 2 n i 1 n p( I ) = I Dn 2 n2 1 12 n 0,29n 12 algorytm jest wrażliwy na dane wejściowe Złożoność pamięciowa algorytmów Przez złożoność pamięciową definiujemy liczbę komórek pamięci niezbędną do realizacji algorytmu. Dla większości problemów algorytmicznych złożoność pamięciowa ma mniejsze znaczenie niż złożoność czasowa. Pamięć wykorzystywaną przez algorytm dzielimy na pamięć zawierającą dane wejściowe i pamięć dodatkową (pomocniczą) wykorzystywaną przez algorytm do organizacji obliczeń. Jeżeli pamięć dodatkowa nie zależy od rozmiaru danych wejściowych to algorytm taki nazywamy działającym w miejscu (in place). Dla złożoności pamięciowej także można mówić o: pesymistycznej złożoności pamięciowej – złożoności pamięciowej najgorszego przypadku oczekiwanej złożoności pamięciowej – złożoności pamięciowej średniego przypadku Często dla różnych wersji danego algorytmu złożoności czasowa i pamięciowa są do siebie odwrotnie proporcjonalne. Dla wielu problemów algorytmicznych istnieje „równowaga” pomiędzy złożonością czasową i pamięciową, tzn. że można zmniejszyć złożoność czasową kosztem zwiększenia zapotrzebowania na pamięć i vice-versa.