« poprzedni punkt   następny punkt »


4.6. Notacja asymptotyczna

Złożoność algorytmów zwykle mierzymy liczbą wykonywanych przez algorytm operacji, przy tym na ogół, bierzemy pod uwagę tylko pewne charakterystyczne (np. najbardziej kosztowne) dla algorytmu operacje. Miara ta jest najbardziej interesująca wtedy, gdy jest wyrażona jako funkcja zależna od rozmiaru danych. Na przykład dla algorytmu Hornera opisanego w przykładzie 4.5.1(3), rozmiar danych - to stopień wielomianu, którego wartość obliczamy. Złożoność algorytmu można opisać funkcją T(n) = n, wyrażającą fakt, że dla obliczenia wartości wielomianu stopnia n musimy wykonać n iteracji pętli. Mając taką informację, możemy obliczyć czas potrzebny do obliczenia wartości wielomianu stopnia 100 na dowolnym komputerze: będzie to 100*czas potrzebny do wykonania jednej iteracji na tym komputerze + stała (koszt dwu instrukcji przypisania na początku algorytmu). W gruncie rzeczy, funkcja T ma nam pozwolić określić koszt algorytmu ale niekoniecznie z dokładnością do sekund czy milisekund. Jest rzeczą drugorzędną czy algorytm wykona n czy n+3 operacje. Natomiast jest rzeczą istotną, czy jego złóżoność wyraża się funkcją f(n)=n, czy f(n) = n3, i jak zachowuje się algorytm dla dużych n. Interesuje nas raczej rząd kosztu, szybkość z jaką rośnie do nieskończoności niż konkretne liczby. Złożoność algorytmu będzie używana jako (kryterium) argument za lub przeciw użyciu konkretnego algorytmu.

Przypuśćmy, że mamy do dyspozycji 6 algorytmów o złożoności odpowiednio lg n, n, n2, n3 i 2n. Tabliczka na rysunku 4.6.1 podaje oszacowanie maksymalnego rozmiaru danych, które można rozwiązać algorytmem o ustalonej złożoności w podanym czasie, na komputerze, który wykonuje 106 operacji na sekundę. Na przykład, w czasie 1 sek można rozwiązać przy pomocy algorytmu o złożoności T(n)= n2 co najwyżej zadanie o rozmiarze 1000, natomiast algorytmem o złożoności T(n)=2n można rozwiązać tylko co najwyżej zadanie o rozmiarze 19. Warto też zwrócić uwagę, jak powoli zmieniają się rozmiary zadań ze wzrostem przeznaczonego na ich wykonanie czasu, gdy mamy do czynienia ze złożonością określoną funkcją wykładniczą 2n. Wartości większe niż 1010 zostały oznaczone symbolem ∞.

czas\złożoność

lg n

n

nlgn

n2

n3

2n

1sek

106

63*103

103

100

19

1min

6*107

28*105

77*102

390

25

1h

36*108

13*107

60*103

15*102

31

Rys. 4.6.1

W tym paragrafie zajmiemy się porównywaniem szybkości wzrostu wartości f(n) funkcji f : N → R+ przy n dążącym do nieskończoności.

Definicja 4.6.1

Niech f i g będą funkcjami określonymi dla liczb naturalnych i o wartościach w R+.

Powiemy, że funkcja f = O(g) wttw istnieją stała c>0 i liczba naturalna n0 takie, że dla wszystkich liczb naturalnych n > n0, f(n) ≤ c*g(n).

Analogicznie, powiemy, że f = Ω (h) wttw istnieją stała c>0 i liczba naturalna n0 takie, że dla wszystkich liczb naturalnych n > n0, c*h(n) ≤ f(n).

Jeśli równocześnie zachodzą oba warunki f = O(g) i f = Ω (g), to mówimy, że funkcja f ma ten sam rząd co funkcja g i oznaczmy krótko f = Θ (g).

Jeśli f = O(g), to funkcja g szacuje z góry wartości funkcji f dla dostatecznie dużych n. Inaczej mówiąc funkcja g szybciej dąży do nieskończoności niż funkcja f. Jeśli f = Ω (g), to funkcja g szacuje wartości funkcji f z dołu dla dostatecznie dużych n, tzn. funkcja g rośnie wolniej do nieskończoności niż funkcja f.

Przykład 4.6.1

  1. n2/2 + 5n = O(n2). Rzeczywiście, dla c= 5 i wszystkich n>1, n2/2 + 5n ≤ 5n2.
  2. n = O(2n), bo n ≤ 2n dla wszystkich liczb naturalnych n. Rzeczywiście dla n>1 mamy
  3. n= 2 * 3/2 * 4/3 * ...* n/(n-1)

    W powyższej równości, po prawej stronie mamy (n-1) elementów, z których każdy jest ≤ 2. Czyli n ≤ 2n-1 < 2n.

  4. lg (n+1) = O(n), co wynika natychmiast z (2).
  5. lg n! = O(n lg n), ponieważ lg n! = lg(1*...*n) = lg1 + lg 2 +..+lg n ≤ n lg n.
  6. lg n! = Ω (n lg n), ponieważ :

Pytanie 4.6.1: Które z podanych zależności są prawdziwe?

1. n2/100 + 500n = O(n)

2. n3/2 + n2 = Ω (n3)

3. 2n = O(2lgn)

4. n = O(2lgn)

Następujące twierdzenie pozwala zastosować metody analizy matematycznej do porównywania szybkości wzrostu funkcji.

Twierdzenie 4.6.1

Niech f : N → R+, g : N → R+ oraz lim n → ∞ f(n)/g(n) = c, gdzie c jest pewną stałą. Wtedy

  1. jeśli c jest liczbą rzeczywistą oraz c ≠ 0, to funkcje f i g mają ten sam rząd, f = Θ(g).
    W przeciwnym przypadku funkcje mają różne rzędy i
  2. jeśli c=0, to f = O(g),
  3. jeśli c= + ∞ , to f = Ω (g).

Dowód twierdzenia pomijamy.

Dla przykładu rozważmy funkcje f(n) = √ n i g(n) = lg n. Ponieważ (lgn)/ √ n przy n dążącym do nieskończoności dąży do 0 (limn → ∞ ((lgx)/ √ x) = limn → ∞ ((lgx)'/( √ x)') = limn → ∞ (2/(ln2* √ x)) = 0), zatem na mocy powyższego twierdzenia funkcja lg dąży do nieskończoności wolniej niż funkcja √ , czyli g = O(f) oraz , f ≠ Θ (g).

Zadanie 4.6.1

Porównaj rzędy funkcji f(n) = lg n (logarytm przy podstawie 2) i g(n) = log n (logarytm przy podstawie 10).

Rozwiązanie. Ponieważ dla dowolnych a ≠ 1 i a, b >0, loga b = logc b/logc a, zatem log n = (1/lg10)lg n. Wynika stąd, że log n = Θ (lg n).


« poprzedni punkt   następny punkt »