Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
OpublikowałWiktoria Wrochna Został zmieniony 11 lat temu
1
Materiały pochodzą z Platformy Edukacyjnej Portalu www.szkolnictwo.pl
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
Sortowanie przez scalanie Sortowanie przez zliczanie
Algorytmy sortujące Sortowanie przez scalanie Sortowanie przez zliczanie
3
Sortowanie przez scalanie (merge sort)
Scalanie ciągów (łączenie) jest operacją połączenia dwóch uporządkowanych rosnąco (malejąco) ciągów elementów w jeden ciąg wynikowy, również uporządkowany rosnąco (malejąco). W informatyce sortowanie przez scalanie (ang. merge sort), to rekurencyjny algorytm sortowania danych. Zaliczany jest do algorytmów szybkich tzn. takich, które posiadają klasę czasowej złożoności obliczeniowej równą O(n log n) lub nawet lepszą. Odkrycie algorytmu przypisuje się Johnowi von Neumannowi - matematykowi, inżynierowi chemii, fizykowi i informatyk. Wniósł on znaczący wkład do wielu dziedzin matematyki, był głównym twórcą teorii gier, teorii automatów, i stworzył formalizm matematyczny mechaniki kwantowej. John von Neumann ( )
4
Sortowanie przez scalanie
Najlepszy opis sortowania przez scalanie opiera się na rekurencji i ilustruje równocześnie bardzo korzystne zastosowanie techniki „dziel i zwyciężaj”. Dziel Dzielimy n-elementowy ciąg na dwa podciągi po n=2 elementów każdy Zwyciężaj Sortujemy otrzymane podciągi, używając rekurencyjnie sortowania przez scalanie Scalaj Łączymy posortowane podciągi w jeden posortowany ciąg. Każdy ciąg o długości 1 jest posortowany
5
Algorytm sortowania przez scalanie - przykład
21 5 7 6 2 4 9 21 5 7 6 2 4 9 5 2 4 9 5 21 5 7 6 21 5 7 6 2 4 9 5 5 21 6 7 2 4 5 9 5 6 7 21 2 4 5 9 2 4 5 5 6 7 9
6
Przykład - rekurencyjne obliczanie silni
Dla przykładu zostanie przedstawiony schemat rekurencyjnego obliczania silni. Silnią (n!) liczby naturalnej n nazywamy iloczyn wszystkich liczb naturalnych nie większych niż n. np. 4!=1*2*3*4=24 Do zapisu algorytmu potrzebujemy: Dane wejściowe n - liczba, której silnie liczymy na danym poziomie rekurencyjnym, n N Dane wyjściowe n! – wartość silni Lista kroków K01: jeśli n>2, silnia(n) ← 1 i Koniec K02: silnia(n) ← 1* silnia(n-1) i Koniec Sprawdzamy warunek zakończenia rekurencji – czyli czy wynik dla otrzymanych danych wejściowych jest oczywisty. Przypadek, w którym silnia przyjmie wartość 1 wystąpi dla n<2. Jeżeli powyższy warunek nie wystąpi, do obliczeń wykorzystujemy rekurencyjne wywołanie obliczania silni dla argumentu zmniejszonego o 1. Wynik tego wywołania mnożymy przez n i zwracamy jako wartość silni dla n.
7
Scalanie zbiorów uporządkowanych
Podstawową operacją algorytmu jest scalanie dwóch zbiorów uporządkowanych w jeden zbiór również uporządkowany. Operację scalania realizujemy wykorzystując pomocniczy zbiór, w którym będziemy tymczasowo odkładać scalane elementy dwóch zbiorów. Zasada scalania zbiorów: Przygotowanie pustego, tymczasowego zbioru Porównujemy pierwsze elementy zbiorów i w zbiorze tymczasowym umieszczamy mniejszy z elementów, usuwając go ze scalanego zbioru. Czynność powtarzamy do momentu opróżnienia jednego ze zbiorów. W tymczasowym zbiorze umieszczamy zawartość tego scalanego zbioru, który zawiera jeszcze elementy Do zbioru wynikowego wpisujemy zawartość zbioru tymczasowego. Koniec algorytmu
8
Scalanie zbiorów uporządkowanych - przykład
Scalmy dwa uporządkowane zbiory [1 4 7 ] i [2 3 8] 1 7 2 3 8 Zbiór tymczasowy Pierwszym krokiem jest porównanie ze sobą najmniejszych elementów ze scalanych zbiorów. W zbiorze tymczasowym umieszczamy najmniejszy element, usuwając go ze zbioru sortowanego. W naszym przypadku będzie to liczba 1. Porównujemy kolejne elementy zbiorów. Za każdym razem najmniejszy element zostaje przeniesiony do zbioru tymczasowego. 4 7 4 1 2 3 8 7 4 1 2 3 8
9
Scalanie zbiorów uporządkowanych – przykład cd.
Zbiór tymczasowy 7 4 1 2 3 Poszczególne kroki porównywania najmniejszych elementów zbiorów scalanych powtarzamy do momentu gdy jeden ze zbiorów zostanie pusty. Zakończyliśmy proces porównywania poszczególnych elementów. Do zbioru tymczasowego wprowadzamy pozostałe elementy z drugiego zbioru. Uzyskaliśmy zbiór uporządkowany, którego zawartość możemy przepisać do zbioru docelowego. 8 7 1 2 3 4 8 8 1 2 3 4 7 1 2 3 4 7 8
10
Algorytm scalania Lista kroków algorytmu scalania Dane wejściowe
d[ ] - scalany zbiór ip - indeks pierwszego elementu w młodszym podzbiorze, ip N is - indeks pierwszego elementu w starszym podzbiorze, is N ik - indeks ostatniego elementu w starszym podzbiorze, ik N Dane wyjściowe d[ ] - scalony zbiór Zmienne pomocnicze p[ ] - zbiór pomocniczy, który zawiera tyle samo elementów, co zbiór d[ ] i1 - indeks elementów w młodszej połówce zbioru d[ ], i1 N i2 - indeks elementów w starszej połówce zbioru d[ ], i2 N i - indeks elementów w zbiorze pomocniczym p[ ], i N Lista kroków algorytmu scalania K01: i1 ← ip; i2 ← is; i ← ip K02: Dla i = ip, ip + 1, ..., ik: wykonuj jeśli (i1 = is) ∨ (i2 ≤ ik i d[i1] > d[i2]), to p[i] ← d[i2]; i2 ← i2 + 1 inaczej p[i] ← d[i1]; i1 ← i1 + 1 K03: Dla i = ip, ip + 1,...,ik: d[i] ← p[i] K04: Koniec
11
Schemat blokowy algorytmu scalania
Aby przeprowadzić operację scalania dwóch zbiorów musimy zarezerwować określony obszar pamięci – w przypadku prezentowanego przykładu będzie to tablica p o rozmiarze równym rozmiarowi zbioru d[ ]. W tablicy p algorytm będzie zapisywał zbiór tymczasowy, który po scalaniu zostanie przeniesiony do zbioru d[ ], zastępując dwa scalane podzbiory. Na wejściu do algorytmu podane są parametry ip, is oraz ik , które dokładnie określają scalanych położenie podzbiorów w tablicy d[ ]. Poszczególne elementy w podzbiorach zostały oznaczone w następujący sposób: i1 - młodszy podzbiór od pozycji ip do is - 1 oraz i2 - starszy podzbiór od pozycji is do ik. Na początku algorytmu przypisujemy tym zmiennym indeksy pierwszych elementów w każdym podzbiorze. Indeksy elementów wstawianych do tablicy p[ ] są zawarte w zmiennej i . Wewnątrz pętli sprawdzamy, czy indeksy i1 i i2 wskazują elementy podzbiorów. Jeśli któryś z nich wyszedł poza dopuszczalny zakres, to dany podzbiór jest wyczerpany - w takim przypadku do tablicy p przepisujemy elementy drugiego podzbioru. Elementy podzbiorów porównujemy tak długo dopóki jeden z nich nie będzie pusty.
12
Schemat blokowy algorytmu scalania
Do tablicy p[ ] zawsze wpisujemy mniejszy z porównywanych elementów. Dzięki temu elementy są uporządkowane w tworzonym zbiorze wynikowym. Po zapisie elementu w tablicy p[ ], odpowiedni indeks i1 lub i2 jest zwiększany o 1. Rośnie także indeks i, aby kolejny zapisywany element w tablicy p[ ] trafił na następne wolne miejsce. Pętla jest kontynuowana aż do zapełnienia w tablicy p[ ] obszaru o indeksach od ip do ik. W końcowej pętli, zostaje przepisany ten obszar z tablicy p[ ] do tablicy wynikowej d[ ]. Scalane zbiory zostają zapisane zbiorem wynikowym, który jest posortowany rosnąco.
13
Algorytm sortujący Lista kroków algorytmu sortującego Dane wejściowe
d[ ] - sortowany zbiór ip - indeks pierwszego elementu w młodszym podzbiorze, ip N ik - indeks ostatniego elementu w starszym podzbiorze, ik N Dane wyjściowe d[ ] - posortowany zbiór Zmienne pomocnicze is - indeks pierwszego elementu w starszym podzbiorze, is N Lista kroków algorytmu sortującego K01: is ← (ip + ik + 1) div 2 K02: Jeśli is - ip > 1, to podzbiór(ip, is - 1) K03: Jeśli ik - is > 0, to podzbiór(is, ik) K04: Scalaj(ip, is, ik) K05: Koniec
14
Schemat blokowy algorytmu sortującego
START Algorytm sortowania przez scalanie, należy do grupy algorytmów rekurencyjnych. Wywołuje się go z określonymi wartościami parametrów ip oraz ik Podczas pierwszego wywołania funkcji indeksy powinny objąć cały zbiór d - zatem ip=1, natomiast ik=n. Aby podzielić sortowany zbiór na dwie części wyznacza indeks is - dla młodszej połowy elementy od ip do is-1, dla starszej o indeksach elementów od is do ik . Kolejnym korkiem jest sprawdzenie czy w danej części zbioru jest więcej niż jeden element. Jeśli tak, to rekurencyjnie sortujemy ją tym samym algorytmem. Kiedy oba podzbiory zostaną posortowane scalamy je według schematu opisanego w poprzednim punkcie. Algorytm zostaje zakończony. Zbiór jest posortowany. Is-ip>1 TAK podzbiór (ip , is – 1) Ik-is>0 TAK podzbiór (is ,ik– 1) Scalaj (ip, is ,ik) KONIEC
15
Sortowanie przez zliczanie (counting sort)
Kolejny algorytm, którego zasadę działania poznamy to sortowanie przez zliczanie. W metodzie tej stosowane są pewne założenia wobec sortowanego zbioru. Algorytm posiada liniową klasę złożoności obliczeniowej O(n+k) (n – oznacza liczebność zbioru, k – rozpiętość danych, czyli w przypadku liczb: powiększoną o 1 różnicę między maksymalną, a minimalną wartością, np. rozpiętość liczb w Dużym Lotku wynosi = 49). Sortowanie przez zliczanie, oprócz tego że jest szybki, ma swoje dwie poważne wady. Po pierwsze - do tego sortowania potrzebna jest dodatkowa pamięć n+k (czyli nie jest to sortowanie w miejscu), a po drugie - tym sposobem można sortować tylko liczby całkowite z ograniczonego zakresu. Zasada działania algorytmu - w tablicy A mamy zapisane wszystkie liczby do posortowania. W tablicy B będziemy zapisywać ile razy występują elementy z tablicy A. Na początku tablicę B wypełniamy zerami. Sprawdzamy kolejne pola w tablicy A. Jeśli element numer i jest równy np. 10, to powiększamy o 1 dziesiąty element tablicy B. Ogólnie idea algorytmu polega na sprawdzeniu ile wystąpień danego klucza występuje w sortowanej tablicy.
16
Przykład counting sort
Posortujmy rosnąco dany zbiór liczb: Następnie, dla każdej wartości w zbiorze dopisujemy licznik ustawiony na 0. [0:0] [1:0] [2:0] [3:0] [4:0] [5:0] [6:0] [7:0] [8:0] [9:0] Kolejnym krokiem, jest zliczanie poszczególnych elementów w zbiorze i przypisywanie ich do konkretnych wartości liczbowych, np. dla liczby 4 , każdy element w zbiorze sortowanym o tej wartości zwiększa wartość licznika o 1. W tym konkretnym przykładzie otrzymamy: [0:1] [1:3] [2:3] [3:2] [4:2] [5:1] [6:4] [7:1] [8:5] [9:1] Teraz poczynając od drugiego licznika sumujemy zawartość licznika oraz jego poprzednika i otrzymujemy: [0:1] [1:4] [2:7] [3:9] [4:11] [5:12] [6:16] [7:17] [8:22] [9:23] Dzięki tej operacji licznik podaje nam ilość wartości mniejszych lub równych konkretnej wartości ze zbioru, np.: [2:7] - w zbiorze do sortowania jest siedem wartości mniejszych lub równych 2 [6:16] - w zbiorze do sortowania jest szesnaście wartości mniejszych lub równych 6 itd. Otrzymujemy uporządkowany zbiór elementów, w którym stan licznika określa ostatnią pozycję w zbiorze danego elementu : [ ] Sortowana liczba 1 2 3 4 5 6 7 8 9 Pozycja w zbiorze 10 11 12 13 14 15 16 17 18 19 20 21 22 23
17
Przykład cd. W zasadzie proces sortowania moglibyśmy zakończyć na tym etapie, jednak jeżeli chcemy aby nasz algorytm był stabilny podczas sortowania wartości „kluczy” (czyli takich wartości, które są przypisane pewnym większym strukturom danych) musimy wykonać obieg dystrybucyjny. Obieg dystrybucyjny ma za zadanie obliczyć dystrybuantę, czyli ilość elementów mniejszych lub równych od danej wartości. W tym celu ponownie przeglądamy zbiór danych wejściowych idąc od ostatniego elementu do pierwszego – inaczej algorytm nie byłby stabilny. Po kolei, każdy element umieszczamy w zbiorze wynikowym na pozycji równej zawartości licznika dla tego elementu. Po wykonaniu tej operacji licznik zmniejszamy o 1. Na przykładzie będzie to wyglądało w następujący sposób: np. dla liczby 4 – jej licznik ma zawartość [4:11] – dlatego liczbę 4 umieszczamy w zbiorze wynikowym na pozycji 11 i zmniejszamy stan licznika o 1, w wyniku czego otrzymamy [4:10]. Kolejna liczba 4 trafi na pozycję 10, itd. Dla liczby 6 wartość licznika wynosi [6:16] – czyli 6 będzie na 16 pozycji, zmniejszamy licznik o 1 i otrzymujemy [6:15]. Analogicznie postępujemy ze wszystkimi liczbami w sortowanym zbiorze, do momentu za zbiór zostanie całkowicie uporządkowany w sposób rosnący. [ ]
18
Specyfikacja problemu
Dane wejściowe d[ ] - zbiór elementów do posortowania. Każdy element posiada pole klucz, według którego dokonuje się sortowania. Pole klucz jest liczbą całkowitą Indeksy elementów rozpoczynają się od 1 n - ilość elementów w zbiorze d[ ], n ∈ N kmin - minimalna wartość klucza, kmin ∈ C kmax - maksymalna wartość klucza, kmax ∈ C Dane wyjściowe b[ ] - zbiór z posortowanymi elementami ze zbioru d[ ] Zmienne pomocnicze i - zmienna dla pętli iteracyjnych, i ∈ C L[ ] - tablica liczników wartości kluczy. Elementy są liczbami całkowitymi. Indeksy przyjmują kolejne wartości od kmin do kmax
19
Lista kroków K01: Dla i = kmin, kmin + 1,...,kmax: wykonuj L[i] ← 0 K02: Dla i = 1,2,...,n: wykonuj L[d[i].klucz] ← L[d[i].klucz] + 1 K03: Dla i = kmin + 1, kmin + 2,...,kmax: wykonuj L[i] ← L[i] + L[i - 1] K04: Dla i = n, n - 1,...,1: wykonuj K05...K06 K05: b[ L[ d[i].klucz ] ] ← d[i] K06: L[ d[i].klucz ] ← L[ d[i].klucz ] - 1 K07: Zakończ
20
Schemat blokowy Algorytm sortowania przez zliczanie zbudowany jest z kolejno następujących po sobie pętli iteracyjnych. W pętli nr 1 tworzymy dla każdej liczby lub klucza w zbiorze licznik i ustawiamy go na 0. W pętli nr 2 następuje zliczenie kolejnych elementów zbioru, w wyniku czego zwiększamy liczniki poszczególnych kluczy o 1. Po zakończeniu tej pętli w licznikach mamy ilość wystąpień poszczególnych kluczy. W pętli numer 3 przekształcamy zliczone wartości wystąpień kluczy na ostatnie pozycje elementów z danym kluczem w zbiorze wyjściowym. W kolejnej pętli – 4, ponownie przeglądamy zbiór wejściowy i przenosimy elementy ze zbioru wejściowego do zbioru wyjściowego, umieszczając go w miejscu o numerze zawartym w liczniku dla danego klucza. Po przesłaniu licznik zmniejszamy o 1, aby kolejny element o tej samej wartości klucza trafił na poprzednią pozycję. Zbiór został posortowany rosnąco. Kończymy algorytm.
21
Podsumowanie Zarówno algorytm sortowania przez scalanie jak i algorytm sortowania przez zliczanie zaliczamy do algorytmów stabilnych czyli elementy w zbiorach sortowanych, o tych samych wartościach występują w tablicy wynikowej w takiej samej kolejności jak w tablicy wejściowej. Klasa złożoności obliczeniowej dla algorytmu sortowania przez scalanie wynosi O(n log n), natomiast algorytmu sortowania przez zliczanie O(n + k) . Obydwa algorytmy wymagają dodatkowej pamięci – dla pierwszego O(n), natomiast w drugim przypadku O(n + k). Oba algorytmy zalicza się do grupy algorytmów szybkich, z tym że sortowanie przez zliczanie nie wykonuje żadnych porównań elementów sortowanych, czyli może osiągać lepszą klasę złożoności obliczeniowej.
22
Bibliografia Sysło M.: Algorytmy, WSiP, Warszawa 1997
Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein: Wprowadzenie do algorytmów, Wydawnictwo Naukowo- Techniczne, wyd. VI 2004 Wróblewski P.: Algorytmy, struktury danych i techniki programowania, Wyd. Helion, 2003
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.