Możliwości i ograniczenia komputerów Komputer potrafi przeanalizować ogromną ilość danych pochodzących np. z wielu zdjęć rentgenowskich ludzkiego mózgu wykonanych pod stopniowo zwiększanym kątem, tworząc obraz przekroju mózgu. Obraz taki może być użyty do umiejscawiania nieregularności, np. guzów. Komputer nie potrafi przeanalizować pojedynczego zdjęcia twarzy tego samego pacjenta i określić jego wieku z dokładnością do np. 5 lat, a nawet dzieci to potrafią. Komputery są zdolne sterować w najbardziej sprawny i precyzyjny sposób dowolnie skomplikowanymi robotami przemysłowymi używanymi do konstruowania dowolnie skomplikowanych części maszyn. Komputer nie potrafi jednak pokierować robotem tak, aby z kupki gałązek utworzył ptasie gniazdo. Potrafi to każdy, nawet młody ptak. 1
Komputery potrafią grać w szachy na poziomie mistrzów międzynarodowych pokonują znakomitą większość ludzi. Komputery nie potrafią dostosować się jednak do nagłej zmiany reguł np. reguły dopuszczającej podwójny ruch skoczka lub ograniczenie ruchu hetmanem do 5 pól. Kilkunastoletni szachista-amator będzie bardzo szybko w stanie grać dobrze przy nowych regułach. Rozdźwięk ten związany jest z różnicą między inteligencją ludzką a komputerową (tzw. paradoks Moraveca). 2
Oprogramowanie (ang. software) Całość informacji w postaci zestawu instrukcji, zaimplementowanych interfejsów i zintegrowanych danych przeznaczonych dla komputera do realizacji wyznaczonych celów. Celem oprogramowania jest przetwarzanie danych w określonym przez twórcę zakresie. Oprogramowanie jest synonimem terminów program komputerowy oraz aplikacja, przy czym stosuje się go zazwyczaj do określania większych programów oraz ich zbiorów. Oprogramowanie tworzą programiści w procesie programowania. Dział informatyki, zajmujący się całościowym badaniem procesu tworzenia oprogramowania (analiza wymagań, projekt systemu, implementacja, testowanie, wdrożenie, konserwacja), to inżynieria oprogramowania. 3
Komunikaty i informacje Komunikat i informacja to dwa ważne pojęcia informatyki, których znaczenie nieco odbiega od przyjętego w języku codziennym. Komunikat wyodrębniony zbiór znaków danego kodu, przekazywany pomiędzy komunikującymi się obiektami w określonym porządku czasowym i przestrzennym. Relacje pomiędzy komunikatem i informacją nie są wzajemnie jednoznaczne: tę samą informację mogą przenosić różne komunikaty, np. komunikaty sformułowane w różnych językach, lub komunikaty z uzupełnieniami/wstawkami nie zawierającymi dodatkowej informacji (np. kolejne przemówienia na ten sam temat wypełnione tzw. watą słowną). dany komunikat może przenosić informację odmienną w stosunku do różnych adresatów (np. ten sam artykuł w gazecie w stosunku do różnych czytelników). 4
Zatem: ten sam komunikat, różnie interpretowany, może przenosić odmienną informację; w wielu aspektach można traktować informację jako wynik interpretacji komunikatu. Zapis symboliczny reguły interpretacji: K α K komunikat, I informacja, α reguła interpretacyjna. Reguła interpretacyjna może być szczególnym przypadkiem reguły ogólniejszej, stosowanej do zbioru komunikatów zbudowanych zgodnie z jednakowymi prawidłami (np. znajomość danego języka oznacza, że w większości przypadków poprawnie interpretowane są komunikaty sformułowane w tym języku). Komunikaty mogące być interpretowane w ten sposób, że różne interpretacje oparte są na sobie, są to komunikaty o różnym stopniu abstrakcji (np. komunikat pada nie tylko informuje o deszczu, lecz także dodatkowo o tym, że warto wziąć parasol). I 5
Niekiedy język znany jest tylko niewielu wtajemniczonym nikt postronny nie powinien być w stanie uzyskać informacji z zaszyfrowanego przekazu jest ona jednak łatwo dostępna dla posiadaczy klucza. H 2 SO 4 <script language= JavaScript > see you tomorrow jeśli uzna, to raczej opornie jeśli uzna, to raczej opornie JDOOLD HVW RPQLV GLYLVD ( Gallia est omnia divisa in partes tres w kodzie Cezara: A >D, B >E,..., itd.) Najlepsze kasztany są na placu Pigalle (agent J23; Stawka większa niż życie) 6
Programowanie to modyfikowanie, rozszerzanie, naprawianie, ale przede wszystkim tworzenie oprogramowania. Język programowania to usystematyzowany sposób przekazywania komputerowi poleceń do wykonania. Język programowania pozwala programiście na precyzyjne przekazanie maszynie, jakie dane mają ulec obróbce i jakie czynności należy podjąć w określonych warunkach. Języki programowania wiążą się zwykle ze sztywną składnią, dopuszczającą używanie jedynie specjalnych kombinacji wybranych symboli i słów kluczowych. Języki programowania mogą być kompilowane lub interpretowane. Formalna składnia typowego języka programowania zawiera zwykle różne warianty struktur sterujących, wzorce podstawowych instrukcji, sposoby definiowania struktur danych. 7
Struktury sterujące bezpośrednie następstwo wykonaj A, potem B, następnie C pseudokod C C++ Fortran77 Python wczytaj a a = a + 1 wypisz a scanf( %d,&a); a++; printf( %d,a); cin >> a; a++; cout << a; read(*,*) a a = a + 1 write(*,*) a a=input() a=a+1 print a 8
wybór warunkowy (rozgałęzienie warunkowe) jeżeli Q to wykonaj A, w przeciwnym razie wykonaj B Q warunek logiczny, np. a>0 pseudokod C/C++ Fortran77 Python jeżeli a > 0 a = a + 1 c = a * 3 w przeciwnym wypadku a = a 1 c = a / 3 if (a>0) {a++; c=a*3;} else {a--; c = a/3;} if (a.gt. 0) then a=a+1 c=a*3 else a=a-1 c=a/3 endif if a > 0: a=a+1 c=a*3 else: a=a-1 c=a/3 9
Iteracja (pętla) ograniczona wykonaj A dokładnie n razy pseudokod C++ Fortran77 Python pętla od i = 1 do n wypisz i wypisz i*i for (i=1; i<=n; i++) {cout << i; cout << i*i;} do 55 i=1, n write(*,*) i write(*,*) i*i 55 continue for i in range(1:n+1): print i print i*i 10
Iteracja (pętla) ograniczona dopóki Q, wykonuj A pseudokod C++ Fortran77 Python i = 1 dopóki i n wypisz i wypisz i*i i = i + 1 wykonuj A, dopóki Q i = 1 wykonuj wypisz i wypisz i*i i = i + 1 dopóki i n+1 i=1; do while (i<=n) {cout << i; cout << i*i; i++;} i=1 15 if (i.ls. n) then write(*,*) i write(*,*) i*i i=i+1 goto 15 endif i=1 while i<=n: print i print i*i i=i+1 11
pętle zagnieżdżone pętla od i = 1 do n dopóki i n pętla od j = 1 do i pętla od j = 1 do i wypisz i + j wypisz i + j i = i + 1 instrukcja skoku skocz do oznaczonego miejsca w programie i = 1 #G wypisz i wypisz i*i i = i + 1 jeżeli i n skocz do G 12
podprogramy fragment algorytmu zapisany w formie osobnej procedury lub funkcji, np. w celu umożliwienia jego wywoływania dla różnych wartości parametrów. silnia(n): jeżeli n == 0 silnia = 1 w przeciwnym wypadku silnia = n * silnia(n-1) dodaj_i_wypisz(a, b): wypisz a + b wywołanie podprogramu wynik = silnia(10) + 1 pętla od n = 1 do 100 wypisz silnia(n) pętla od i = 1 do 100 pętla od j = 1 do 100 dodaj_i_wypisz(i, j) Przykład w C++ #include <iostream> using namespace std; int silnia(int n){ if (n == 0) return 1; else return n * silnia(n-1); } int main(){ int n; cin >> n; cout << silnia(n); } 13
Typy danych obiekty, na których operują algorytmy: liczby (całkowite, zmiennoprzecinkowe, zespolone, dwójkowe itp.) typ znakowy (pojedynczy znak) typ tekstowy (ciąg znaków) typ logiczny (prawda/fałsz) wskaźnik Zmienna Obszar pamięci przechowujący dane. O rodzaju i sposobie przechowywania decyduje typ zmiennej. 14
Struktury danych Tablica jednowymiarowa (wektor) Poszczególne komórki dostępne są za pomocą kluczy, które najczęściej przyjmują wartości numeryczne. W komórkach można przechowywać zmienne różnego typu. T1 = {1, 4, 5, 12, 24, 10, 0, -4, 12, 15} T1[2] = 4, T1[6] = 10 itp. wypisz (T1[3] + T1[4]) / 2 T2 = {"poniedziałek", "wtorek",..., "niedziela"} T2[1]= "poniedziałek" wypisz "Dzisiaj jest: ", T2[6] 15
Tablica wielowymiarowa Tablica 3x3: T = T T T 11 21 31 T T T 12 22 32 T T T 13 23 33 1 = 4 7 2 5 8 3 6 9 T = {{1,2,3},{4,5,6},{7,8,9}} T 12 = T[1][2] = 2 itp. pętla od i = 1 do 3 pętla od j = 1 do 3 T[i][j] = 10 // przypisanie liczby 10 wszystkim elementom Kolejka Liniowa struktura danych FIFO (First In, First Out; pierwszy na wejściu, pierwszy na wyjściu). Znaczeniowo odpowiadająca nazwie: nowe dane dopisywane są na końcu kolejki, a jako pierwsze obsługiwane są dane z początku. 16
Stos Liniowa struktura danych LIFO (Last In, First Out; ostatni na wejściu, pierwszy na wyjściu), znaczeniowo odpowiadająca nazwie: dane dokładane są na wierzch stosu, również z wierzchołka są ściągane). Drzewo Hierarchiczna struktura danych: korzeń (ojciec) węzeł (potomstwo); potomstwo potomstwa itp., liście na najniższym poziomie 17
Przykłady Generowanie podciągu Wejście: dwie liczby całkowite m i n, gdzie m <= n. Wyjście: posortowana lista m losowych liczb całkowitych z przedziału 1...n, wśród których żadna nie powtarza się dwukrotnie. test losowy Wykorzystujemy algorytm, który analizuje kolejno liczby całkowite 1, 2,..., n i na podstawie odpowiedniego testu losowego decyduje, czy wybrać, czy też odrzucić każdą z nich. Ogólnie, aby wylosować w liczb spośród p pozostałych, należy następną liczbę wybierać z prawdopodobieństwem w/p. 18
szkielet algorytmu: wczytaj m, n wybierz = m pozostało = n pętla od i=1 do n oblicz prawdopodobieństwo wylosowania i-tej liczby, prawd = wybierz / pozostało wygeneruj liczbę losową los z przedziału [0,1) koniec jeżeli los < prawd wypisz i wybierz = wybierz 1 pozostało = pozostało - 1 19
wybieranie Cel realizujemy wybierając m elementów wejściowej tablicy n-elementowej. Po każdym losowaniu sprawdzamy, czy liczba się nie powtórzyła a następnie sortujemy wybrane elementy. 20
szkielet algorytmu: wczytaj m, n pętla od i=1 do m powtarzaj wygeneruj liczbę losową los z przedziału [1,n] jeżeli i > 1 to sprawdź, czy liczba los już wcześniej wystąpiła: wystąpiła = false pętla od j=1 do i-1 jeżeli W[j] == los wystąpiła = true zakończ pętlę jeżeli wystąpiła == true zapisz los do wyniku: W[i] = los posortuj tablicę W koniec 21
przemieszanie Cel realizujemy mieszając (czyli zamieniając) pierwszych m elementów wejściowej tablicy n-elementowej (czyli liczby z przedziału 1...m) z elementami 1...n tej samej tablicy (oczywiście, w szczególnym przypadku taka zamiana może nie nastąpić, gdy chcąc przemieszać i-tą liczbę wylosujemy właśnie liczbę i). Wynikiem (po posortowaniu) jest tablica złożona z pierwszych m przemieszanych elementów. 22
szkielet algorytmu: wczytaj m, n utwórz tablicę T=[1,2,...,n]: pętla od i=1 do n T[i] = i pętla od i=1 do m wygeneruj liczbę losową los z przedziału [1,n] zamień T[i] z T[los] Utwórz tablicę wynikową: W = T[1,2,...,m]: pętla od i=1 do m W[i] = T[i] posortuj tablicę W koniec 23
Wyszukiwanie Wejście: posortowana, n-elementowa tablica liczbowa T oraz liczba p. Wyjście: liczba naturalna, określająca pozycję elementu p w tablicy T, bądź 1, jeżeli element w tablicy nie występuje. wyszukiwanie binarne Wyszukiwanie binarne polega na tropieniu fragmentu tablicy, o którym wiemy, że musi zawierać element p, o ile element ten znajduje się w tablicy T. Początkowo tym fragmentem jest cała tablica. Przedział kurczy się po porównaniu środkowego elementu ze zmienną p i odrzuceniu odpowiedniej połowy tego przedziału. Proces trwa do chwili odnalezienia p w tablicy lub do momentu, gdy wiadomo, że przedział w którym musiałby się on znajdować, jest pusty. Złożoność obliczeniowa: O(log n). (Na analogicznej zasadzie działa np. algorytm bisekcji, służący do znajdywania miejsc zerowych funkcji.) 24
Sformułowanie algorytmu Wiemy, że jeżeli p znajduje się w gdziekolwiek w tablicy T, to musi być w określonym przedziale. Oznaczmy ten fakt skrótem: musi_być(przedział). Szkic programu: zainicjuj przedział jako 1...n pętla // niezmiennik: musi_być(przedział) jeżeli przedział pusty elementu p nie ma w tablicy T koniec oblicz wartość środek, środka przedziału użyj środek do testów, aby zmniejszyć przedział jeżeli p znaleziono podczas testu wynik = pozycja p w tablicy koniec 25
Przedział reprezentujemy przy pomocy dwóch indeksów: dół, góra. Oznaczenie musi_być(dół, góra) oznacza, że jeżeli element znajduje się gdzieś w tablicy, to znajduje się w przedziale domkniętym T[dół... góra]. Inicjowanie: jakie wartości muszą mieć zmienne góra i dół, aby warunek musi_być(dół, góra) był spełniony? Oczywiście 1 oraz n. A zatem: dół = 1 góra = n Następnie sprawdzamy, czy przedział jest pusty: jeżeli dół > góra wynik = -1 // elementu nie ma w tablicy koniec 26
Obliczamy wartość środka przedziału: środek = (dół + góra) div 2 // Dzielenie całkowite Szkic programu wygląda teraz tak: dół = 1 góra = n pętla // niezmiennik: musi_być(dół, góra) jeżeli dół > góra wynik = -1 koniec środek = (dół + góra) div 2 użyj środek do testów, aby zmniejszyć przedział [dół... góra] jeżeli p znaleziono podczas testu wynik = pozycja p w tablicy koniec 27
Ostatnie 4 wiersze wiążą się z porównaniem p i T[środek] oraz podjęciem odpowiednich działań w celu zachowania niezmiennika. Piszemy: jeżeli T[środek] < p: przypadek A jeżeli T[środek] == p: przypadek B jeżeli T[środek] > p: przypadek C Przypadek B oznacza, że znaleziono element: przypisujemy wynik = środek i kończymy program. Analiza przypadku A: Jeżeli T[środek] < p, to T[1] T[2]... T[środek] < p. Innymi słowy, p nie może znajdować się w przedziale T[1... środek]. Zatem, jeżeli p znajduje się w tablicy T, to znajduje się w przedziale [środek +1... góra], co zapisujemy: musi_być(środek +1, góra). Przywracamy zatem prawdziwość niezmiennika musi_być(dół, góra) poprzez podstawienie: dół = środek + 1. 28
Analogicznie, w przypadku C przywracamy niezmiennik podstawiając góra = środek 1. dół = 1 góra = n pętla // niezmiennik: musi_być(dół, góra) jeżeli dół > góra wynik = -1 koniec środek = (dół + góra) div 2 jeżeli T[środek] < p: dół = środek + 1 jeżeli T[środek] == p: wynik = środek; koniec jeżeli T[środek] > p: góra = środek -1 29
Sortowanie Wejście: tablica T zawierająca n elementów (a 1, a 2,..., a n ) typu porządkowego. Wyjście: tablica o tych samych elementach, ale uporządkowana niemalejąco. metoda przez wstawianie Przypomnienie: Algorytm polega na usuwaniu pewnego elementu z danych wejściowych i wstawianiu go na odpowiednie miejsce w wynikach. Wybór następnego elementu z danych jest dowolny. Szybkość tego algorytmu zależy od struktury danych wyjściowych i implementacji operacji wstawiania. Złożoność obliczeniowa: O(n 2 ) 30
Schemat działania algorytmu: 1. Utwórz zbiór elementów posortowanych i przenieś do niego dowolny element ze zbioru nieposortowanego. 2. Weź dowolny element ze zbioru nieposortowanego. 3. Wyciągnięty element porównuj z kolejnymi elementami zbioru posortowanego póki nie napotkasz elementu równego lub elementu mniejszego, lub nie znajdziesz się na początku zbioru uporządkowanego. 4. Wyciągnięty element wstaw w miejsce gdzie skończyłeś porównywać. 5. Jeśli zbiór elementów nieuporządkowanych jest niepusty, wróć do punktu 2. pętla od i = 2 do n // niezmiennik: fragment T[1... i-1] jest posortowany // cel: przesunąć element T[i] w dół na właściwe miejsce pętla od j = i do 2 jeżeli T[j] < T[j-1] zamień T[j] z T[j-1] 31
sortowanie stogowe (przez kopcowanie) Kopiec (stóg, sterta), ang. heap: struktura danych reprezentacja zbioru elementów (np. liczb), mająca postać tzw. drzewa binarnego. Zastosowania: sortowanie przez kopcowanie porządkuje n-elementową tablicę w czasie Θ(n log n), kolejki priorytetowe: określanie operacji w zbiorze, służących do dodawania nowego elementu oraz usuwania elementu najmniejszego w czasie O(log n). Przykład kopca: 32
Własności kopca: 1. Uporządkowanie. Wartość każdego wierzchołka (ojca) jest nie większa niż wartości jego synów. Wniosek: najmniejszy element zbioru znajduje się w korzeniu drzewa. Nic jednak nie wiemy o wzajemnym uporządkowaniu lewego i prawego syna. 2. Kształt Synowie znajdują się na jednym lub więcej poziomach, a te na najniższym poziomie (liście) są przesunięte jak najbardziej w lewo. Wniosek: jeżeli drzewo zawiera n wierzchołków, to żaden z nich nie jest bardziej oddalony od korzenia niż o (log n) węzłów. Własności 1 i 2 są warunkami na tyle silnymi, żeby umożliwić szybkie odnalezienie elementu najmniejszego w zbiorze a jednocześnie umożliwiają szybką reorganizację struktury kopca po dodaniu lub usunięciu z niego elementu. 33
Realizacja kopca za pomocą tablicy: korzeń = 1 wartość(i) = x[i] lewysyn(i) = 2*i prawysyn(i) = 2*i+1 ojciec(i) = i div 2 Tablica x={12,20,15,29,23,17,22,35,40,26,51,19} Uwaga: w C/C++ tablice indeksujemy od zera a nie od jedynki! Ściśle: Tablica x[1...n] jest kopcem, jeżeli 2 i n x[i div 2] x[i]. Mówimy, że zachodzi kopiec(1,n). Fragment tablicy x[d...g] jest kopcem (czyli zachodzi kopiec(d,g)), jeżeli 2d i g x[i div 2] x[i]. 34
Procedury porządkowania kopca 1. Załóżmy, że x[1...n-1] jest kopcem i dodajmy nowy element x[n]. Prawdopodobnie x[1...n] nie jest już kopcem. Procedura przywracania własności kopca dla tablicy x[1...n]: Procedura dogóry(n): Przemieszczamy nowy element w górę drzewa tak daleko, jak powinien dotrzeć, zamieniając go po drodze z ojcem. Kończymy, gdy przemieszczany element stanie się większy lub równy ojcu. Uwaga: droga w górę drzewa to malejące indeksy w tablicy. Koszt operacji: O(log n). 35
2. Jeżeli w kopcu x[1...n] na pozycji x[1] przypiszemy nową wartość, to warunek kopiec(2,n) pozostanie prawdziwy. Procedura przywracania własności kopiec(1,n): Procedura nadół(n): Przemieszczamy element x[1] w dół drzewa (indeksy rosną!), zamieniając go po drodze z mniejszym synem, aż do chwili kiedy nie ma on już żadnych synów albo jest od nich mniejszy lub równy. Koszt operacji: O(log n). 36
Kolejki priorytetowe Kolejka umożliwia operację dodania i usunięcia elementu z pewnej ich sekwencji, w naszym przypadku struktury kopca. Początkowo kolejkę stanowi pusty zbiór S. Procedura wstaw(t) wstawia do kolejki nowy element t: wstaw(t): S = S {t} n++ dogóry(n) 37
Procedura usunmin() usuwa najmniejszy element zbioru: usunmin(): S = S \ {t} i t = min(s) S[1] = S[n] n-- nadół(n) Ostateczna postać algorytmu sortowania przez kopcowanie: pętla od i=1 do n wstaw(x[i]) // Utworzenie kopca pętla od i=1 do n usunmin() // Zdejmowanie z kopca el. min. Pesymistyczny i średni koszt operacji: Θ(n log n). 38
sortowanie szybkie Wykorzystuje metodę dziel i zwyciężaj (rekurencyjna) Złożoność obliczeniowa: O(n log n) Aby posortować tablicę, dzielimy ją na dwie części ze względu na wybrany element tablicy tak, żeby wszystkie elementy mniejsze od tego wybranego znalazły się po lewej stronie a większe po prawej. Następnie sortujemy rekurencyjnie każdą z części. Rekurencja kończy się, gdy przedział ma mniej niż 2 elementy. 39
Szkielet kodu: qsort(dół, góra): jeżeli dół góra // nie więcej niż jeden element nie rób nic koniec // cel: podzielić tablicę ze względu na pewną wartość, // która jest na koniec wstawiana na właściwe miejsce oznaczone środek. qsort(dół, środek-1) qsort(środek+1, góra) 40
Sortowanie za pomocą porównań Porządek wyjściowy jest wyznaczany jedynie na podstawie wyników porównań między elementami. sortowanie przez wstawianie sortowanie przez scalanie sortowanie przez kopcowanie sortowanie szybkie (quicksort) Dolne ograniczenie sortowania za pomocą porównań Twierdzenie: Dolne ograniczenie sortowania n elementów za pomocą porównań, wynosi Ω(n log n). Wniosek: Sortowanie przez kopcowanie, scalanie oraz quicksort są asymptotycznie optymalnymi algorytmami sortującymi za pomocą porównań. 41
Sortowanie przez zliczanie Założenie: każdy z n sortowanych elementów jest liczbą całkowitą z przedziału od 1 do k dla pewnego ustalonego k. Idea: dla każdego elementu wejściowego x należy wyznaczyć ile elementów jest mniejszych od x. Znając te liczbę, znamy jednocześnie dokładną pozycję liczby x w ciągu posortowanym. Przykład: jeżeli od x jest mniejszych 17 elementów, to x powinien się znaleźć na miejscu 18 w ciągu posortowanym (przy założeniu, że elementy nie mogą się powtarzać). 42
Implementacja Tablica A elementy wejściowe; B elementy wyjściowe (posortowane); C dane pomocnicze Counting-Sort(A, B, k): pętla od i=1 do k C[i]=0 pętla od j=1 do n C[A[j]] = C[A[j]]+1 // C[i] zawiera teraz liczbę elementów równych i pętla od i=2 do k C[i] = C[i] + C[i-1] // C[i] zawiera liczbę elementów mniejszych lub równych i pętla od i=n do 1 B[C[A[i]]] = A[i] C[A[i]] = C[A[i]]-1 Pętla 1: inicjalizacja. Pętla 2: zliczenie elementów równych indeksowi tablicy. Pętla 3: zliczenie elementów mniejszych lub równych indeksowi tablicy. Pętla 4: zapisanie wyników do wyjściowej tablicy; zmniejszenie zawartości tablicy C w celu uniknięcia konfliktu przy powtarzających się liczbach. 43
Ilustracja działania procedury a) stan po wykonaniu drugiej pętli, b) stan po wykonaniu 3 pętli, c) d) e) stan po wykonaniu odpowiednio 1, 2, 3 iteracji w czwartej pętli, f) ostateczna zawartość tablicy wyjściowej. 44
Czas działania W algorytmie nie występują porównania elementów, zatem nie ma tu zastosowania twierdzenie dotyczące dolnego ograniczenia dla metod sortowania przez porównanie. Pierwsza pętla: Druga pętla: Trzecia pętla: Czwarta pętla: k wykonań, n wykonań, k wykonań, n wykonań, Razem liczba operacji: O(n+k). W praktyce najczęściej k=o(n), zatem czas działania procedury wynosi w takim przypadku O(n) mniej niż czas Ω(n log n). Algorytm jest stabilny (nie zmienia kolejności takich samych liczb w tablicy wynikowej). 45
Sortowanie pozycyjne polega na sortowaniu liczby według najmniej znaczącej cyfry, proces jest powtarzany dla wszystkich cyfr 329 720 720 329 457 355 329 355 657 436 436 436 839 457 839 457 436 657 355 657 720 329 457 720 355 839 657 839 dla pewnych długości elementów do posortowania oraz ich liczby, algorytm działa w czasie liniowym (oczywiście pod warunkiem odpowiedniego wyboru algorytmu sortującego wg kolejnej cyfry najczęściej stosuje się zliczanie) 46
Sortowanie kubełkowe polega na utworzeniu kubełków pojemników, w których są przechowywane liczby przeznaczone do posortowania kubełki tworzymy poprzez podzielenie przedziału, do jakiego należą sortowane liczby na szereg podprzedziałów liczby wrzucamy do odpowiedniego kubełka, sortujemy w każdym z nich i wypisujemy od kubełka pierwszego do ostatniego a) tablica do posortowania b) 10 kubełków i liczby, które do nich należą; liczby (po posortowaniu np. przez wstawianie) są wyświetlane od kubełka o najniższym numerze (B[0]) do tego o najwyższym. Operacja ta wykonywana jest w czasie liniowym O(n). 47