Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Wszelkie treści i zasoby edukacyjne publikowane na łamach Portalu www.szkolnictwo.pl mogą być wykorzystywane przez jego Użytkowników wyłącznie w zakresie.

Podobne prezentacje


Prezentacja na temat: "Wszelkie treści i zasoby edukacyjne publikowane na łamach Portalu www.szkolnictwo.pl mogą być wykorzystywane przez jego Użytkowników wyłącznie w zakresie."— Zapis prezentacji:

1 Wszelkie treści i zasoby edukacyjne publikowane na łamach Portalu mogą być wykorzystywane przez jego Użytkowników wyłącznie w zakresie własnego użytku osobistego oraz do użytku w szkołach podczas zajęć dydaktycznych. Kopiowanie, wprowadzanie zmian, przesyłanie, publiczne odtwarzanie i wszelkie wykorzystywanie tych treści do celów komercyjnych jest niedozwolone. Plik można dowolnie modernizować na potrzeby własne oraz do wykorzystania w szkołach podczas zajęć dydaktycznych.

2 Algorytmy sortujące

3 Podstawowa wersja algorytmu Quicksort została wynaleziona w latach 60-tych przez angielskiego informatyka, profesora Tony'ego Hoare'a. Algorytm jest popularny, ponieważ nietrudno go zaimplementować, działa sprawnie na danych różnego typu i często zużywa mniej zasobów pamięciowych niż jakikolwiek inny algorytm. Sir Charles Antony Richard Hoare (Tony Hoare, ur. 11 stycznia 1934 w Kolombo, Sri Lanka) Algorytm sortowania szybkiego działa na zasadzie dziel i zwyciężaj (ang. divide and conquer). Zasadę tą możemy opisać w następujący sposób: 1.dziel - problem główny zostaje podzielony na podproblemy 2.zwyciężaj - znajdujemy rozwiązanie podproblemów 3.połącz - rozwiązania podproblemów zostają połączone w rozwiązanie problemu głównego Działanie algorytmu opiera sie na dzieleniu tablicy na dwie części, które następnie sortowane są niezależnie. Warto zaznaczyć, iż początkowy porządek elementów w wejściowym zbiorze danych ma wpływ na to, jak będzie przebiegał podział. Należy wiedzieć, że to właśnie proces podziału jest sednem metody.

4 W przypadku typowym, algorytm sortowania szybkiego jest najszybszym algorytmem sortującym z klasy złożoności obliczeniowej O(n log n) - stąd pochodzi jego popularność w zastosowaniach. Musimy jednak pamiętać, iż w pewnych sytuacjach (zależnych od sposobu wyboru piwotu oraz niekorzystnego ułożenia danych wejściowych) klasa złożoności obliczeniowej tego algorytmu może się degradować do O(n 2 ) Do tego, poziom wywołań rekurencyjnych może spowodować przepełnienie stosu i zablokowanie komputera. Z tych powodów algorytmu sortowania szybkiego nie można stosować bezmyślnie w każdej sytuacji tylko dlatego, iż jest uważany za jeden z najszybszych algorytmów sortujących - zawsze należy przeprowadzić analizę możliwych danych wejściowych właśnie pod kątem przypadku niekorzystnego - czasem lepszym rozwiązaniem może być zastosowanie algorytmu sortowania przez kopcowanie, który nigdy nie degraduje się do klasy O(n 2 ).

5 Potem sortujemy rekurencyjnie tym samym algorytmem lewą i prawą cześć tablicy. Połączenie tych dwóch partycji w jeden zbiór daje w wyniku zbiór posortowany. Ogólna strategia podziału: 1.wybieramy element rozgraniczający - piwot (wyznaczający podział), ten który trafi na właściwe miejsce 2.przeglądamy tablice od jej lewego końca, aż znajdziemy element większy niż rozgraniczający 3.przeglądamy tablice od jej prawego końca, aż znajdziemy element mniejszy niż rozgraniczający 4.oba elementy, które zatrzymują przeglądanie tablicy, są na pewno nie na swoich miejscach w tablicy, wiec je zamieniamy ze sobą Algorytm sortowania szybkiego możemy opisać następująco: najpierw sortowany zbiór dzielimy na dwie części w taki sposób, aby wszystkie elementy leżące w pierwszej części były mniejsze lub równe od wszystkich elementów drugiej części zbioru.

6 Do utworzenia partycji musimy ze zbioru element, nazwany piwotem. Wszystkie elementy mniejsze od piwotu będą leżały w lewej partycji, natomiast elementy większe od piwotu w prawej partycji. Miejsce, w którym znajdą się elementy równe, nie ma wpływu na proces sortowania, dlatego mogą one występować w obu partycjach. Również porządek elementów w każdej z partycji nie jest ustalony. Jako piwot można wybierać element pierwszy, środkowy, ostatni, medianę lub losowy. Dla naszych potrzeb wybierzemy element środkowy: piwot d[(lewy + prawy) div 2] piwot - element podziałowy d[ ] - dzielony zbiór lewy - indeks pierwszego elementu prawy - indeks ostatniego elementu

7 Aby podzielić zbiór na dwie partycje umieszczamy na początku zbioru wskaźniki - i oraz j. Wskaźnik i przechodząc przez zbiór wyszukuje mniejsze wartości od piwotu. W monecie kiedy taka wartość jest odnaleziona, zostaje wymieniona z elementem na pozycji j. Po wykonaniu tej operacji wskaźnik j zostanie przesunięty na kolejną pozycję w zbiorze. Wskaźnik j zapamiętuje pozycję, na podziału na partycje, będzie miejscem, gdzie znajdzie się piwot. W trakcie podziału piwot jest bezpiecznie przechowywany na ostatniej pozycji w zbiorze, nie bierze udziału w przeszukiwaniu zbioru. Wewnętrzna pętla sortowania szybkiego zwiększa wskaźnik i porównuje element tablicy z ustaloną wartością. To właśnie ta prostota sprawia, że Quicksort jest tak szybki - trudno o prostszą pętlę wewnętrzną algorytmu sortowania. wartości mniejsze lub równe od piwotuwartości większe lub równe od piwotuPIWOT partycja lewa ji partycja prawaelment rozgraniczający

8 Lp. OperacjaOpis Dzielimy zbiór na partycje, wyznaczamy na piwot element środkowy Piwot wymieniamy z ostatnim elementem 3 j Umieszczamy na początku zbioru wskaźniki. Wskaźnik i będzie przeglądał zbiór do przedostatniej pozycji. Wskaźnik j zapamiętuje miejsce wstawiania elementów mniejszych od piwotu i 4 j Wskaźnikiem i szukamy elementu mniejszego od piwotu i 5 j Element, który znaleźliśmy wymieniamy z elementem na pozycji j. Następnie wskaźnik j przesuwamy o jedną pozycję i 6 j Szukamy kolejnych elementów i Jako przykład posortujmy zbiór [ ]

9 Lp. OperacjaOpis 7 j Element, który znaleźliśmy wymieniamy z elementem na pozycji j. Następnie wskaźnik j przesuwamy o jedną pozycję i 8 j Szukamy kolejnych elementów i 9 j Element wymieniamy z elementem na pozycji j. Następnie wskaźnik j przesuwamy o jedną pozycję i 10 j Szukamy kolejnych elementów i 11 j Element wymieniamy z elementem na pozycji j. Następnie wskaźnik j przesuwamy o jedną pozycję i 12 j Szukamy kolejnych elementów i 13 j Element wymieniamy z elementem na pozycji j. Następnie wskaźnik j przesuwamy o jedną pozycję i

10 Lp. OperacjaOpis 14 j Szukamy kolejnych elementów i 15 j Element, który znaleźliśmy wymieniamy z elementem na pozycji j. Następnie wskaźnik j przesuwamy o jedną pozycję i 16 j Szukamy kolejnych elementów i 17 j Element wymieniamy z elementem na pozycji j. Następnie wskaźnik j przesuwamy o jedną pozycję i 18 j Szukamy kolejnych elementów i 19 j Element wymieniamy z elementem na pozycji j. Następnie wskaźnik j przesuwamy o jedną pozycję i 20 lewa parytcja j Brak dalszych elementów do wymiany. Piwot wymieniamy z elementem na pozycji j-tej. Podział na partycje zakończony prawa partycja i

11 W momencie, kiedy kończymy podział danego zbioru elementów na partycję, wskaźnik j wyznacza pozycję dla piwotu. Tak, jak już wcześniej wspomnieliśmy - lewa partycja (część podzielonego zbioru) zawiera elementy mniejsze od piwotu i zaczyna się na początku zbioru, a kończy na pozycji j - 1. Partycja prawa, zatem zawiera elementy, które są większe lub równe piwotowi. Zbiór ten zaczyna się od pozycji j + 1, natomiast kończy się na końcu zbioru. Operacja podziału zbioru elementów na partycję posiada klasę złożoności obliczeniowej O(n), jest to klasa liniowa.

12 Dane wejściowe d[ ] - Zbiór zawierający elementy do posortowania. Zakres indeksów elementów jest dowolny. lewy - indeks pierwszego elementu w zbiorze, lewy C prawy - indeks ostatniego elementu w zbiorze, prawy C Dane wyjściowe d[ ] - zbiór zawierający elementy posortowane rosnąco Zmienne pomocnicze piwot - element podziałowy i, j - indeksy, i, j C

13 K01: K02: piwot d[i]; d[i] d[prawy]; j lewy K03: Dla i = lewy, lewy + 1,..., prawy - 1: wykonuj K04...K05 K04: Jeśli d[i] piwot, to wykonaj kolejny obieg pętli K03 K05: d[i] d[j]; j j + 1 K06: d[prawy] d[j]; d[j] piwot K07: Jeśli lewy < j - 1, to Sortuj_szybko(lewy, j - 1) K08: Jeśli j + 1 < prawy, to Sortuj_szybko(j + 1, prawy) K09: Zakończ

14 W pierwszym kroku wybieramy element podziałowy, w naszym przypadku będzie to element leżący w środku dzielonego zbioru. Obliczamy jego pozycję i zapamiętujemy ją tymczasowo w zmiennej i. Robimy to po to, aby uniknąć tych samych rachunków. Element d[i] zapamiętujemy w zmiennej piwot, natomiast do d[i] przypisujemy ostatni element zbioru. Dzięki temu piwot został usunięty ze zbioru. Zmienną j ustawiamy na początku partycji.

15 Przy wykonywaniu pętli sterowanej zmienną j przeglądamy wszystkie elementy zbioru, od pierwszego do przedostatniego – ostatni element jest piwotem i jest wykluczony z przeglądania. Zasada działania: jeśli i-ty element jest mniejszy od piwotu, to zostaje przeniesiony na początek parytcji – zostają wymienione ze sobą elementy znajdujące się na pozycjach i-tej i j- ej. Po tej zamianie następuje przesunięcie punktu podziałowego partycji j. Po zakończeniu wykonywania pętli, element z pozycji i-tej zostaje przeniesiony na koniec partycji, a na jego miejsce zostaje przeniesiony piwot.

16 Zbiór wejściowy został zatem podzielony na dwie części: partycja lewa od pozycji lewy do j - 1 zawiera elementy mniejsze od piwotu partycja prawa od pozycji j + 1 do pozycji prawy zawiera elementy większe lub równe elementowi podziałowemu. Sprawdzamy, czy partycje te obejmują więcej niż jeden element. Jeśli tak, to wywołujemy rekurencyjnie algorytm sortowania szybkiego przekazując mu granice wyznaczonych partycji. Po powrocie z wywołań rekurencyjnych partycja wyjściowa jest posortowana rosnąco. Kończymy algorytm.

17 Od tego zależy, czy podziały dokonywane w algorytmie są zrównoważone, czy tez nie, a to z kolei zależy od wybranego klucza podziału. W przypadku, gdy podziały są zrównoważone, algorytm jest równie szybki jak np. sortowanie przez scalanie. Gdy natomiast podziały są niezrównoważone, sortowanie może przebiegać asymptotycznie tak wolno, jak sortowanie przez wstawianie. Złożoność algorytmu Quicksort możemy rozpatrywać analizując trzy możliwości: przypadek optymistyczny - gdy za każdym razem jako klucz podziału wybrana zostaje mediana z sortowanego aktualnie fragmentu tablicy, czyli gdy każdy podział daje równe podzbiory danych. Wówczas liczba porównań niezbędnych do uporządkowania n-elementowgo fragmentu jest rzędu: T(n) = O(n log n) Zarówno czas działania algorytmu sortowania szybkiego jak również zapotrzebowanie na pamięć są uzależnione od postaci tablicy wejściowej.

18 przypadek pesymistyczny – występuje gdy dane wejściowe sa juz posortowane, lub gdy sa uporządkowane w odwrotnej kolejności. W przypadku pesymistycznym, objawiającym się każdorazowym wyborem elementu najmniejszego, bądź największego w sortowanym fragmencie tablicy, złomność obliczeniowa przyjmuje postać: przypadek przeciętny - średni czas działania algorytmu jest bliski najlepszemu przypadkowi. Quicksort. W wielu sytuacjach podziały przeciętne wypadają w połowie. Dla tak równomiernego rozkładu prawdopodobieństwa wyboru elementu, względem którego dokonujemy podziału, algorytm sortowania szybkiego wymaga czasu działania: T(n) = O(2n ln n)

19 Zalety: działa w miejscu, czyli in situ (używa tylko niewielkiego stosu pomocniczego) do posortowania n elementów wymaga średnio czasu proporcjonalnego do n logn ma wyjątkowo skromna pętlę wewnętrzną Wady: jest niestabilny zabiera około n 2 operacji w najgorszym przypadku jest wrażliwy (prosty niezauważony błąd w implementacji może powodować niewłaściwe działanie w przypadku niektórych danych)

20 Sysło M.: Algorytmy, WSiP, Warszawa 1997 Drozdek A.: Struktury danych w języku C, Helion, 2004 Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein: Wprowadzenie do algorytmów, Wydawnictwo Naukowo-Techniczne, wyd. VI


Pobierz ppt "Wszelkie treści i zasoby edukacyjne publikowane na łamach Portalu www.szkolnictwo.pl mogą być wykorzystywane przez jego Użytkowników wyłącznie w zakresie."

Podobne prezentacje


Reklamy Google