Jak ocenić złożoność obliczeniową algorytmu? - 2h
Teoria obliczeń
Teoria obliczeń to dział informatyki. Jedną z gałęzi tego działu jest teoria złożoności obliczeniowej. W uproszczeniu można powiedzieć, że zajmuje się ona oszacowaniem wydajności czasowej i pamięciowej algorytmów. Teoria złożoności obliczeniowej bazuje na wielu modelach, które służą do łatwego porównywania algorytmów.
Przykład wyznaczania złożoności obliczeniowejń
Załóżmy że chcemy policzyć sumę elementów tablicy. Może nam w tym pomóc następujący algorytm:
public int sum(int[] numbers) { int sum = 0; for (int number : numbers) { sum += number; } return sum; } |
Dodając te operacje do siebie otrzymujemy wzór:
f(n)=1+n+1=n+2
Zatem złożoność naszego algorytmu opisana jest przez funkcję f(n) = n + 2.
Złożoność obliczeniową określamy jako funkcję danych wejściowych algorytmu. Wyznacza się ją jak opisałem w poprzednim punkcie – licząc operacje. O ile dla naukowców znalezienie dokładnej funkcji może być bardzo istotne, to w praktyce wystarczą jej oszacowania. Takie oszacowania to notacja Ο (dużego O), notacja Ω (omega) i notacja Θ (theta).
Notacja Ο (dużego O)
Notacja ta zakłada, że istnieje funkcja g(n), dla której spełniona jest poniższa własność:
∀n⩾n0:f(n)⩽c∗g(n)
Własność ta oznacza, że wynik funkcji g(n) pomnożony przez jakąś stałą c będzie większy bądź równy wynikowi funkcji f(n). Własność ta jest spełniona dla wszystkich n, które będą większe od n0.
Notacja Ο jest oszacowaniem z góry. Zatem można powiedzieć, że jeśli algorytm ma złożoność Ο(n^2) to ma także złożoność Ο(n^3) czy nawet Ο(n!). Jednak Ο(n^2) może być najlepszym oszacowaniem złożoności danego algorytmu. |
Z racji tego, że jest to oszacowanie pomijamy w nim wszelkiego rodzaju stałe. Zatem Ο(2n + 123), Ο(2n) i Ο(n) to ta sama złożoność obliczeniowa. Stałe te i tak nie mają znaczenia przy odpowiednio dużych wartościach n. |
O(1)
Ο(n)
Złożoność liniowa. Jest to specyficzny przypadek złożoności wielomianowej. Czas rozwiązania problemu jest wprost proporcjonalny do wielkości danych wejściowych. Przykład problemu, dla którego istnieje algorytm Ο(n):
Na wejściu programu jest tablica liczb o długości N. Znajdź sumę wszystkich liczb w tablicy wejściowej.
Ο(log(n))
Złożoność logarytmiczna, czas rozwiązania zależy od wyniku logarytmu3 z wielkości danych wejściowych. Przykład problemu, dla którego istnieje algorytm Ο(log(n)):
Na wejściu programu jest posortowana tablica liczb o długości N. Sprawdź czy liczba x istnieje w tablicy wejściowej.
To popularny algorytm przeszukiwania binarnego. Jego nazwa pochodzi od tego, że przy każdej iteracji algorytmu dzielimy przeszukiwany zbiór na dwie równe4 części. Algorytmy, które dzielą w ten sposób problem na mniejsze problemy przeważnie są zależne od logarytmu wielkości danych wejściowych.
Warto podkreślić praktyczne zastosowanie łączenia zdań logicznych poprzez spójniki logiczne w zdania złożone. Dzięki temu możemy pisać kody źródłowe, które są krótsze i bardziej zwięzłe, gdyż wiele instrukcji warunkowych można zastąpić jedną.
Realizując ćwiczenia i zadania polegające na stworzeniu kodu źródłowego o określonym działaniu, warto podkreślić, by przed przystąpieniem do pisania kodu źródłowego pamiętać o wcześniejszych etapach rozwiązywania problemów z wykorzystaniem komputera – na przykład sformułowaniu algorytmu.
Wykorzystując zmienne typu string w języku C++, do kodu źródłowego dodaje się dyrektywę #include
Zadanie 2 (łatwe)
Zadanie 3 (dosyć łatwe)
Ile razy wzrośnie zakres n-bitowych liczb binarnych, gdy liczbę bitów zwiększymy o 1, 2, 3, 4, m bitów? Odpowiedź uzasadnij.
Zadanie 4 (łatwe)
Zadanie 5 (łatwe)
Zadanie 6 (dosyć łatwe)
Wyznaczyć 100 cyfr ułamkowych dwójkowego rozwinięcia liczby dziesiętnej 0,8(10).
Zadanie 7 (dosyć łatwe)
Wyznacz błąd względny i bezwzględny rozwinięcia dwójkowego liczby
dziesiętnej 0,2(10)
o dokładności 10 binarnych cyfr ułamkowych.
Zadanie 1 (łatwe)- binarne na dziesiętne
Podział liczby na cyfry
Zapisz specyfikację oraz algorytm wypisujący poszczególne cyfry liczby.
Schemat blokowy
Zapisz w postaci schematu blokowego algorytm obliczający dla danej liczby naturalnej liczbę jej dzielników właściwych większych od 1. Wykorzystaj pseudokod zapisany w podręczniku na stronie 17 /Ćwiczenie 5 str. 16/
Programy w C++
Napisz w środowisku CodeBlocks/ na st następujące programy:
1. Program sprawdzający czy liczba jest parzysta
2. Program wypisujący poszczególne cyfry z liczby
3. Program wyświetlający liczbę dzielnikó właściwych liczby n większych od 1
4. Program wypisujący NWD dla dwóch liczb naturalnych a i b
Test wiedzy
|
Pytanie 1. Zamień liczbę 111 systemu dwójkowego na dziesiątkowy i podaj prawidłowy wynik A/ 6 B/ 7 C/ 10 D/ 11 E/ 12 Pytanie 2 Zamień liczbę 11001 systemu dwójkowego na dziesiątkowy i podaj prawidłowy wynik A/ 20 B/ 24 C/ 25 D/ 27 E/ 31 Pytanie 3 Zamień liczbę 101 systemu dwójkowego na dziesiątkowy i podaj prawidłowy wynik A/ 5 B/ 7 C/ 9 D/ 11 E/ 13 Pytanie 4 Zamień liczbę 110011 systemu dwójkowego na dziesiątkowy i podaj prawidłowy wynik A/ 37 B/ 40 C/ 47 D/ 50 E/ 51 Pytanie 5 Zamień liczbę 10001 systemu dwójkowego na dziesiątkowy i podaj prawidłowy wynik A/ 12 B/ 14 C/ 17 D/ 21 E/ 27 Pytanie 6 Zamień liczbę 56 systemu dziesiętnego na dwójkowy i podaj prawidłowy wynik 110011 111000 110101 110101 111111 Pytanie 7 Zamień liczbę 20 systemu dziesiętnego na dwójkowy i podaj prawidłowy wynik 10100 11111 10010 10001 111101 Pytanie 8 Zamień liczbę 17 systemu dziesiętnego na dwójkowy i podaj prawidłowy wynik A/ 11111 B/ 10101 C/ 100101 D/ 10111 E/ 10001 Pytanie 9 Zamień liczbę 120 systemu dziesiętnego na dwójkowy i podaj prawidłowy wynik A/ 1100100 B/ 1101100 C/ 1100110 D/ 1000010 E/ 1111000 Pytanie 10 Zamień liczbę 6 systemu dziesiętnego na dwójkowy i podaj prawidłowy wynik A/ 110 B/ 101 C/ 100 D/ 111 E/ 1001 Pytanie 11 Zamień liczbę 76 systemu ósemkowego na szesnastkowy i podaj prawidłowy wynik A/ A3 B/ 4F3 C/ 4C D/ 2A E/ 3E Pytanie 12 Zamień liczbę 5F systemu szesnastkowego na dziesiątkowy i podaj prawidłowy wynik A/ 79 B/ 87 C/ 64 D/ 95 E/ 112
Podsumowanie
cout <<nazwa_zmiennej;
if (warunek) instrukcja na tak;
else instrukcja na nie;
for ( inicjalizacja zmiennej; warunek; aktualizacja zmiennej )
{
// Wykonywany kod, jeśli warunek został spełniony
}
Przykład:
for ( int i = 0; i < 10; i++ )
{
cout << i << endl;
}
a) Największą liczbą ośmiocyfrową w systemie dwójkowym jest 11111111, której zapis dziesiętny to 255.
b) Największą liczbą szesnastocyfrową w systemie dwójkowym jest 1111111111111111, której zapis dziesiętny to 65535. Rozwiązanie w pliku T02_CW1.
a) 2 bajty to 16 bitów, czyli w zapisie binarnym 16 cyfr. Największą liczbą będzie zatem 1111111111111111 zapisane binarnie, czyli 65535 w systemie dziesiętnym.
b) 78 w systemie binarnym to 1001110, liczba składa się z 7 znaków, czyli do zapisu potrzeba minimum 7 bitów. Rozwiązanie w pliku T02_CW4.