Professional Documents
Culture Documents
Algorytmy Sortujące PDF
Algorytmy Sortujące PDF
Artykuł opisuje podstawowe algorytmy sortowania danych. Algorytmy zostały zaimplementowane w kilku językach programowania (C++, Pascal, Basic,
Python i JavaScript).
Spis treści
Wstęp
Sortowanie zwariowane
Sortowanie głupie
Sortowanie bąbelkowe - wersja 1
Sortowanie bąbelkowe - wersja 2
Sortowanie bąbelkowe - wersja 3
Sortowanie bąbelkowe - wersja 4 - wersja zoptymalizowana
Sortowanie bąbelkowe - wersja 5 - dwukierunkowa
Sortowanie przez wybór
Sortowanie przez wstawianie
Binarne sortowanie przez wstawianie
Sortowanie metodą Shella
Sortowanie przez scalanie
Sortowanie przez kopcowanie - drzewo binarne
Sortowanie przez kopcowanie - tworzenie kopca
Sortowanie przez kopcowanie - rozbiór kopca
Sortowanie przez kopcowanie
Sortowanie szybkie
Sortowanie rozrzutowe
Sortowanie kubełkowe
Sortowanie kubełkowe - listy
Sortowanie kubełkowe - wersja listowa
Sortowanie przez zliczanie
Sortowanie pozycyjne
Podsumowanie
Temat:
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/index.php 1 / 209
Algorytmy Sortujące - Wstęp 2014-10-03
Wstęp
Podrozdziały
Uwagi na temat języków programowania
Porządek w zbiorze
Kryterium uporządkowania zbioru
Czasowa złożoność obliczeniowa
Badania algorytmów sortujących
Sortowanie danych jest jednym z podstawowych problemów programowania komputerów, z którym prędzej czy później spotka się każdy
programista. Poniżej przedstawiamy tylko nieliczne dziedziny, w których występuje potrzeba sortowania danych:
sport - wyniki uzyskane przez poszczególnych zawodników należy ułożyć w określonej kolejności, aby wyłonić zwycięzcę oraz podać
lokatę każdego zawodnika.
bank - spłaty kredytów należy ułożyć w odpowiedniej kolejności, aby wiadomo było kto i kiedy ma płacić odsetki do banku.
grafika - wiele algorytmów graficznych wymaga porządkowania elementów, np. ścian obiektów ze względu na odległość od obserwatora.
Uporządkowanie takie pozwala później określić, które ze ścian są zakrywane przez inne ściany dając w efekcie obraz trójwymiarowy.
bazy danych - informacja przechowywana w bazie danych może wymagać różnego rodzaju uporządkowania, np. lista książek może być
alfabetycznie porządkowana wg autorów lub tytułów, co znacznie ułatwia znalezienie określonej pozycji.
Artykuł ma na celu dokładne zapoznanie uczniów z podstawowymi algorytmami sortującymi, ich własnościami oraz umiejętnym doborem
algorytmów sortujących dla danego zadania. Wbrew obiegowym opiniom nie ma "idealnego" i "uniwersalnego" algorytmu sortującego - jeden
algorytm jest lepszy w jednej sytuacji, drugi w innej. Dlatego dokładna znajomość własności algorytmów sortujących pozwala na tworzenie
naprawdę efektywnych aplikacji.
Z problemem sortowania powiązane są problemy wyszukiwania danych. Dlatego zapraszamy naszych czytelników do zapoznania się z
artykułem o algorytmach wyszukujących.
Porządek w zbiorze
Zbiór posortowany to taki zbiór, w którym kolejne elementy są poukładane w pewnym porządku (kolejności). Porządek ten
możemy określić za pomocą relacji ≥ lub ≤ (albo dowolnej innej relacji porządkowej, która jednoznacznie wyznacza kolejność
elementów w zbiorze). Równość jest konieczna w przypadku, gdy elementy zbioru mogą przyjmować takie same wartości. Na
przykład zbiór liczb:
D = { 1, 3, 3, 5, 7, 7, 8, 9 }
jest posortowany rosnąco, ponieważ pomiędzy każdymi dwoma kolejnymi elementami zachodzi relacja:
element poprzedni jest mniejszy lub równy od elementu następnego
Odwrotnie, zbiór:
D = { 9, 8, 6, 6, 4, 2, 1, 1, 0 }
jest posortowany malejąco, ponieważ pomiędzy każdymi dwoma kolejnymi elementami zachodzi relacja:
element poprzedni jest większy lub równy od elementu następnego.
Oczywiście zbiór wcale nie musi składać się z liczb (chociaż tak naprawdę każdy rodzaj danych komputerowych w końcu
sprowadza się do liczb, gdyż wewnętrznie jest reprezentowany przez kody binarne). W takim przypadku należy określić dla
każdego elementu tzw. klucz (ang. key), wg którego dokonywane jest sortowanie. Ponieważ klucz jest liczbą, zatem obowiązują
dla niego podane wyżej zasady kolejności elementów. Na przykład dla tekstów kluczem mogą być kody poszczególnych znaków.
Większość języków programowania posiada operatory porównywania ciągu znaków (problemem może być sortowanie wg zasad
języka polskiego - nie wystarczy wtedy porównywać kodów znakowych, ponieważ kody polskich literek ą, ć, Ć itd. są zwykle
większe od kodów liter alfabetu łacińskiego, ale i ten problem daje się z powodzeniem rozwiązać przez odpowiedni dobór kluczy).
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0001.php 2 / 209
Algorytmy Sortujące - Wstęp 2014-10-03
Przez sortowanie będziemy rozumieć taką zmianę kolejności elementów zbioru nieuporządkowanego (permutację), aby w wyniku
spełniały one założony porządek.
O(n) - Algorytm o liniowej zależności czasu wykonania od ilości danych. Dwukrotny wzrost liczby
przetwarzanych danych powoduje dwukrotny wzrost czasu wykonania. Tego typu złożoność
powstaje, gdy dla każdego elementu należy wykonać stałą liczbę operacji.
O(n2) - Algorytm, w którym czas wykonania rośnie z kwadratem liczby przetwarzanych elementów.
Dwukrotny wzrost liczby danych powoduje czterokrotny wzrost czasu wykonania. Tego typu
złożoność powstaje, gdy dla każdego elementu należy wykonać ilość operacji proporcjonalną do
liczby wszystkich elementów.
O(n log n) - Dobre algorytmy sortujące mają taką właśnie złożoność obliczeniową. Czas wykonania przyrasta
dużo wolniej od wzrostu kwadratowego. Tego typu złożoność powstaje, gdy zadanie dla n
elementów można rozłożyć na dwa zadania zawierające po połowie elementów.
O(n!) - Bardzo pesymistyczne algorytmy, czas wykonania rośnie szybko ze wzrostem liczby elementów
wejściowych, czyli znalezienie rozwiązania może zająć najszybszym komputerom całe wieki lub
O(an) tysiąclecia. Takich algorytmów należy unikać jak ognia !
Zapis O( ) określamy mianem klasy złożoności obliczeniowej algorytmu. Klasa czasowej złożoności obliczeniowej umożliwia
porównywanie wydajności różnych algorytmów sortujących. Z reguły proste algorytmy posiadają wysoką złożoność obliczeniową -
długo dochodzą do wyniku końcowego. Algorytmy bardziej skomplikowane posiadają mniejszą złożoność obliczeniową - szybko
dochodzą do rozwiązania. Złożoność obliczeniowa wszystkich algorytmów sortujących została dokładnie oszacowana - my
również dokonujemy tego w niniejszym opracowaniu.
Przykład:
Załóżmy, iż mamy program sortujący dane zbudowany na bazie algorytmu sortującego o klasie złożoności
obliczeniowej O(n2). Sto elementów jest sortowane w czasie 1 sekundy. Ile czasu zajmie posortowanie za pomocą
tego programu zbioru o tysiącu elementach? Odpowiedź brzmi - 100 sekund. Ponieważ ilość danych wzrosła 10
razy, to czas obliczeń wzrósł 102, czyli 100 razy. Poniżej przedstawiamy odpowiednią tabelkę.
Widzimy więc, iż algorytm ten spisuje się dobrze tylko przy niewielkiej liczbie elementów. Gdy liczba sortowanych elementów jest
duża, czas oczekiwania na rozwiązanie może być nie do zaakceptowania. Dlatego właśnie informatycy poświęcili tak dużo
swojego wysiłku na opracowanie odpowiednio szybkich algorytmów sortujących (te najszybsze mają klasę złożoności O(n log n)
).
Oprócz złożoności czasowej rozważa się również złożoność pamięciową. Określa ona ilość zasobów komputera, których
wymaga dany algorytm w zależności od liczby danych wejściowych. Tutaj także ma zastosowanie notacja omikron. Przy
określaniu złożoności algorytmu należy wziąć pod uwagę oba typy złożoności obliczeniowej.
Ze względu na złożoność pamięciową algorytmy sortujące dzielimy na dwie podstawowe grupy:
Algorytmy sortujące w miejscu (ang. in place) - wymagają stałej liczby dodatkowych struktur danych, która nie zależy od
liczby elementów sortowanego zbioru danych (ani od ich wartości). Dodatkowa złożoność pamięciowa jest zatem klasy
O(1). Sortowanie odbywa się wewnątrz zbioru. Ma to bardzo istotne znaczenie w przypadku dużych zbiorów danych, gdyż
mogłoby się okazać, iż posortowanie ich nie jest możliwe z uwagi na brak pamięci w systemie. Większość opisanych tu
algorytmów sortuje w miejscu, co jest ich bardzo dużą zaletą.
Algorytmy nie sortujące w miejscu - wymagają zarezerwowania w pamięci dodatkowych obszarów, których wielkość jest
uzależniona od liczby sortowanych elementów lub od ich wartości. Tego typu algorytmy są zwykle bardzo szybkie w
działaniu, jednakże okupione to jest dodatkowymi wymaganiami na pamięć. Zatem w pewnych sytuacjach może się
okazać, iż taki algorytm nie będzie w stanie posortować dużego zbioru danych, ponieważ system komputerowy nie posiada
wystarczającej ilości pamięci RAM.
Algorytmy sortujące dzieli się również na dwie grupy:
Algorytmy stabilne - zachowują kolejność elementów równych. Oznacza to, iż elementy o tych samych wartościach będą
występowały w tej samej kolejności w zbiorze posortowanym, co w zbiorze nieposortowanym. Czasami ma to znaczenie,
gdy sortujemy rekordy bazy danych i nie chcemy, aby rekordy o tym samym kluczu zmieniały względem siebie położenie.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0001.php 3 / 209
Algorytmy Sortujące - Wstęp 2014-10-03
Algorytmy niestabilne - kolejność wynikowa elementów równych jest nieokreślona (zwykle nie zostaje zachowana).
tpo - czas sortowania zbioru posortowanego. Nie, to nie jest pomyłka. Pomiar tego czasu da nam odpowiedź, czy
algorytm wykorzystuje fakt posortowania zbioru.
tod - czas sortowania zbioru uporządkowanego odwrotnie. To zwykle jest ciężki orzech do zgryzienia dla
algorytmów, które w typowych warunkach radzą sobie całkiem sprawnie. Tego typu sytuacja występuje przy
zmianie kierunku uporządkowania zbioru, który wcześniej został już posortowany.
tpp - czas sortowania zbioru uporządkowanego, w którym pierwszy element przyjmuje wartość losową.
Wykonamy dziesięć sortowań dla każdego zbioru uśredniając wynik. Tego typu sytuacja występuje przy
dodawaniu nowego elementu na początku zbioru już uporządkowanego.
tpk czas posortowania zbioru uporządkowanego, w którym ostatni element przyjmuje wartość losową.
-
Wykonamy dziesięć sortowań uśredniając wynik. Tego typu sytuacja występuje przy dodawaniu nowego
elementu na końcu zbioru uporządkowanego.
tnp - czas posortowania zbioru z losowym rozkładem elementów. Wykonamy dziesięć sortowań uśredniając
wynik. Ten czas poinformuje nas, jak dany algorytm radzi sobie w typowych warunkach.
Poniżej przedstawiamy program, który będzie stosowany do badań. Zastosowane w nim rozwiązania mogą się przydać również
przy pomiarze czasu działania innych algorytmów.
Do pomiaru czasu wykorzystujemy dwie funkcje biblioteki Win32API operujące na tzw. sprzętowym liczniku wydajności. Licznik
ten zlicza takty procesora i jest 64-bitowy. Jeśli komputer nie posiada takiego licznika, to funkcje zwracają wartość logiczną false.
QueryPerformanceFrequency(adres zmiennej 64-bitowej) - funkcja umieszcza w podanej zmiennej całkowitej 64-
bitowej ilość taktów procesora na 1 sekundę.
QueryPerformanceCounter(adres zmiennej 64-bitowej) - funkcja umieszcza w podanej zmiennej całkowitej 64-bitowej
stan licznika taktów procesora.
DevPascal
...
uses Windows;
var
qpf : int64; // ilość taktów procesora na sekundę
qpc1,qpc2 : int64; // stany liczników 64-bitowych
tqpc : int64; // czas operacji odczytu czasu
t : extended; // czas w sekundach
...
if QueryPerformanceFrequency(addr(qpf)) then
begin
// kalibrujemy czas operacji pomiaru czasu
QueryPerformanceCounter(addr(qpc1));
QueryPerformanceCounter(addr(qpc2));
tqpc := qpc2 - qpc1;
...
// wykonujemy pomiar czasu
QueryPerformanceCounter(addr(qpc1));
// tutaj jest kod, którego czas pracy mierzymy
QueryPerformanceCounter(addr(qpc2));
t := (qpc2 - qpc1 - tqpc) / qpf; // czas w sekundach
...
end
else
writeln('Na tym komputerze program testowy nie pracuje...');
...
DevPascal
// Program testujący czas sortowania dla
// danego algorytmu sortującego
//--------------------------------------
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0001.php 4 / 209
Algorytmy Sortujące - Wstęp 2014-10-03
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// w Tarnowie
//--------------------------------------
program TestCzasuSortowania;
uses Windows;
const
NAZWA = 'Tutaj podajemy nazwe algorytmu';
K1 = '----------------------------------------';
K2 = '(C)2005 mgr J.Walaszek I LO w Tarnowie';
K3 = '------n---------tpo---------tod---------tpp---------tpk---------tnp';
K4 = '-------------------------------------------------------------------';
MAX_LN = 8; // określa ostatnie LN
LN : array[1..8] of integer = (1000,2000,4000,8000,16000,32000,64000,128000);
var
d : array[1..128000] of real; // sortowana tablica
n : integer; // liczba elementów
qpf,tqpc : int64; // dane dla pomiaru czasu
qpc1,qpc2 : int64;
// Tutaj umieszczamy procedurę sortującą tablicę d
//-------------------------------------------------------
function Sort : extended;
begin
QueryPerformanceCounter(addr(qpc1));
QueryPerformanceCounter(addr(qpc2));
Sort := (qpc2 - qpc1 - tqpc) / qpf;
end;
// Program główny
//---------------
var
i,j,k : integer;
tpo,tod,tpp,tpk,tnp : extended;
f : Text;
begin
if QueryPerformanceFrequency(addr(qpf)) then
begin
QueryPerformanceCounter(addr(qpc1));
QueryPerformanceCounter(addr(qpc2));
tqpc := qpc2 - qpc1;
assignfile(f,'wyniki.txt'); rewrite(f);
// Wydruk na ekran
writeln('Nazwa: ',NAZWA);
writeln(K1);
writeln(K2);
writeln;
writeln(K3);
// Wydruk do pliku
writeln(f,'Nazwa: ',NAZWA);
writeln(f,K1);
writeln(f,K2);
writeln(f,'');
writeln(f,K3);
for i := 1 to MAX_LN do
begin
n := LN[i];
// Czas sortowania zbioru posortowanego
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na początku - średnia z 10 obiegów
tpp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[1] := random * n + 1;
tpp += Sort;
end;
tpp /= 10;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na końcu - średnia z 10 obiegów
tpk := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[n] := random * n + 1;
tpk += Sort;
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0001.php 5 / 209
Algorytmy Sortujące - Wstęp 2014-10-03
end;
tpk /= 10;
// Czas sortowania zbioru nieuporządkowanego - średnia z 10 obiegów
tnp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := random;
tnp += Sort;
end;
tnp /= 10;
writeln(n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
writeln(f,n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
end;
writeln(K4);
writeln(f,K4);
writeln(f,'Koniec');
closefile(f);
writeln;
writeln('Koniec. Wyniki w pliku WYNIKI.TXT');
end
else writeln('Na tym komputerze program testowy nie pracuje !');
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
Bardzo duże znaczenie dla otrzymanych wyników ma środowisko, w którym program testowy będzie pracował. Aby usunąć obce
wpływy mogące zakłócić wyniki, komputer pracuje w czystym systemie Windows XP z SP2 bez uruchomionych w tle procesów, z
wyłączoną siecią oraz z wyłączonym oprogramowaniem antywirusowym. Parametry są następujące:
Wynikiem działania programu będą odpowiednie czasy sortowania zbiorów, które zależą od użytego do doświadczeń sprzętu
komputerowego - na twoim komputerze na pewno uzyskasz inne czasy. Co zatem będziemy badać?
Otóż dla każdego czasu określimy klasę czasowej złożoności obliczeniowej. Wg definicji (podanej w artykule o liczbach
pierwszych) klasa czasowej złożoności obliczeniowej jest funkcją liczby elementów przetwarzanych przez algorytm o jak
najprostszej postaci, do której jest proporcjonalny czas wykonania algorytmu (nie jest to definicja formalna!!!).
Przykład:
Jeśli pewien algorytm posiada czasową złożoność obliczeniową klasy O(n2), to czas wykonania algorytmu dla n
elementów jest w przybliżeniu proporcjonalny do kwadratu n, czyli:
t(n) ≈ cn2
n - liczba przetwarzanych elementów
t(n) - czas przetwarzania n-elementów w algorytmie
c - stała proporcjonalności pomiędzy t(n) a n2
t(n)
≈c
n2
Co z tego wynika? To proste - jeśli dany algorytm ma rzeczywiście klasę złożoności obliczeniowej O(n2), to otrzymany iloraz jest
w przybliżeniu taki sam dla wszystkich zmierzonych czasów. Wartość liczbowa stałej c jest dla nas zupełnie nieistotna (zależy
ona od parametrów komputera, na którym uruchomiliśmy nasz program testowy). Ważne jest jedynie, aby dla kolejnych ilorazów
miała ona taką samą wartość (z pewnym przybliżeniem, co jest chyba oczywiste).
Aby zatem określić klasę złożoności obliczeniowej, otrzymane w wyniku wykonania programu testowego czasy będziemy kolejno
dzielić przez: n, n2, n3 i n·logn, Do tego celu wykorzystamy prosty arkusz kalkulacyjny:
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0001.php 6 / 209
Algorytmy Sortujące - Wstęp 2014-10-03
Najpierw w kolumnie t(n) umieszczamy otrzymane czasy sortowania dla poszczególnych wartości n. W przykładzie kolumna
zawiera już dane oznakowane kolorem fioletowym. Podane czasy arkusz podzieli w kolejnych kolumnach przez wielkości n, n2, n3
i nlogn pobrane z kolumny n. Ponieważ wyniki są zwykle bardzo małe, a nas interesuje nie ich wartość liczbowa tylko to, czy są
stałe w pewnych granicach, czy też nie, arkusz wymnaża ilorazy przez podane u góry kolumny mnożniki tak dobrane, aby wyniki
w kolumnie były dobrze czytelne (najlepiej większe od 1). Teraz wystarczy sprawdzić, w której kolumnie wyliczone ilorazy
przyjmują w przybliżeniu stałą wartość. W przykładzie jest to kolumna O(n2). Zatem algorytm wykazuje klasę czasowej
złożoności obliczeniowej O(n2).
Drugą badaną dziedziną będzie efektywność opisywanych algorytmów. Ponieważ wszystkie z nich posortują identyczne zbiory
danych i wykonają się w tych samych warunkach oraz na tym samym sprzęcie komputerowym, to otrzymane czasy pozwolą nam
zorientować się, który z algorytmów robi to najlepiej i policzyć względny wzrost prędkości działania:
Przykład:
Jeśli algorytm A1 sortuje dany plik w czasie t1 = 32 [s] a algorytm A2 wykonuje to samo zadanie w czasie
t2 = 24 [s], to względny wzrost prędkości algorytmu A2 w stosunku do A1 wyraża się wzorem:
Wynika z tego, iż algorytm A2 jest w tym konkretnym przypadku 11/4 razy szybszy w stosunku do algorytmu A1.
Na końcu artykułu przeprowadzimy podsumowanie otrzymanych wyników i wyciągniemy z nich odpowiednie wnioski.
Zapraszam do lektury
mgr Jerzy Wałaszek
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0001.php 7 / 209
Algorytmy Sortujące - Sortowanie Zwariowane 2014-10-03
Sortowanie zwariowane
Bogo Sort, Bozo Sort, Blort Sort
Podrozdziały
Algorytm Programy
Szczegóły implementacyjne algorytmu Program w języku Pascal
Specyfikacja problemu Program w języku C++
Lista kroków Program w języku Basic
Schemat blokowy Program w języku JavaScript
Podsumowanie
Algorytm
Pierwszy z prezentowanych algorytmów sortujących opiera się na dosyć zwariowanych zasadach. Jego
działanie możemy scharakteryzować na przykładzie układania talii kart. Bierzemy talię kart. Sprawdzamy,
czy jest ułożona. Jeśli nie, tasujemy ją i znów sprawdzamy ułożenie. Operacje sprawdzania i tasowania
wykonujemy dotąd, aż talia nam się ułoży w pożądanej kolejności kart.
Nic nie sortujemy, wręcz dokonujemy operacji odwrotnej - tasowania, a talia może zostać posortowana.
Dlaczego? Wynika to z praw rachunku prawdopodobieństwa. Otóż tasowanie powoduje, iż karty przyjmują
losowe permutacje swoich położeń. Ponieważ każda permutacja zbioru kart jest równie prawdopodobna (jeśli
przy tasowaniu nie oszukujemy), zatem możemy też otrzymać układ uporządkowany. Oczywiście wynik taki
pojawia się dosyć rzadko (bądźmy szczerzy - przy dużej liczbie elementów bardzo, bardzo... rzadko). Nie
polecamy sortowania tą metodą zbiorów liczniejszych niż 9 elementów.
Algorytm opiera się na losowym sortowaniu zbioru. Tymczasem w komputerze nie mamy tak naprawdę
dostępu do liczb czysto losowych. Zadowalamy się ich przybliżeniem, czyli liczbami pseudolosowymi powstającymi na bazie
algorytmicznej (dokładny opis tworzenia liczb pseudolosowych znajdziesz w artykule o liczbach pierwszych). Może się zatem
zdarzyć, iż nasz generator pseudolosowy nigdy nie wygeneruje potrzebnej sekwencji liczb pseudolosowych, zatem algorytm
sortujący nie będzie w stanie ukończyć swojej pracy.
Z tego powodu jest to jeden z najgorszych algorytmów sortujących. Posiada pesymistyczną czasową złożoność obliczeniową
klasy O(n •n!). Złożoność taką nazywamy złożonością super wykładniczą. Co gorsze, ten sam zbiór raz może zostać
błyskawicznie posortowany (gdy akurat mamy szczęście), a innym razem możemy czekać na wynik nawet cały rok (albo jeszcze
dłużej). Sortowanie odbywa się w miejscu.
Szczegóły implementacji
Jeśli za pomocą podanego algorytmu chcemy posortować zbiór liczbowy (a takimi się zajmujemy w opracowaniu), to musimy
rozwiązać dwa istotne problemy: sprawdzenie posortowania elementów oraz losowe potasowanie.
Pascal x := a; a := b; b := x;
C++ i JavaScript x = a; a = b; b = x;
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0002.php 8 / 209
Algorytmy Sortujące - Sortowanie Zwariowane 2014-10-03
Specyfikacja problemu
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n.
Dane wyjściowe
d[ ] - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n.
Zmienne pomocnicze
i - zmienna sterująca pętli, i Î N
i1, i2 - losowe indeksy elementów zbioru d[ ], i1, i2 Î N
Lista kroków
Algorytm procedury Tasuj
K01: Dla i = 1,2,...,3 × n, wykonuj K02...K03
K02: Wylosuj indeksy i1, i2 Î <1,n>
K03: d[i1] ↔ d[i2]
K04: Zakończ
Schemat blokowy
Trzon algorytmu stanowi pojedyncza pętla warunkowa typu while. Na początku wywołana zostaje funkcja
sprawdzająca posortowanie zbioru - Posortowane?. Jeśli da wynik pozytywny, kończymy algorytm.
Przy wyniku negatywnym wywołujemy procedurę tasowania elementów zbioru - Tasuj i pętla jest
powtarzana aż do skutku (który może nigdy nie nastąpić z powodu niedoskonałości generacji liczb
pseudolosowych! - zobacz na uwagi umieszczone na początku rozdziału).
Uwaga:
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0002.php 9 / 209
Algorytmy Sortujące - Sortowanie Zwariowane 2014-10-03
Sprawdź, jak zachowa się ten sam algorytm dla zbioru pustego (tzn. przy n = 0) oraz dla zbioru
jednoelementowego (tzn. n = 1). Jakie wyciągniesz stąd wnioski?
Tasowanie elementów zbioru jest operacją odwrotną do ich sortowania. Tutaj celem będzie losowe
ustawienie elementów.
Trzonem algorytmu jest pętla iteracyjna wykonująca się 3n razy. Ilość wykonań można dobrać
doświadczalnie - my przyjęliśmy trzykrotną ilość elementów w tasowanym zbiorze.
Pojedyncza operacja tasowania polega na wylosowaniu dwóch indeksów w zakresie od 1 do n, a
następnie zamianie zawartości elementów zbioru o tych indeksach. W wyniku elementy zbioru
zostaną rozmieszczone losowo.
Zapamiętaj ten algorytm - bardzo przydaje się on w różnych grach losowych, gdzie należy coś
pomieszać (na przykład wszelkie gry karciane).
Uwaga:
Sprawdź co się stanie, gdy wylosowane zostaną dwa takie same indeksy. Czy
musimy się tego obawiać przy tasowaniu zbioru?
Programy
Efekt uruchomienia programu
Sortowanie zwariowane
----------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
2043 6751 8525 2306 3923 7457 9686 5862
Po sortowaniu:
2043 2306 3923 5862 6751 7457 8525 9686
DevPascal
// Sortowanie Zwariowane
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program Bogo_Sort;
const N = 8; // Liczebność zbioru. Nie wstawiaj liczb
// większych od 9, bo możesz się nie
// doczekać rozwiązania
var
d : array[1..N] of integer;
// Funkcja sprawdzająca uporządkowanie w zbiorze
//-----------------------------------------------------
function Posortowane : boolean;
var
i : integer;
begin
for i := 1 to N - 1 do
if d[i] > d[i+1] then
begin
Posortowane := false; Exit;
end;
Posortowane := true;
end;
// Procedura tasująca zbiór
//-------------------------
procedure Tasuj;
var
i,i1,i2,x : integer;
begin
for i := 1 to 3 * N do
begin
i1 := 1 + random(N); i2 := 1 + random(N);
x := d[i1]; d[i1] := d[i2]; d[i2] := x;
end;
end;
// Program główny
//---------------
var
i : integer;
begin
writeln('Sortowanie zwariowane');
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0002.php 10 / 209
Algorytmy Sortujące - Sortowanie Zwariowane 2014-10-03
writeln('----------------------');
writeln('(C)2005 Jerzy Walaszek');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(10000);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 6);
writeln; writeln;
// Sortujemy
while not Posortowane do Tasuj;
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 6);
writeln; writeln;
writeln('Nacisnij Enter...');
readln;
end.
Code::Blocks
// Sortowanie Zwariowane
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 8; // Liczebność zbioru. Nie wstawiaj liczb
// większych od 9, bo możesz się nie
// doczekać rozwiązania
int d[N];
// Funkcja sprawdzająca uporządkowanie w zbiorze
//-----------------------------------------------------
bool Posortowane()
{
int i;
for(i = 0; i < N - 1; i++) if(d[i] > d[i+1]) return false;
return true;
}
// Procedura tasująca zbiór
//-------------------------
void Tasuj()
{
int i,i1,i2;
for(i = 1; i <= 3 * N; i++)
{
i1 = rand() % N; i2 = rand() % N;
swap(d[i1], d[i2]);
}
}
//******************************************************
int main()
{
int i;
cout << "Sortowanie zwariowane\n"
"----------------------\n"
"(C)2005 Jerzy Walaszek\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 10000;
cout << "Przed sortowaniem:\n\n";
for(i = 0; i < N; i++) cout << setw(6) << d[i];
cout << endl << endl;
// Sortujemy
while(!Posortowane()) Tasuj();
// Wyświetlamy wynik sortowania
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0002.php 11 / 209
Algorytmy Sortujące - Sortowanie Zwariowane 2014-10-03
Free Basic
' Sortowanie Zwariowane
' --------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 8 ' Liczebność zbioru. Nie wstawiaj liczb
' większych od 9, bo możesz się nie
' doczekać rozwiązania
Dim Shared d(1 To N) As Integer
Declare Function Posortowane() As Integer
Declare Sub Tasuj()
' Program główny
' --------------
Dim i As Integer
Print "Sortowanie zwariowane"
Print "----------------------"
Print "(C)2005 Jerzy Walaszek"
Print
' Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
' a następnie wyświetlamy jej zawartość
Randomize Timer
For i = 1 To N: d(i) = Int(Rnd * 10000): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "######"; d(i);: Next
Print
Print
' Sortujemy
While Posortowane() = 0: Tasuj(): Wend
' Wyświetlamy wynik sortowania
Print "Po sortowaniu:"
Print
For i = 1 To N: Print Using "######"; d(i);: Next
Print
Print
Print "Nacisnij Enter...";
Sleep
End
' Funkcja sprawdzająca uporządkowanie w zbiorze
'-----------------------------------------------------
Public Function Posortowane() As Integer
Dim i As Integer
For i = 1 To N - 1
If d(i) > d(i+1) Then
Posortowane = 0: Exit Function
End If
Next
Posortowane = 1
End Function
' Procedura tasująca zbiór
'-------------------------
Public Sub Tasuj()
Dim i As Integer, i1 As Integer, i2 As Integer
For i = 1 To 3 * N
i1 = 1 + Int(Rnd * N)
i2 = 1 + Int(Rnd * N)
Swap d(i1), d(i2)
Next
End Sub
JavaScript
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0002.php 12 / 209
Algorytmy Sortujące - Sortowanie Zwariowane 2014-10-03
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmbogosort">
<h3 style="text-align: center">Sortowanie Zwariowane</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Zwariowane
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 8; // Liczebność zbioru. Nie wstawiaj liczb
// większych od 9, bo możesz się nie
// doczekać rozwiązania
var d = Array(N);
// Funkcja sprawdzająca uporządkowanie w zbiorze
//-----------------------------------------------------
function Posortowane()
{
var i;
for(i = 0; i < N - 1; i++) if(d[i] > d[i+1]) return false;
return true;
}
// Procedura tasująca zbiór
//-------------------------
function Tasuj()
{
var i,i1,i2,x;
for(i = 1; i <= 3 * N; i++)
{
i1 = Math.floor(Math.random() * N); i2 = Math.floor(Math.random() * N);
x = d[i1]; d[i1] = d[i2]; d[i2] = x;
}
}
//******************************************************
function main()
{
var i,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 10000);
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Sortujemy
while(!Posortowane()) Tasuj();
// Wyświetlamy wynik sortowania
t += "Po sortowaniu<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Sortowanie Zwariowane
(C)2012 I LO w Tarnowie - I LO w Tarnowie
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0002.php 13 / 209
Algorytmy Sortujące - Sortowanie Zwariowane 2014-10-03
Sortuj
...
Podsumowanie
Zaprezentowany algorytm jest ekstremalnie złym algorytmem sortującym i na pewno nikt o zdrowych zmysłach nie będzie go
stosował. Jednakże zawiera dwa ciekawe składniki, które można wykorzystywać w "poważniejszych" projektach
programistycznych: sprawdzanie posortowania zbioru oraz tasowanie elementów zbioru.
Algorytmu Bogo Sort nie testujemy w naszym programie badania czasów sortowania, ponieważ, jak zdążyliście się zorientować,
nawet posortowanie 10 elementów może zająć całkiem sporą chwilę czasu. W ramach eksperymentu próbowałem posortować tym
algorytmem zbiór 12 liczb, lecz muszę szczerze przyznać, iż po dwóch godzinach czekania zniecierpliwiony zakończyłem
program w sposób awaryjny. Posortowanie 1000 liczb wydaje się niewykonalne w tym miliardoleciu.
Jako ciekawostkę podam fakt, iż informatycy terminem "bogo sort" określają program lub algorytm, którego idea działania jest tak
beznadziejnie głupia, iż praktycznie nie może dać rozwiązania w sensownym czasie. Zatem jeśli usłyszysz zdanie: "twój program
to bogo sort", to już będziesz wiedział o co chodzi rozmówcy... :)
Cechy Algorytmu Sortowania Zwariowanego
klasa złożoności obliczeniowej O(n × n!)
Sortowanie w miejscu TAK
Stabilność NIE
Temat:
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0002.php 14 / 209
Algorytmy Sortujące - Sortowanie Głupie 2014-10-03
Algorytm
Sortowanie głupie jest również bardzo złym algorytmem sortującym, lecz, w przeciwieństwie do
opisanego w poprzednim rozdziale sortowania zwariowanego, daje zawsze poprawne wyniki. Zasada
działania jest bardzo prosta:
Przeglądamy kolejne pary sąsiednich elementów sortowanego zbioru. Jeśli bieżąco przeglądana
para elementów jest w złej kolejności, elementy pary zamieniamy miejscami i całą operację
rozpoczynamy od początku zbioru. Jeśli przeglądniemy wszystkie pary, zbiór będzie posortowany.
Naiwność (lub, jak wolą niektórzy, głupota) algorytmu wyraża się tym, iż po napotkaniu
nieposortowanych elementów algorytm zamienia je miejscami, a następnie rozpoczyna całą pracę
od początku zbioru. Złożoność obliczeniowa algorytmu przy sortowaniu zbioru nieuporządkowanego
ma klasę O(n3). Sortowanie odbywa się w miejscu.
Algorytm sortowania głupiego występuje w dwóch wersjach - rekurencyjnej oraz iteracyjnej. Wersja
rekurencyjna jest jeszcze gorsza od iteracyjnej, gdyż dodatkowo zajmuje pamięć na kolejne poziomy wywołań rekurencyjnych,
dlatego nie będziemy się nią zajmować (zainteresowanych odsyłam do Wikipedii).
Specyfikacja problemu
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n.
Dane wyjściowe
d[ ] - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n.
Zmienne pomocnicze
i - zmienna sterująca pętli, i Î N
Lista kroków
K01: Dla i = 1,2,...,n - 1: wykonuj K02...K04
K02: Jeśli d[i] ≤ d[i + 1], to wykonaj następny obieg K01
K03: d[i] ↔ d[i + 1]
K04: Idź do K01
K05: Zakończ
Schemat blokowy
Rozpoczynamy przeglądanie zbioru od pierwszego elementu - indeks i przyjmuje wartość 1. W
pętli sprawdzamy kolejność elementu d[i] z elementem następnym d[i+1]. Ponieważ założyliśmy
porządek rosnący, to w przypadku d[i] > d[i+1] elementy te są w złej kolejności (dla porządku
malejącego należy zmienić relację większości na relację mniejszości). W takiej sytuacji
zamieniamy miejscami elementy, indeks i ustawiamy z powrotem na 1 (powrót na sam początek
algorytmu byłby nieco kłopotliwy do zrealizowania w językach programowania, stąd dla prostoty
ustawiamy i na 1 za operacją zamiany elementów) i wracamy na początek pętli.
Jeśli porównywane elementy są w dobrej kolejności, zwiększamy indeks i o 1, sprawdzamy, czy
osiągnął już wartość końcową n i, jeśli nie, wracamy na początek pętli. W przeciwnym razie
kończymy - zbiór jest posortowany.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0003.php 15 / 209
Algorytmy Sortujące - Sortowanie Głupie 2014-10-03
Programy
Efekt uruchomienia programu
Sortowanie glupie
----------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
60 70 46 52 97 34 14 60 4 56 77 34 75 72 78 86 45 12 30 99
Po sortowaniu:
4 12 14 30 34 34 45 46 52 56 60 60 70 72 75 77 78 86 97 99
DevPascal
// Sortowanie Głupie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program Stupid_Sort;
const N = 20; // Liczebność zbioru.
var
d : array[1..N] of integer;
// Program główny
//---------------
var
i,x : integer;
begin
writeln(' Sortowanie glupie ');
writeln('----------------------');
writeln('(C)2005 Jerzy Walaszek');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
// Sortujemy
i := 1;
repeat
if d[i] > d[i+1] then // Porządek rosnący
begin
x := d[i]; d[i] := d[i+1]; d[i+1] := x;
i := 1;
continue;
end;
inc(i);
until i = N;
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln('Nacisnij Enter...');
readln;
end.
Code::Blocks
// Sortowanie Głupie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0003.php 16 / 209
Algorytmy Sortujące - Sortowanie Głupie 2014-10-03
// Program główny
//---------------
int main()
{
int d[N],i;
cout << " Sortowanie glupie\n"
"----------------------\n"
"(C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 100;
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// Sortujemy
i = 0;
do
{
if(d[i] > d[i+1]) // Porządek rosnący
{
swap(d[i], d[i+1]);
i = 0;
continue;
}
i++;
} while(i < N-1);
// Wyświetlamy wynik sortowania
cout << "Po sortowaniu:\n\n";
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
return 0;
}
Free Basic
' Sortowanie Głupie
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 20 ' Liczebność zbioru.
Dim d(1 To N) As Integer, i As Integer
Print " Sortowanie glupie "
Print "----------------------"
Print "(C)2005 Jerzy Walaszek"
Print
' Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
' a następnie wyświetlamy jej zawartość
Randomize Timer
For i = 1 To N: d(i) = Int(Rnd * 100): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
' Sortujemy
i = 1
Do
If d(i) > d(i+1) Then ' Porządek rosnący
Swap d(i), d(i+1)
i = 1
Continue Do
End If
i = i + 1
Loop Until i = N
' Wyświetlamy wynik sortowania
Print "Po sortowaniu"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
Print "Nacisnij Enter...";
Sleep
End
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0003.php 17 / 209
Algorytmy Sortujące - Sortowanie Głupie 2014-10-03
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmstupidsort">
<h3 style="text-align: center">Sortowanie Głupie</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Głupie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
function main()
{
var d = new Array(N);
var i,x,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 100);
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Sortujemy
i = 0;
do
{
if(d[i] > d[i+1]) // Porządek rosnący
{
x = d[i]; d[i] = d[i+1]; d[i+1] = x;
i = 0;
continue;
}
i++;
} while(i < N-1);
// Wyświetlamy wynik sortowania
t += "Po sortowaniu:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Sortowanie Głupie
(C)2012 I LO w Tarnowie - I LO w Tarnowie
Sortuj
...
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0003.php 18 / 209
Algorytmy Sortujące - Sortowanie Głupie 2014-10-03
DevPascal
// Program testujący czas sortowania dla
// danego algorytmu sortującego
//--------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// w Tarnowie
//--------------------------------------
program TestCzasuSortowania;
uses Windows;
const
NAZWA = 'Sortowanie Glupie - Stupid Sort';
K1 = '----------------------------------------';
K2 = '(C)2011/2012 I Liceum Ogolnoksztalcace w Tarnowie';
K3 = '------n---------tpo---------tod---------tpp---------tpk---------tnp';
K4 = '-------------------------------------------------------------------';
MAX_LN = 4; // określa ostatnie LN
LN : array[1..8] of integer = (1000,2000,4000,8000,16000,32000,64000,128000);
var
D : array[1..128000] of real; // sortowana tablica
n : integer; // liczba elementów
qpf,tqpc : int64; // dane dla pomiaru czasu
qpc1,qpc2 : int64;
// Tutaj umieszczamy procedurę sortującą tablicę d
//-------------------------------------------------------
function Sort : extended;
var
i : integer;
x : real;
begin
QueryPerformanceCounter(addr(qpc1));
i := 1;
repeat
if d[i] > d[i+1] then // Porządek rosnący
begin
x := d[i]; d[i] := d[i+1]; d[i+1] := x;
i := 1;
continue;
end;
inc(i);
until i = n;
QueryPerformanceCounter(addr(qpc2));
Sort := (qpc2 - qpc1 - tqpc) / qpf;
end;
// Program główny
//---------------
var
i,j,k : integer;
tpo,tod,tpp,tpk,tnp : extended;
f : Text;
begin
if QueryPerformanceFrequency(addr(qpf)) then
begin
QueryPerformanceCounter(addr(qpc1));
QueryPerformanceCounter(addr(qpc2));
tqpc := qpc2 - qpc1;
assignfile(f,'wyniki.txt'); rewrite(f);
// Wydruk na ekran
writeln('Nazwa: ',NAZWA);
writeln(K1);
writeln(K2);
writeln;
writeln(K3);
// Wydruk do pliku
writeln(f,'Nazwa: ',NAZWA);
writeln(f,K1);
writeln(f,K2);
writeln(f,'');
writeln(f,K3);
for i := 1 to MAX_LN do
begin
n := LN[i];
// Czas sortowania zbioru posortowanego
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na początku - średnia z 10 obiegów
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0003.php 19 / 209
Algorytmy Sortujące - Sortowanie Głupie 2014-10-03
tpp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[1] := random * n + 1;
tpp += Sort;
end;
tpp /= 10;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na końcu - średnia z 10 obiegów
tpk := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[n] := random * n + 1;
tpk += Sort;
end;
tpk /= 10;
// Czas sortowania zbioru nieuporządkowanego - średnia z 10 obiegów
tnp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := random;
tnp += Sort;
end;
tnp /= 10;
writeln(n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
writeln(f,n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
end;
writeln(K4);
writeln(f,K4);
writeln(f,'Koniec');
closefile(f);
writeln;
writeln('Koniec. Wyniki w pliku WYNIKI.TXT');
end
else writeln('Na tym komputerze program testowy nie pracuje !');
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika - najlepiej na noc):
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu sortowania głupiego
wyciągamy następujące wnioski
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0003.php 20 / 209
Algorytmy Sortujące - Sortowanie Głupie 2014-10-03
Stabilność TAK
optymistyczna - dla zbiorów uporządkowanych (z niewielką liczbą elementów nie na swoich miejscach) - na podstawie
czasów tpo, tpp, tpk
typowa - dla zbiorów o losowym rozkładzie elementów - na podstawie czasu tnp
pesymistyczna - dla zbiorów posortowanych odwrotnie - na podstawie czasu tod.
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
O(n) O(n3) O(n2) O(n2) O(n3)
Sortowanie głupie
tpo << tod tpp ≈1tpk tnp ≈2tod
2 3
1. Dla zbioru już posortowanego klasa czasowej złożoności obliczeniowej wynosi O(n) - liniowa. Czas sortowania jest
pomijalnie mały.
2. Dla zbioru posortowanego odwrotnie klasa czasowej złożoności obliczeniowej wynosi O(n3) - sześcienna, a czas
sortowania jest najdłuższy z otrzymanych, jest to zatem przypadek najbardziej niekorzystny.
3. Dla zbioru posortowanego z losowym elementem na początku klasa czasowej złożoności obliczeniowej wynosi O(n2) -
kwadratowa. Czas sortowania jest bardzo mały w porównaniu z czasem sortowania zbioru nieuporządkowanego.
4. Dla zbioru posortowanego z losowym elementem na końcu klasa czasowej złożoności obliczeniowej wynosi O(n2) -
kwadratowa.
5. Dla zbioru o losowym rozkładzie elementów klasa czasowej złożoności obliczeniowej wynosi O(n3) - sześcienna. Czas
sortowania jest krótszy w porównaniu z czasem sortowania zbioru uporządkowanego odwrotnie.
Temat:
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0003.php 21 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
Algorytm
Algorytm sortowania bąbelkowego jest jednym z najstarszych algorytmów sortujących. Można
go potraktować jako ulepszenie opisanego w poprzednim rozdziale algorytmu sortowania głupiego.
Zasada działania opiera się na cyklicznym porównywaniu par sąsiadujących elementów i zamianie
ich kolejności w przypadku niespełnienia kryterium porządkowego zbioru. Operację tę wykonujemy
dotąd, aż cały zbiór zostanie posortowany.
Algorytm sortowania bąbelkowego przy porządkowaniu zbioru nieposortowanego ma klasę czasowej
złożoności obliczeniowej równą O(n2). Sortowanie odbywa się w miejscu.
Przykład:
Jako przykład działania algorytmu sortowania bąbelkowego posortujemy przy jego pomocy 5-cio elementowy zbiór
liczb {5 4 3 2 1}, który wstępnie jest posortowany w kierunku odwrotnym, co możemy uznać za przypadek
najbardziej niekorzystny, ponieważ wymaga przestawienia wszystkich elementów.
Obieg Zbiór Opis operacji
5 4 3 2 1 Rozpoczynamy od pierwszej pary, która wymaga wymiany elementów
4 5 3 2 1 Druga para też wymaga zamiany elementów
4 3 2 1 5 Stan po pierwszym obiegu. Zwróć uwagę, iż najstarszy element (5) znalazł się na końcu
zbioru, a najmłodszy (1) przesunął się o jedną pozycję w lewo.
4 3 2 1 5 Para wymaga wymiany
3 4 2 1 5 Para wymaga wymiany
3 2 4 1 5 Para wymaga wymiany
2 3 2 1 4 5 Elementy są w dobrej kolejności, zamiana nie jest konieczna.
Stan po drugim obiegu. Zwróć uwagę, iż najmniejszy element (1) znów przesunął się o
3 2 1 4 5 jedną pozycję w lewo. Z obserwacji tych można wywnioskować, iż po każdym obiegu
najmniejszy element wędruje o jedną pozycję ku początkowi zbioru. Najstarszy element
zajmuje natomiast swe miejsce końcowe.
3 2 1 4 5 Para wymaga wymiany
2 3 1 4 5 Para wymaga wymiany
3 2 1 3 4 5 Dobra kolejność
2 1 3 4 5 Dobra kolejność
4 1 2 3 4 5 Dobra kolejność
1 2 3 4 5 Dobra kolejność
Posortowanie naszego zbioru wymaga 4 obiegów. Jest to oczywiste: w przypadku najbardziej niekorzystnym
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0004.php 22 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
najmniejszy element znajduje się na samym końcu zbioru wejściowego. Każdy obieg przesuwa go o jedną pozycję w
kierunku początku zbioru. Takich przesunięć należy wykonać n - 1 (n - ilość elementów w zbiorze).
Algorytm sortowania bąbelkowego, w przeciwieństwie do algorytmu sortowania głupiego, nie przerywa porównywania
par elementów po napotkaniu pary nie spełniającej założonego porządku. Po zamianie kolejności elementów
sprawdzana jest kolejna para elementów sortowanego zbioru. Dzięki temu podejściu rośnie efektywność algorytmu
oraz zmienia się klasa czasowej złożoności obliczeniowej z O(n3) na O(n2).
Uwaga:
Algorytm sortowania bąbelkowego jest uważany za bardzo zły algorytm sortujący. Można go
stosować tylko dla niewielkiej liczby elementów w sortowanym zbiorze (do około 5000). Przy
większych zbiorach czas sortowania może być zbyt długi.
Specyfikacja problemu
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n.
Dane wyjściowe
d[ ] - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n.
Zmienne pomocnicze
i, j - zmienne sterujące pętli, i, j Î N
Lista kroków
K01: Dla j = 1,2,...,n - 1: wykonuj K02
K02: Dla i = 1,2,...,n - 1: jeśli d[i] > d[i + 1], to d[i] ↔ d[i + 1]
K03: Zakończ
Schemat blokowy
Sortowanie wykonywane jest w dwóch zagnieżdżonych pętlach. Pętla zewnętrzna nr 1
kontrolowana jest przez zmienną j. Wykonuje się ona n - 1 razy. Wewnątrz pętli nr 1 umieszczona
jest pętla nr 2 sterowana przez zmienną i. Wykonuje się również n - 1 razy. W efekcie algorytm
wykonuje w sumie:
T1(n) = (n - 1)2 = n2 - 2n + 1
obiegów pętli wewnętrznej, po których zakończeniu zbiór zostanie posortowany.
Sortowanie odbywa się wewnątrz pętli nr 2. Kolejno porównywany jest i-ty element z elementem
następnym. Jeśli elementy te są w złej kolejności, to zostają zamienione miejscami. W tym
miejscu jest najważniejsza różnica pomiędzy algorytmem sortowania bąbelkowego a algorytmem
sortowania głupiego. Ten drugi w momencie napotkania elementów o złej kolejności zamienia je
miejscami i rozpoczyna cały proces sortowania od początku. Algorytm sortowania bąbelkowego
wymienia miejscami źle ułożone elementy sortowanego zbioru i przechodzi do następnej pary
zwiększając indeks i o 1. Dzięki takiemu podejściu rośnie efektywność, co odzwierciedla klasa
czasowej złożoności obliczeniowej:
Programy
Efekt uruchomienia programu
Sortowanie babelkowe
WERSJA NR 1
----------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
74 77 21 76 64 19 43 47 75 77 66 47 60 7 97 71 87 95 76 79
Po sortowaniu:
7 19 21 43 47 47 60 64 66 71 74 75 76 76 77 77 79 87 95 97
DevPascal
// Sortowanie Bąbelkowe - Wersja nr 1
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0004.php 23 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program Bubble_Sort_1;
const N = 20; // Liczebność zbioru.
var
d : array[1..N] of integer;
// Program główny
//---------------
var
i,j,x : integer;
begin
writeln(' Sortowanie babelkowe ');
writeln(' WERSJA NR 1 ');
writeln('----------------------');
writeln('(C)2005 Jerzy Walaszek');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
// Sortujemy
for j := 1 to N - 1 do
for i := 1 to N - 1 do
if d[i] > d[i+1] then // Porządek rosnący
begin
x := d[i]; d[i] := d[i+1]; d[i+1] := x;
end;
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln('Nacisnij Enter...');
readln;
end.
Code::Blocks
// Sortowanie Bąbelkowe - Wersja nr 1
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
// Program główny
//---------------
int main()
{
int d[N],i,j;
cout << " Sortowanie babelkowe\n"
" WERSJA NR 1\n"
"----------------------\n"
"(C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 100;
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// Sortujemy
for(j = 0; j < N - 1; j++)
for(i = 0; i < N - 1; i++)
if(d[i] > d[i + 1]) swap(d[i], d[i + 1]);
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0004.php 24 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
Free Basic
' Sortowanie Bąbelkowe - Wersja nr 1
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 20 ' Liczebność zbioru.
Dim d(1 To N) As Integer, i As Integer, j As Integer
Print " Sortowanie babelkowe "
Print " WERSJA NR 1 "
Print "----------------------"
Print "(C)2005 Jerzy Walaszek"
Print
' Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
' a następnie wyświetlamy jej zawartość
Randomize Timer
For i = 1 To N: d(i) = Int(Rnd * 100): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
' Sortujemy
For j = 1 To N - 1
For i = 1 To N - 1
If d(i) > d(i+1) Then Swap d(i), d(i+1)
Next
Next
' Wyświetlamy wynik sortowania
Print "Po sortowaniu:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
Print "Nacisnij Enter..."
Sleep
End
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmbubblesort">
<h3 style="text-align: center">Sortowanie Bąbelkowe - wersja nr 1</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Bąbelkowe - wersja nr 1
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
function main()
{
var d = new Array(N);
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0004.php 25 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
var i,j,x,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 100);
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Sortujemy
for(j = 0; j < N - 1; j++)
for(i = 0; i < N - 1; i++)
if(d[i] > d[i + 1])
{
x = d[i]; d[i] = d[i + 1]; d[i + 1] = x;
};
// Wyświetlamy wynik sortowania
t += "Po sortowaniu:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Sortuj
...
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0004.php 26 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
QueryPerformanceCounter(addr(qpc2));
Sort := (qpc2 - qpc1 - tqpc) / qpf;
end;
// Program główny
//---------------
var
i,j,k : integer;
tpo,tod,tpp,tpk,tnp : extended;
f : Text;
begin
if QueryPerformanceFrequency(addr(qpf)) then
begin
QueryPerformanceCounter(addr(qpc1));
QueryPerformanceCounter(addr(qpc2));
tqpc := qpc2 - qpc1;
assignfile(f,'wyniki.txt'); rewrite(f);
// Wydruk na ekran
writeln('Nazwa: ',NAZWA);
writeln(K1);
writeln(K2);
writeln;
writeln(K3);
// Wydruk do pliku
writeln(f,'Nazwa: ',NAZWA);
writeln(f,K1);
writeln(f,K2);
writeln(f,'');
writeln(f,K3);
for i := 1 to MAX_LN do
begin
n := LN[i];
// Czas sortowania zbioru posortowanego
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na początku - średnia z 10 obiegów
tpp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[1] := random * n + 1;
tpp += Sort;
end;
tpp /= 10;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na końcu - średnia z 10 obiegów
tpk := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[n] := random * n + 1;
tpk += Sort;
end;
tpk /= 10;
// Czas sortowania zbioru nieuporządkowanego - średnia z 10 obiegów
tnp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := random;
tnp += Sort;
end;
tnp /= 10;
writeln(n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
writeln(f,n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
end;
writeln(K4);
writeln(f,K4);
writeln(f,'Koniec');
closefile(f);
writeln;
writeln('Koniec. Wyniki w pliku WYNIKI.TXT');
end
else writeln('Na tym komputerze program testowy nie pracuje !');
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0004.php 27 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika):
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu sortowania bąbelkowego 1
wyciągamy następujące wnioski:
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
Sortowanie głupie 1 n 1 1 n
Sortowanie bąbelkowe 1 n 33 6 4 33
źle dobrze źle źle dobrze
4. Porównując wzrost prędkości algorytmu sortowania bąbelkowego w stosunku do algorytmu sortowania głupiego
zauważamy, iż korzyść następuje w przypadku sortowania zbioru uporządkowanego odwrotnie oraz zbioru
nieuporządkowanego. Wzrost prędkości jest proporcjonalny liniowo do liczby elementów w sortowanym zbiorze. W
pozostałych przypadkach algorytm sortowania głupiego jest dużo szybszy od tej wersji algorytmu.
1. Uzasadnij, iż wszystkie czasy dla tej wersji algorytmu są proporcjonalne do kwadratu liczby sortowanych elementów.
2. Dlaczego czasy tpo, tpp oraz tpk są takie same?
3. Uzasadnij wyniki otrzymane przy badaniu wzrostu prędkości algorytmu sortowania bąbelkowego w stosunku do algorytmu sortowania
głupiego.
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0004.php 29 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
Algorytm
Podany w poprzednim rozdziale algorytm sortowania bąbelkowego można zoptymalizować pod
względem czasu wykonania. Jeśli przyjrzymy się dokładnie obiegom wykonywanym w tym
algorytmie, to zauważymy bardzo istotną rzecz:
Wniosek ten jest oczywisty. W każdej kolejnej parze porównywanych elementów element starszy
przechodzi na drugą pozycję. W kolejnej parze jest on na pierwszej pozycji, a skoro jest
najstarszym, to po porównaniu znów przejdzie na pozycję drugą itd. - jest jakby ciągnięty na koniec zbioru (jak bąbelek powietrza
wypływający na powierzchnię wody).
Przykład:
Wykonamy jeden obieg sortujący dla zbioru pięcioelementowego
{ 9 3 1 7 0 }. Elementem najstarszym jest pierwszy element - liczba 9.
Co z tego wynika dla nas? Otóż po każdym obiegu na końcu zbioru tworzy się podzbiór uporządkowanych
najstarszych elementów. Zatem w kolejnych obiegach możemy pomijać sprawdzanie ostatnich elementów -
liczebność zbioru do posortowania z każdym obiegiem maleje o 1.
Przykład:
Dokończmy sortowania podanego powyżej zbioru uwzględniając podane przez nas fakty. Po pierwszym obiegu na
końcu zbioru mamy umieszczony element najstarszy. W drugim obiegu będziemy zatem sortować zbiór 4
elementowy, w trzecim obiegu 3 elementowy i w obiegu ostatnim, czwartym - zbiór 2 elementowy.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0005.php 30 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
W porównaniu do tabelki z poprzedniego rozdziału nawet wzrokowo możemy zauważyć istotne zmniejszenie ilości niezbędnych
operacji.
Specyfikacja problemu
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n.
Dane wyjściowe
d[ ] - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n.
Zmienne pomocnicze
i, j - zmienne sterujące pętli, i, j Î N
Lista kroków
K01: Dla j = n - 1, n - 2, ..., 1: wykonuj K02
K02: Dla i = 1, 2, ..., j: jeśli d[i] > d[i + 1], to d[i] ↔ d[i + 1]
K03: Zakończ
Schemat blokowy
Zmiany w stosunku do poprzedniej wersji zaznaczyliśmy na schemacie blokowym innym kolorem
elementów. Są one następujące:
- pętla zewnętrzna nr 1 zlicza obiegi wstecz, tzn. pierwszy obieg ma numer n-1. Dzięki takiemu
podejściu w zmiennej j mamy zawsze numer ostatniego elementu, do którego ma dojść pętla
wewnętrzna nr 2. Ta zmiana wymaga również odwrotnej iteracji zmiennej j.
- pętla wewnętrzna sprawdza w warunku kontynuacji, czy wykonała j obiegów, a nie jak
poprzednio n-1 obiegów. Dzięki temu po każdym obiegu pętli nr 1 (zewnętrznej) pętla nr 2 będzie
wykonywać o jeden obieg mniej.
Pozostała część algorytmu nie jest zmieniona - w pętli wewnętrznej nr 2 sprawdzamy, czy
element d[i] jest w złej kolejności z elementem d[i+1]. Sprawdzany warunek spowoduje
posortowanie zbioru rosnąco. Przy sortowaniu malejącym zmieniamy relację większości na relację
mniejszości. Jeśli warunek jest spełniony, zamieniamy miejscami element d[i] z elementem
d[i+1], po czym kontynuujemy pętlę nr 2 zwiększając o 1 indeks i. Po każdym zakończeniu pętli
nr 2 indeks j jest zmniejszany o 1.
Ilość obiegów pętli wewnętrznej wynosi:
Otrzymane wyrażenie ma wciąż kwadratową klasę złożoności obliczeniowej, jednakże T2(n) < T1(n) dla n > 1. Osiągnęliśmy
zatem większą efektywność działania dzięki wprowadzonym zmianom.
Programy
Efekt uruchomienia programu
Sortowanie babelkowe
WERSJA NR 2
----------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
44 29 43 80 95 88 40 45 28 13 8 90 49 28 76 64 22 70 4 6
Po sortowaniu:
4 6 8 13 22 28 28 29 40 43 44 45 49 64 70 76 80 88 90 95
DevPascal
// Sortowanie Bąbelkowe - Wersja nr 2
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0005.php 31 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
//--------------------------------------------------------
program Bubble_Sort_2;
const N = 20; // Liczebność zbioru.
var
d : array[1..N] of integer;
// Program główny
//---------------
var
i,j,x : integer;
begin
writeln(' Sortowanie babelkowe ');
writeln(' WERSJA NR 2 ');
writeln('----------------------');
writeln('(C)2005 Jerzy Walaszek');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
// Sortujemy
for j := N - 1 downto 1 do
for i := 1 to j do
if d[i] > d[i+1] then
begin
x := d[i]; d[i] := d[i+1]; d[i+1] := x;
end;
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln('Nacisnij Enter...');
readln;
end.
Code::Blocks
// Sortowanie Bąbelkowe - Wersja nr 2
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
// Program główny
//---------------
int main()
{
int d[N],i,j;
cout << " Sortowanie babelkowe\n"
" WERSJA NR 2\n"
"----------------------\n"
"(C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 100;
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// Sortujemy
for(j = N - 1; j > 0; j--)
for(i = 0; i < j; i++)
if(d[i] > d[i + 1]) swap(d[i], d[i + 1]);
// Wyświetlamy wynik sortowania
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0005.php 32 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
Free Basic
' Sortowanie Bąbelkowe - Wersja nr 2
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 20 ' Liczebność zbioru.
Dim d(1 To N) As Integer, i As Integer, j As Integer
Print " Sortowanie babelkowe "
Print " WERSJA NR 2 "
Print "----------------------"
Print "(C)2005 Jerzy Walaszek"
Print
' Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
' a następnie wyświetlamy jej zawartość
Randomize Timer
For i = 1 To N: d(i) = Int(Rnd * 100): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
' Sortujemy
For j = N - 1 To 1 Step -1
For i = 1 To j
If d(i) > d(i+1) Then Swap d(i), d(i+1)
Next
Next
' Wyświetlamy wynik sortowania
Print "Po sortowaniu:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
Print "Nacisnij Enter..."
Sleep
End
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmbubblesort">
<h3 style="text-align: center">Sortowanie Bąbelkowe - wersja nr 2</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Bąbelkowe - wersja nr 2
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
function main()
{
var d = new Array(N);
var i,j,x,t;
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0005.php 33 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
Sortuj
...
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0005.php 34 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
// Program główny
//---------------
var
i,j,k : integer;
tpo,tod,tpp,tpk,tnp : extended;
f : Text;
begin
if QueryPerformanceFrequency(addr(qpf)) then
begin
QueryPerformanceCounter(addr(qpc1));
QueryPerformanceCounter(addr(qpc2));
tqpc := qpc2 - qpc1;
assignfile(f,'wyniki.txt'); rewrite(f);
// Wydruk na ekran
writeln('Nazwa: ',NAZWA);
writeln(K1);
writeln(K2);
writeln;
writeln(K3);
// Wydruk do pliku
writeln(f,'Nazwa: ',NAZWA);
writeln(f,K1);
writeln(f,K2);
writeln(f,'');
writeln(f,K3);
for i := 1 to MAX_LN do
begin
n := LN[i];
// Czas sortowania zbioru posortowanego
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na początku - średnia z 10 obiegów
tpp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[1] := random * n + 1;
tpp += Sort;
end;
tpp /= 10;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na końcu - średnia z 10 obiegów
tpk := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[n] := random * n + 1;
tpk += Sort;
end;
tpk /= 10;
// Czas sortowania zbioru nieuporządkowanego - średnia z 10 obiegów
tnp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := random;
tnp += Sort;
end;
tnp /= 10;
writeln(n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
writeln(f,n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
end;
writeln(K4);
writeln(f,K4);
writeln(f,'Koniec');
closefile(f);
writeln;
writeln('Koniec. Wyniki w pliku WYNIKI.TXT');
end
else writeln('Na tym komputerze program testowy nie pracuje !');
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0005.php 35 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu sortowania bąbelkowego 2
wyciągamy następujące wnioski:
Cechy Algorytmu Sortowania Bąbelkowego
wersja nr 2
klasa złożoności obliczeniowej optymistyczna O(n2)
optymistyczna - dla zbiorów uporządkowanych (z niewielką liczbą elementów nie na swoich miejscach) - na podstawie
czasów tpo, tpp, tpk
typowa - dla zbiorów o losowym rozkładzie elementów - na podstawie czasu tnp
pesymistyczna - dla zbiorów posortowanych odwrotnie - na podstawie czasu tod.
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
Sortowanie bąbelkowe 1 13 11 7
≈ ≈ ≈2 ≈2 ≈
Sortowanie bąbelkowe 2
6 10 6
dobrze niewiele dobrze dobrze niewiele
3. Analizując wzrost prędkości algorytmu sortowania bąbelkowego w wersji 2 w stosunku do wersji 1 zauważamy, iż
wprowadzona zmiana faktycznie zwiększyła ogólną prędkość działania. Jednakże korzyść w typowym przypadku zbioru
nieuporządkowanego jest stosunkowo niewielka. Największy wzrost prędkości notujemy przy sortowaniu zbiorów częściowo
uporządkowanych.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0005.php 36 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
4. Uzasadnij wyniki otrzymane przy badaniu wzrostu prędkości algorytmu sortowania bąbelkowego w wersji 2 w stosunku do wersji 1.
5. Dokonaj analogicznego porównania wzrostu prędkości tego algorytmu w stosunku do algorytmu sortowania głupiego.
Temat:
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0005.php 37 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
Algorytm
Algorytm sortowania bąbelkowego wykonuje dwa rodzaje operacji:
- test bez zamiany miejsc elementów
- test ze zamianą miejsc elementów.
Pierwsza z tych operacji nie sortuje zbioru, jest więc operacją pustą. Druga operacja
dokonuje faktycznej zmiany porządku elementów, jest zatem operacją sortującą.
Ze względu na przyjęty sposób sortowania algorytm bąbelkowy zawsze musi wykonać tyle
samo operacji sortujących. Tego nie możemy zmienić. Jednakże możemy wpłynąć na
eliminację operacji pustych. W ten sposób usprawnimy działanie algorytmu.
Jeśli dokładnie przyjrzałeś się wersjom 1 i 2, które prezentowaliśmy w poprzednich
rozdziałach, to powinieneś dokonać następujących spostrzeżeń:
1. Wersja pierwsza jest najmniej optymalną wersją algorytmu bąbelkowego. Wykonywane są wszystkie możliwe operacje
sortujące i puste.
2. Wersja druga redukuje ilość operacji pustych poprzez ograniczanie liczby obiegów pętli wewnętrznej (sortującej).
Możliwa jest dalsza redukcja operacji pustych, jeśli będziemy sprawdzać, czy w pętli wewnętrznej były przestawiane elementy
(czyli czy wykonano operacje sortujące). Jeśli nie, to zbiór jest już posortowany (dlaczego?) i możemy zakończyć pracę
algorytmu.
Teraz rośnie trudność wyznaczenia czasowej złożoności obliczeniowej, ponieważ ilość faktycznie wykonywanych operacji
porównań i przestawień zależy od rozkładu elementów w sortowanym zbiorze. Zadanie komplikuje dodatkowo fakt, iż operacja
pusta jest zwykle wykonywana kilkakrotnie szybciej od operacji sortującej. Na pewno można powiedzieć, iż dla zbioru
posortowanego algorytm wykona tylko n - 1 operacji pustych, zatem w przypadku najbardziej optymistycznym czasowa złożoność
obliczeniowa redukuje się do klasy O(n) - liniowej. W przypadku najbardziej niekorzystnym algorytm wykona wszystkie operacje
puste i sortujące, zatem będzie posiadał klasę czasowej złożoności obliczeniowej O(n2).
Przykład:
Posortujmy zbiór { 3 1 0 7 9 } zgodnie z wprowadzoną modyfikacją.
0 1 3 7 9 Koniec trzeciego obiegu. Nie było przestawień elementów, kończymy sortowanie. Wykonaliśmy o 1 obieg
sortujący mniej.
Specyfikacja problemu
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0006.php 38 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n.
Dane wyjściowe
d[ ] - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n.
Zmienne pomocnicze
i, j - zmienne sterujące pętli, i, j Î N
p - znacznik zamiany miejsc elementów w zbiorze. p Î N
Lista kroków
K01: Dla j = n - 1, n - 2, ..., 1: wykonuj K02...K04
K02: p←1
K03: Dla i = 1, 2, ..., j: jeśli d[i] > d[i + 1], to d[i] ↔ d[i + 1]; p ← 0
K04: Jeśli p = 1, to zakończ
K05: Zakończ
Schemat blokowy
Wprowadzona do algorytmu sortowania bąbelkowego modyfikacja ma na celu
wykrycie posortowania zbioru. Zmiany zaznaczyliśmy blokami o odmiennym
kolorze.
Zbiór będzie posortowany, jeśli po wykonaniu wewnętrznego obiegu sortującego nie
wystąpi ani jedno przestawienie elementów porządkowanego zbioru.
Przed wejściem do pętli sortującej nr 2 ustawiamy zmienną pomocniczą p. Jeśli w
pętli zajdzie potrzeba przestawienia elementów, to zmienna p jest zerowana. Po
wykonaniu pętli sortującej sprawdzamy, czy zmienna p jest ustawiona. Jeśli tak, to
przestawienie elementów nie wystąpiło, zatem kończymy algorytm. W przeciwnym
razie wykonujemy kolejny obieg pętli nr 1.
Uwaga techniczna:
Zmienna p powinna być typu liczbowego integer, a nie boolean
(chociaż tak może wydawać się właściwiej). Jednakże musimy
pamiętać, iż procesory Pentium są zoptymalizowane na liczby
32-bitowe. Standardowy typ boolean (w C++ bool) jest daną
8-bitową, która wydłuża wykonanie programu, ponieważ dostęp do
takich danych zajmuje procesorowi Pentium o wiele więcej czasu
niż dostęp do danych 32-bitowych.
Programy
Efekt uruchomienia programu
Sortowanie babelkowe
WERSJA NR 3
----------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
31 17 26 0 42 61 24 89 56 72 92 66 91 13 74 88 10 90 68 25
Po sortowaniu:
0 10 13 17 24 25 26 31 42 56 61 66 68 72 74 88 89 90 91 92
DevPascal
// Sortowanie Bąbelkowe - Wersja nr 3
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program Bubble_Sort_3;
const N = 20; // Liczebność zbioru.
var
d : array[1..N] of integer;
// Program główny
//---------------
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0006.php 39 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
var
i,j,p,x : integer;
begin
writeln(' Sortowanie babelkowe ');
writeln(' WERSJA NR 3 ');
writeln('----------------------');
writeln('(C)2005 Jerzy Walaszek');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
// Sortujemy
for j := N - 1 downto 1 do
begin
p := 1;
for i := 1 to j do
if d[i] > d[i+1] then
begin
x := d[i]; d[i] := d[i+1]; d[i+1] := x;
p := 0;
end;
if p = 1 then break;
end;
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln('Nacisnij Enter...');
readln;
end.
Code::Blocks
// Sortowanie Bąbelkowe - Wersja nr 3
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
// Program główny
//---------------
int main()
{
int d[N],i,j,p;
cout << " Sortowanie babelkowe\n"
" WERSJA NR 3\n"
"----------------------\n"
"(C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 100;
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// Sortujemy
for(j = N - 1; j > 0; j--)
{
p = 1;
for(i = 0; i < j; i++)
if(d[i] > d[i + 1])
{
swap(d[i], d[i + 1]);
p = 0;
}
if(p) break;
}
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0006.php 40 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
Free Basic
' Sortowanie Bąbelkowe - Wersja nr 3
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 20 ' Liczebność zbioru.
Dim d(1 To N) As Integer, i As Integer, j As Integer, p As Integer
Print " Sortowanie babelkowe "
Print " WERSJA NR 3 "
Print "----------------------"
Print "(C)2005 Jerzy Walaszek"
Print
' Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
' a następnie wyświetlamy jej zawartość
Randomize Timer
For i = 1 To N: d(i) = Int(Rnd * 100): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
' Sortujemy
For j = N - 1 To 1 Step -1
p = 1
For i = 1 To j
If d(i) > d(i+1) Then
Swap d(i), d(i+1)
p = 0
End If
Next
If p = 1 Then Exit For
Next
' Wyświetlamy wynik sortowania
Print "Po sortowaniu:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
Print "Nacisnij Enter..."
Sleep
End
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmbubblesort">
<h3 style="text-align: center">Sortowanie Bąbelkowe - wersja nr 3</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Bąbelkowe - wersja nr 3
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0006.php 41 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
Sortuj
...
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0006.php 42 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
begin
QueryPerformanceCounter(addr(qpc1));
for j := n - 1 downto 1 do
begin
p := 1;
for i := 1 to j do
if d[i] > d[i+1] then
begin
x := d[i]; d[i] := d[i+1]; d[i+1] := x;
p := 0;
end;
if p = 1 then break;
end;
QueryPerformanceCounter(addr(qpc2));
Sort := (qpc2 - qpc1 - tqpc) / qpf;
end;
// Program główny
//---------------
var
i,j,k : integer;
tpo,tod,tpp,tpk,tnp : extended;
f : Text;
begin
if QueryPerformanceFrequency(addr(qpf)) then
begin
QueryPerformanceCounter(addr(qpc1));
QueryPerformanceCounter(addr(qpc2));
tqpc := qpc2 - qpc1;
assignfile(f,'wyniki.txt'); rewrite(f);
// Wydruk na ekran
writeln('Nazwa: ',NAZWA);
writeln(K1);
writeln(K2);
writeln;
writeln(K3);
// Wydruk do pliku
writeln(f,'Nazwa: ',NAZWA);
writeln(f,K1);
writeln(f,K2);
writeln(f,'');
writeln(f,K3);
for i := 1 to MAX_LN do
begin
n := LN[i];
// Czas sortowania zbioru posortowanego
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na początku - średnia z 10 obiegów
tpp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[1] := random * n + 1;
tpp += Sort;
end;
tpp /= 10;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na końcu - średnia z 10 obiegów
tpk := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[n] := random * n + 1;
tpk += Sort;
end;
tpk /= 10;
// Czas sortowania zbioru nieuporządkowanego - średnia z 10 obiegów
tnp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := random;
tnp += Sort;
end;
tnp /= 10;
writeln(n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
writeln(f,n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0006.php 43 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
end;
writeln(K4);
writeln(f,K4);
writeln(f,'Koniec');
closefile(f);
writeln;
writeln('Koniec. Wyniki w pliku WYNIKI.TXT');
end
else writeln('Na tym komputerze program testowy nie pracuje !');
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika):
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu sortowania bąbelkowego 3
wyciągamy następujące wnioski:
Cechy Algorytmu Sortowania Bąbelkowego
wersja nr 3
klasa złożoności obliczeniowej optymistyczna O(n) -O(n2)
optymistyczna - dla zbiorów uporządkowanych (z niewielką liczbą elementów nie na swoich miejscach) - na podstawie
czasów tpo, tpp, tpk
typowa - dla zbiorów o losowym rozkładzie elementów - na podstawie czasu tnp
pesymistyczna - dla zbiorów posortowanych odwrotnie - na podstawie czasu tod.
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
O(n) O(n2) O(n) O(n2) O(n2)
Sortowanie bąbelkowe
wersja nr 3
tpo << tod tpp << tpk tnp ≈ 3tod
5
1. Wprowadzona zmiana wpłynęła na zmianę klasy czasowej złożoności obliczeniowej przy sortowaniu zbioru
uporządkowanego oraz przy sortowaniu zbioru uporządkowanego z losowym elementem na początku. W obu przypadkach
notujemy proporcjonalność czasu sortowania do liczby elementów w zbiorze, zatem klasa czasowej złożoności
obliczeniowej wynosi O(n).
2. Nie zmieniła się natomiast klasa czasowej złożoności obliczeniowej przy sortowaniu zbioru uporządkowanego odwrotnie,
przy sortowaniu zbioru uporządkowanego z losowym elementem na końcu oraz przy sortowaniu zbioru
nieuporządkowanego. Dlatego w przypadku ogólnym klasa czasowej złożoności obliczeniowej wynosi O(n2).
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0006.php 44 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe 2014-10-03
3. Analizując wzrost prędkości algorytmu sortowania bąbelkowego w wersji 3 w stosunku do wersji 2 zauważamy, iż
wprowadzona zmiana nie zwiększyła prędkości działania algorytmu dla przypadku zbioru posortowanego odwrotnie oraz dla
zbioru nieuporządkowanego. Największy zysk (zmniejszenie klasy czasowej złożoności obliczeniowej) występuje przy
sortowaniu zbiorów częściowo uporządkowanych.
Temat:
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0006.php 45 / 209
Algorytmy Sortujące - Zaotymalizowane Sortowanie Bąbelkowe 2014-10-03
Algorytm
Czy algorytm sortowania bąbelkowego można jeszcze ulepszyć? Tak,
ale zaczynamy już osiągać kres jego możliwości, ponieważ ulepszenia
polegają jedynie na redukcji operacji pustych. Wykorzystamy informację
o miejscu wystąpienia zamiany elementów (czyli o miejscu wykonania
operacji sortującej).
Jeśli w obiegu sortującym wystąpi pierwsza zamiana na pozycji i-tej, to
w kolejnym obiegu będziemy rozpoczynali sortowanie od pozycji o jeden
mniejszej (chyba że pozycja i-ta była pierwszą pozycją w zbiorze).
Dlaczego? Odpowiedź jest prosta. Zamiana spowodowała, iż młodszy
element znalazł się na pozycji i-tej. Ponieważ w obiegu sortującym
młodszy element zawsze przesuwa się o 1 pozycję w kierunku początku
zbioru, to nie ma sensu sprawdzanie pozycji od 1 do i-2, ponieważ w
poprzednim obiegu zostały one już sprawdzone, nie wystąpiła na nich zamiana elementów, zatem elementy na pozycjach od 1 do
i-2 są chwilowo w dobrej kolejności względem siebie. Nie mamy tylko pewności co do pozycji i-1-szej oraz i-tej, ponieważ ostatnia
zamiana elementów umieściła na i-tej pozycji młodszy element, który być może należy wymienić z elementem na pozycji
wcześniejszej, czyli i-1. W ten sposób określimy początkową pozycję, od której rozpoczniemy sortowanie elementów w
następnym obiegu sortującym.
Ostatnia zamiana elementów wyznaczy pozycję końcową dla następnego obiegu. Wiemy, iż w każdym obiegu sortującym
najstarszy element jest zawsze umieszczany na swojej docelowej pozycji. Jeśli ostatnia zamiana elementów wystąpiła na pozycji
i-tej, to w następnym obiegu porównywanie elementów zakończymy na pozycji o 1 mniejszej - w ten sposób nie będziemy
sprawdzać już najstarszego elementu z poprzedniego obiegu.
Sortowanie prowadzimy dotąd, aż w obiegu sortującym nie wystąpi ani jedna zamiana elementów.
Teoretycznie powinno to zoptymalizować algorytm, ponieważ są sortowane tylko niezbędne fragmenty zbioru - pomijamy obszary
posortowane, które tworzą się na końcu i na początku zbioru. Oczywiście zysk nie będzie oszałamiający w przypadku zbioru
nieuporządkowanego lub posortowanego odwrotnie (może się zdarzyć, iż ewentualne korzyści czasowe będą mniejsze od czasu
wykonywania dodatkowych operacji). Jednakże dla zbiorów w dużym stopniu uporządkowanych możemy uzyskać całkiem
rozsądny algorytm sortujący prawie w czasie liniowym O(n).
Przykład:
Według opisanej powyżej metody posortujmy zbiór { 0 1 2 3 5 4 7 9 }. W zbiorze tym są tylko dwa elementy
nieuporządkowane - 5 i 4.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0007.php 46 / 209
Algorytmy Sortujące - Zaotymalizowane Sortowanie Bąbelkowe 2014-10-03
2
pozycja ostatniej zamiany w poprzednim obiegu.
0 1 2 3 4 5 7 9 Koniec, zbiór jest posortowany
Chociaż podany przykład jest troszeczkę tendencyjny, to jednak pokazuje wyraźnie, iż zoptymalizowany algorytm sortowania
bąbelkowego może bardzo szybko posortować zbiory prawie uporządkowane.
Specyfikacja problemu
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n.
Dane wyjściowe
d[ ] - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n.
Zmienne pomocnicze
i - zmienna sterująca pętli, i Î N
pmin - dolna granica pozycji sortowanych elementów, pmin Î N
pmax - górna granica pozycji sortowanych elementów, pmax Î N
p - numer pozycji zamiany elementów, p Î N
Lista kroków
K01: pmin ← 1; pmax ← n - 1
K02 p ← 0
K03: Dla i = pmin, ..., pmax: wykonuj K04...K07
K04: Jeśli d[i] ≤ d[i + 1], to następny obieg pętli K03
K05: d[i] ↔ d[i + 1]
K06: Jeśli p = 0, to pmin ← i
K07: p←i
K08: Jeśli pmin > 1, to pmin ← pmin - 1
K09: pmax ← p - 1
K10: Jeśli p > 0, to idź do K02
K11: Zakończ
Schemat blokowy
Tym razem wprowadzonych zmian do algorytmu sortowania bąbelkowego jest
dużo, zatem opiszemy cały algorytm od początku.
Zmienna pmin przechowuje numer pozycji, od której rozpoczyna się sortowanie
zbioru. W pierwszym obiegu sortującym rozpoczynamy od pozycji nr 1. Zmienna
pmax przechowuje numer ostatniej pozycji do sortowania. Pierwszy obieg sortujący
kończymy na pozycji n-1, czyli na przedostatniej.
Pętla numer 1 wykonywana jest dotąd, aż w wewnętrznej pętli nr 2 nie wystąpi
żadna zamiana elementów. Zmienna p pełni w tej wersji algorytmu nieco inną rolę
niż poprzednio. Mianowicie będzie przechowywała numer pozycji, na której
algorytm ostatnio dokonał wymiany elementów. Na początku wpisujemy do p
wartość 0, która nie oznacza żadnej pozycji w zbiorze. Zatem jeśli ta wartość
zostanie zachowana, uzyskamy pewność, iż zbiór jest posortowany, ponieważ nie
dokonano wymiany elementów.
Wewnętrzną pętlę sortującą rozpoczynamy od pozycji pmin. W pętli sprawdzamy
kolejność elementu i-tego z elementem następnym. Jeśli kolejność jest zła,
wymieniamy miejscami te dwa elementy. Po wymianie sprawdzamy, czy jest to
pierwsza wymiana - zmienna p ma wtedy wartość 0. Jeśli tak, to numer pozycji, na
której dokonano wymiany umieszczamy w pmin. Numer ten zapamiętujemy
również w zmiennej p. Zwróć uwagę, iż dzięki takiemu podejściu p zawsze będzie
przechowywało numer pozycji ostatniej wymiany - jest to zasada zwana "ostatni
zwycięża".
Po sprawdzeniu elementów przechodzimy do następnej pozycji zwiększając i o 1 i
kontynuujemy pętlę, aż do przekroczenia pozycji pmax. Wtedy pętla wewnętrzna
zakończy się.
Jeśli w pętli nr 2 była dokonana zamiana elementów, to pmin zawiera numer pozycji pierwszej zamiany. Jeśli nie jest
to pierwsza pozycja w zbiorze, pmin zmniejszamy o 1, aby pętla sortująca rozpoczynała od pozycji poprzedniej w
stosunku do pozycji pierwszej zamiany elementów.
Pozycję ostatnią zawsze ustalamy o 1 mniejszą od numeru pozycji końcowej zamiany elementów.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0007.php 47 / 209
Algorytmy Sortujące - Zaotymalizowane Sortowanie Bąbelkowe 2014-10-03
Na koniec sprawdzamy, czy faktycznie doszło do zamiany elementów. Jeśli tak, t o p jest większe od 0, gdyż
zawiera numer pozycji w zbiorze, na której algorytm wymienił miejscami elementy. W takim przypadku pętlę nr 1
rozpoczynamy od początku. W przeciwnym razie kończymy, zbiór jest uporządkowany.
Programy
Efekt uruchomienia programu
Sortowanie babelkowe
WERSJA NR 4
----------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
18 5 9 51 20 65 75 32 84 98 77 42 0 30 5 44 97 77 31 12
Po sortowaniu:
0 5 5 9 12 18 20 30 31 32 42 44 51 65 75 77 77 84 97 98
DevPascal
// Sortowanie Bąbelkowe - Wersja nr 4
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program Bubble_Sort_4;
const N = 20; // Liczebność zbioru.
var
d : array[1..N] of integer;
// Program główny
//---------------
var
i,p,pmin,pmax,x : integer;
begin
writeln(' Sortowanie babelkowe ');
writeln(' WERSJA NR 4 ');
writeln('----------------------');
writeln('(C)2005 Jerzy Walaszek');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
// Sortujemy
pmin := 1; pmax := N - 1;
repeat
p := 0;
for i := pmin to pmax do
if d[i] > d[i+1] then
begin
x := d[i]; d[i] := d[i+1]; d[i+1] := x;
if p = 0 then pmin := i;
p := i;
end;
if pmin > 1 then dec(pmin);
pmax := p - 1;
until p = 0;
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln('Nacisnij Enter...');
readln;
end.
Code::Blocks
// Sortowanie Bąbelkowe - Wersja nr 4
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0007.php 48 / 209
Algorytmy Sortujące - Zaotymalizowane Sortowanie Bąbelkowe 2014-10-03
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
// Program główny
//---------------
int main()
{
int d[N],i,p,pmin,pmax;
cout << " Sortowanie babelkowe\n"
" WERSJA NR 4\n"
"----------------------\n"
"(C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 100;
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// Sortujemy
pmin = 0; pmax = N - 1;
do
{
p = -1;
for(i = pmin; i < pmax; i++)
if(d[i] > d[i+1])
{
swap(d[i], d[i+1]);
if(p < 0) pmin = i;
p = i;
}
if(pmin) pmin--;
pmax = p;
} while(p >= 0);
// Wyświetlamy wynik sortowania
cout << "Po sortowaniu:\n\n";
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
return 0;
}
Free Basic
' Sortowanie Bąbelkowe - Wersja nr 4
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 20 ' Liczebność zbioru.
Dim d(1 To N) As Integer
Dim i As Integer, p As Integer, pmin As Integer, pmax As Integer
Print " Sortowanie babelkowe "
Print " WERSJA NR 4 "
Print "----------------------"
Print "(C)2005 Jerzy Walaszek"
Print
' Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
' a następnie wyświetlamy jej zawartość
Randomize Timer
For i = 1 To N: d(i) = Int(Rnd * 100): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
' Sortujemy
pmin = 1: pmax = N - 1
Do
p = 0
For i = pmin To pmax
If d(i) > d(i+1) Then
Swap d(i), d(i+1)
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0007.php 49 / 209
Algorytmy Sortujące - Zaotymalizowane Sortowanie Bąbelkowe 2014-10-03
If p = 0 Then pmin = i
p = i
End If
Next
If pmin > 1 Then pmin = pmin - 1
pmax = p - 1
Loop Until p = 0
' Wyświetlamy wynik sortowania
Print "Po sortowaniu:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
Print "Nacisnij Enter..."
Sleep
End
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmbubblesort">
<h3 style="text-align: center">Sortowanie Bąbelkowe - wersja nr 4</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Bąbelkowe - wersja nr 4
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
function main()
{
var d = new Array(N);
var i,p,pmin,pmax,x,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 100);
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Sortujemy
pmin = 0; pmax = N - 1;
do
{
p = -1;
for(i = pmin; i < pmax; i++)
if(d[i] > d[i+1])
{
x = d[i]; d[i] = d[i+1]; d[i+1] = x;
if(p < 0) pmin = i;
p = i;
};
if(pmin) pmin--;
pmax = p;
} while(p >= 0);
// Wyświetlamy wynik sortowania
t += "Po sortowaniu:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0007.php 50 / 209
Algorytmy Sortujące - Zaotymalizowane Sortowanie Bąbelkowe 2014-10-03
Sortuj
...
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0007.php 51 / 209
Algorytmy Sortujące - Zaotymalizowane Sortowanie Bąbelkowe 2014-10-03
writeln(f,'Nazwa: ',NAZWA);
writeln(f,K1);
writeln(f,K2);
writeln(f,'');
writeln(f,K3);
for i := 1 to MAX_LN do
begin
n := LN[i];
// Czas sortowania zbioru posortowanego
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na początku - średnia z 10 obiegów
tpp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[1] := random * n + 1;
tpp += Sort;
end;
tpp /= 10;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na końcu - średnia z 10 obiegów
tpk := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[n] := random * n + 1;
tpk += Sort;
end;
tpk /= 10;
// Czas sortowania zbioru nieuporządkowanego - średnia z 10 obiegów
tnp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := random;
tnp += Sort;
end;
tnp /= 10;
writeln(n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
writeln(f,n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
end;
writeln(K4);
writeln(f,K4);
writeln(f,'Koniec');
closefile(f);
writeln;
writeln('Koniec. Wyniki w pliku WYNIKI.TXT');
end
else writeln('Na tym komputerze program testowy nie pracuje !');
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika):
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0007.php 52 / 209
Algorytmy Sortujące - Zaotymalizowane Sortowanie Bąbelkowe 2014-10-03
tpp
tpk - czas sortowania zbioru posortowanego z losowym elementem na końcu
tnp- czas sortowania zbioru z losowym rozkładem elementów
(Arkusz kalkulacyjny Excel do wyznaczania klasy czasowej złożoności obliczeniowej)
(Arkusz kalkulacyjny Excel do wyznaczania wzrostu prędkości sortowania)
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu sortowania bąbelkowego 4
wyciągamy następujące wnioski:
optymistyczna - dla zbiorów uporządkowanych (z niewielką liczbą elementów nie na swoich miejscach) - na podstawie
czasów tpo, tpp, tpk
typowa - dla zbiorów o losowym rozkładzie elementów - na podstawie czasu tnp
pesymistyczna - dla zbiorów posortowanych odwrotnie - na podstawie czasu tod.
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
O(n) O(n2) O(n) O(n) O(n2)
Sortowanie bąbelkowe
wersja nr 4
tpo << tod tpp ≈ 5tpk tnp ≈ 3tod
3 5
1. Wprowadzona zmiana wpłynęła na zmianę klasy czasowej złożoności obliczeniowej przy sortowaniu zbioru
uporządkowanego z losowym elementem na końcu z O(n2) na O(n).
2. Nie zmieniła się natomiast klasa czasowej złożoności obliczeniowej przy sortowaniu zbioru uporządkowanego odwrotnie i
przy sortowaniu zbioru nieuporządkowanego. Dlatego w przypadku ogólnym klasa czasowej złożoności obliczeniowej
wynosi O(n2).
3. Z wyników testu wyciągamy wniosek, iż ta wersja algorytmu sortowania bąbelkowego jest najlepszą ze wszystkich
przedstawionych tutaj wersji. Dlatego dalsze algorytmy sortujące będziemy porównywali do wyników zoptymalizowanego
algorytmu sortowania bąbelkowego.
3. Analizując wzrost prędkości algorytmu sortowania bąbelkowego w wersji 4 w stosunku do wersji 3 zauważamy, iż
wprowadzona zmiana nie zwiększyła w istotny sposób prędkości działania algorytmu w przypadku ogólnym. Zysk jest tylko
przy sortowaniu zbiorów w dużym stopniu uporządkowanych. W takich przypadkach zoptymalizowany algorytm sortowania
bąbelkowego posiada liniową czasową złożoność obliczeniową.
Temat:
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0007.php 53 / 209
Algorytmy Sortujące - Zaotymalizowane Sortowanie Bąbelkowe 2014-10-03
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0007.php 54 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe Dwukierunkowe 2014-10-03
Algorytm
Dwukierunkowe sortowanie bąbelkowe oparte jest na
spostrzeżeniu, iż każdy obieg wewnętrznej pętli sortującej
umieszcza na właściwym miejscu element najstarszy, a
elementy młodsze przesuwa o 1 pozycję w kierunku początku
zbioru. Jeśli pętla ta zostanie wykonana w kierunku odwrotnym,
to wtedy najmłodszy element znajdzie się na swoim właściwym
miejscu, a elementy starszy przesuną się o jedną pozycję w
kierunku końca zbioru. Połączmy te dwie pętle sortując
wewnętrznie naprzemiennie w kierunku normalnym i odwrotnym,
a otrzymamy algorytm dwukierunkowego sortowania
bąbelkowego.
Wykonanie pętli sortującej w normalnym kierunku ustali
maksymalną pozycję w zbiorze, od której powinna rozpocząć sortowanie pętla odwrotna. Ta z kolei ustali minimalną pozycję w
zbiorze, od której powinna rozpocząć sortowanie pętla normalna w następnym obiegu pętli zewnętrznej. Sortowanie możemy
zakończyć, jeśli nie wystąpiła potrzeba zamiany elementów w żadnej z tych dwóch pętli.
Specyfikacja problemu
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n.
Dane wyjściowe
d[ ] - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n.
Zmienne pomocnicze
i - zmienna sterująca pętli, i Î N
pmin - dolna granica pozycji sortowanych elementów, pmin Î N
pmax - górna granica pozycji sortowanych elementów, pmax Î N
p - numer pozycji zamiany elementów, p Î N
Lista kroków
Operacja sortująca
K01: Jeśli d[i] ≤ d[i + 1], to zakończ operację sortującą
K02: d[i] ↔ d[i + 1]
K03: p←i
K04: Zakończ operację sortującą
Algorytm główny
K01: pmin ← 1; pmax ← n - 1
K02: p ← 0
K03: Dla i = pmin, pmin + 1, ..., pmax: wykonuj operację sortującą
K04: Jeśli p = 0, to zakończ
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0008.php 55 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe Dwukierunkowe 2014-10-03
K05: pmax ← p - 1; p ← 0
K06: Dla i = pmax, pmax - 1, ..., pmin: wykonuj operację sortującą
K07: pmin ← p + 1
K08: Jeśli p > 0, to idź do K02
K09: Zakończ
Schemat blokowy
W algorytmie wydzieliliśmy powtarzający się fragment operacji i nazwaliśmy go
operacją sortującą. Porównuje ona dwa kolejne elementy zbioru i zamienia je
miejscami, jeśli są w złej kolejności. Po zamianie do zmiennej p trafia indeks
pierwszego z elementów pary. Podany warunek sprawdza uporządkowanie
rosnące. Jeśli chcemy posortować zbiór malejąco, relację większości należy
zastąpić relacją mniejszości.
W algorytmie występują trzy pętle. Pętla nr 1 jest pętlą warunkową i obejmuje
dwie pętle wewnętrzne nr 2 i nr 3. Pętla ta wykonywana jest dotąd, aż w
sortowanym zbiorze nie wystąpi w trakcie sortowania ani jedna zamiana miejsc
elementów.
Pętla nr 2 jest pętlą sortującą w górę. Pętla nr 3 sortuje w dół.
Na początku algorytmu ustalamy dwie granice sortowania:
- dolną w pmin
- górną w pmax.
Programy
Efekt uruchomienia programu
Dwukierunkowe Sortowanie babelkowe
----------------------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
22 67 9 15 4 3 21 40 66 83 89 38 31 42 92 55 3 87 48 50
Po sortowaniu:
3 3 4 9 15 21 22 31 38 40 42 48 50 55 66 67 83 87 89 92
DevPascal
// Dwukierunkowe Sortowanie Bąbelkowe
//-----------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program Bidirectional_Bubble_Sort;
const N = 20; // Liczebność zbioru.
var
d : array[1..N] of integer;
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0008.php 56 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe Dwukierunkowe 2014-10-03
// Program główny
//---------------
var
i,p,pmin,pmax,x: integer;
begin
writeln('Dwukierunkowe sortowanie babelkowe');
writeln('----------------------------------');
writeln(' (C)2005 Jerzy Walaszek');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
// Sortujemy
pmin := 1; pmax := N - 1;
repeat
p := 0;
for i := pmin to pmax do
if d[i] > d[i + 1] then
begin
x := d[i]; d[i] := d[i+1]; d[i+1] := x;
p := i;
end;
if p = 0 then break;
pmax := p - 1;
p := 0;
for i := pmax downto pmin do
if d[i] > d[i + 1] then
begin
x := d[i]; d[i] := d[i+1]; d[i+1] := x;
p := i;
end;
pmin := p + 1;
until p = 0;
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln('Nacisnij Enter...');
readln;
end.
Code::Blocks
// Dwukierunkowe Sortowanie Bąbelkowe
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
// Program główny
//---------------
int main()
{
int d[N],i,pmin,pmax,p;
cout << "Dwukierunkowe Sortowanie babelkowe\n"
"----------------------------------\n"
" (C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 100;
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// Sortujemy
pmin = 0; pmax = N - 2;
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0008.php 57 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe Dwukierunkowe 2014-10-03
do
{
p = -1;
for(i = pmin; i <= pmax; i++)
if(d[i] > d[i + 1])
{
swap(d[i], d[i + 1]);
p = i;
}
if(p < 0) break;
pmax = p - 1;
p = -1;
for(i = pmax; i >= pmin; i--)
if(d[i] > d[i + 1])
{
swap(d[i], d[i + 1]);
p = i;
}
pmin = p + 1;
} while(p >= 0);
// Wyświetlamy wynik sortowania
cout << "Po sortowaniu:\n\n";
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
return 0;
}
Free Basic
' Dwukierunkowe Sortowanie Bąbelkowe
'-----------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 20 ' Liczebność zbioru.
Dim d(1 To N) As Integer
Dim i As Integer, p As Integer, pmin As Integer, pmax As Integer
Print "Dwukierunkowe sortowanie babelkowe"
Print "----------------------------------"
Print " (C)2005 Jerzy Walaszek"
Print
' Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
' a następnie wyświetlamy jej zawartość
Randomize Timer
For i = 1 To N: d(i) = Int(Rnd * 100): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
' Sortujemy
pmin = 1: pmax = N - 1
Do
p = 0
For i = pmin To pmax
If d(i) > d(i + 1) Then
Swap d(i), d(i + 1)
p = i
End If
Next
If p = 0 Then Exit Do
pmax = p - 1
p = 0
For i = pmax To pmin Step -1
If d(i) > d(i + 1) Then
Swap d(i), d(i + 1)
p = i
End If
Next
pmin = p + 1
Loop Until p = 0
' Wyświetlamy wynik sortowania
Print "Po sortowaniu:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
Print "Nacisnij Enter...";
Sleep
End
JavaScript
<html>
<head>
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0008.php 58 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe Dwukierunkowe 2014-10-03
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmbubblesort">
<h3 style="text-align: center">Sortowanie Bąbelkowe - wersja nr 3</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Bąbelkowe - wersja nr 3
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
function main()
{
var d = new Array(N);
var i,j,pmin,pmax,p,x,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 100);
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Sortujemy
pmin = 0; pmax = N - 2;
do
{
p = -1;
for(i = pmin; i <= pmax; i++)
if(d[i] > d[i + 1])
{
x = d[i]; d[i] = d[i+1]; d[i+1] = x;
p = i;
}
if(p < 0) break;
pmax = p - 1;
p = -1;
for(i = pmax; i >= pmin; i--)
if(d[i] > d[i + 1])
{
x = d[i]; d[i] = d[i+1]; d[i+1] = x;
p = i;
}
pmin = p + 1;
} while(p >= 0);
// Wyświetlamy wynik sortowania
t += "Po sortowaniu:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Sortuj
...
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0008.php 59 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe Dwukierunkowe 2014-10-03
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0008.php 60 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe Dwukierunkowe 2014-10-03
n := LN[i];
// Czas sortowania zbioru posortowanego
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na początku - średnia z 10 obiegów
tpp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[1] := random * n + 1;
tpp += Sort;
end;
tpp /= 10;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na końcu - średnia z 10 obiegów
tpk := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[n] := random * n + 1;
tpk += Sort;
end;
tpk /= 10;
// Czas sortowania zbioru nieuporządkowanego - średnia z 10 obiegów
tnp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := random;
tnp += Sort;
end;
tnp /= 10;
writeln(n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
writeln(f,n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
end;
writeln(K4);
writeln(f,K4);
writeln(f,'Koniec');
closefile(f);
writeln;
writeln('Koniec. Wyniki w pliku WYNIKI.TXT');
end
else writeln('Na tym komputerze program testowy nie pracuje !');
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości
czasów wykonania, dlatego w celach porównawczych proponuję uruchomić podany program na komputerze
czytelnika):
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0008.php 61 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe Dwukierunkowe 2014-10-03
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu dwukierunkowego sortowania
bąbelkowego wyciągamy następujące wnioski:
Cechy Algorytmu Dwukierunkowego
Sortowania Bąbelkowego
klasa złożoności obliczeniowej optymistyczna O(n)
klasa złożoności obliczeniowej typowa O(n2)
klasa złożoności obliczeniowej pesymistyczna O(n2)
Sortowanie w miejscu TAK
Stabilność TAK
optymistyczna - dla zbiorów uporządkowanych (z niewielką liczbą elementów nie na swoich miejscach) - na podstawie
czasów tpo, tpp, tpk
typowa - dla zbiorów o losowym rozkładzie elementów - na podstawie czasu tnp
pesymistyczna - dla zbiorów posortowanych odwrotnie - na podstawie czasu tod.
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
O(n) O(n2) O(n) O(n) O(n2)
Dwukierunkowe
sortowanie bąbelkowe
tpo << tod tpp ≈ 9tpk tnp ≈ 3tod
8 5
1. W przypadku zbioru posortowanego odwrotnie oraz zbioru nieuporządkowanego algorytm wykonuje się w czasie
proporcjonalnym do kwadratu liczby elementów, zatem jego typowa czasowa złożoność obliczeniowa jest klasy O(n2).
2. Zbiór nieuporządkowany sortowany jest szybciej od zbioru uporządkowanego odwrotnie. Wnioskujemy zatem, iż zbiór
uporządkowany odwrotnie jest najtrudniejszym zadaniem dla tego algorytmu.
3. Przy sortowaniu zbiorów w znacznym stopniu posortowanych klasa czasowej złożoności obliczeniowej redukuje się do
O(n), jest zatem bardzo korzystna.
4. W porównaniu do zoptymalizowanego algorytmu bąbelkowego w wersji 4 algorytm dwukierunkowego sortowania
bąbelkowego nie wykazuje specjalnie nowych własności. Obserwujemy jedynie wyrównanie czasów sortowania zbioru
uporządkowanego z jednym elementem losowym na początku i na końcu.
Sortowanie bąbelkowe 4 7 7 6
≈1 ≈ ≈1 ≈ ≈
Dwukierunkowe sortowanie bąbelkowe
6 10 5
brak lepiej brak gorzej lepiej
5. Jeśli porównamy prędkości obu algorytmów, to obecna wersja radzi sobie nieco lepiej w przypadku zbiorów
uporządkowanych odwrotnie oraz zbiorów nieuporządkowanych. W pozostałych sytuacjach jest bez zmian, lub nawet
gorzej.
Temat:
Uwaga: ← tutaj wpisz wyraz ilo , inaczej list zostanie zignorowany
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0008.php 62 / 209
Algorytmy Sortujące - Sortowanie Bąbelkowe Dwukierunkowe 2014-10-03
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0008.php 63 / 209
Algorytmy Sortujące - Sortowanie Przez Wybór 2014-10-03
Algorytm
Idea algorytmu sortowania przez wybór jest bardzo prosta.
Załóżmy, iż chcemy posortować zbiór liczbowy rosnąco. Zatem
element najmniejszy powinien znaleźć się na pierwszej pozycji.
Szukamy w zbiorze elementu najmniejszego i wymieniamy go
z elementem na pierwszej pozycji. W ten sposób element
najmniejszy znajdzie się na swojej docelowej pozycji.
W identyczny sposób postępujemy z resztą elementów
należących do zbioru. Znów wyszukujemy element najmniejszy i
zamieniamy go z elementem na drugiej pozycji. Otrzymamy dwa
posortowane elementy. Procedurę kontynuujemy dla pozostałych
elementów dotąd, aż wszystkie będą posortowane.
Algorytm sortowania przez wybór posiada klasę czasowej złożoności obliczeniowej równą O(n2). Sortowanie odbywa się w
miejscu.
Przykład:
Dla przykładu posortujmy tą metodą zbiór {4 7 2 9 3}. Kolorem zielonym oznaczyliśmy elementy zbioru, które są już
posortowane.
Podana metoda sortuje zbiór rosnąco. Jeśli chcemy posortować zbiór malejąco, to zamiast elementu minimalnego poszukujemy
elementu maksymalnego. Pozostała część procedury sortującej nie ulega zmianie.
Specyfikacja problemu
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n.
Dane wyjściowe
d[ ] - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n.
Zmienne pomocnicze
i, j - zmienne sterujące pętli, i, j Î N
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0009.php 64 / 209
Algorytmy Sortujące - Sortowanie Przez Wybór 2014-10-03
Lista kroków
K01: Dla j = 1, 2, ..., n - 1: wykonuj K02...K04
K02: pmin ← j
K03: Dla i = j + 1, j + 2, ..., n: jeśli d[i] < d[pmin], to pmin ← i
K04: d[j] ↔ d[pmin]
K05: Zakończ
Schemat blokowy
Pętla zewnętrzna sterowana zmienną j wyznacza kolejne elementy zbioru o indeksach od 1
do n - 1, w których zostaną umieszczone elementy minimalne. Na początku tej pętli
zakładamy, iż elementem minimalnym jest element d[j] i zapamiętujemy jego indeks w
zmiennej pmin.
W pętli numer 2 sterowanej zmienną i porównujemy pozostałe elementy zbioru z elementem
d[pmin]. Jeśli element zbioru d[i] jest mniejszy od elementu d[pmin], to znaleźliśmy nowy
element minimalny. W takim przypadku zapamiętujemy jego pozycję w pmin i kontynuujemy
pętlę wewnętrzną.
Po zakończeniu pętli wewnętrznej pmin zawiera indeks elementu minimalnego. Zamieniamy
miejscami element d[j] z elementem d[pmin]. Dzięki tej operacji element minimalny znajduje
się na swojej docelowej pozycji. Zwiększamy j przechodząc do kolejnego elementu zbioru i
kontynuujemy pętlę zewnętrzną.
Programy
Efekt uruchomienia programu
Sortowanie przez wybor
------------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
98 49 73 16 64 25 5 83 54 51 80 3 98 26 86 87 80 68 5 11
Po sortowaniu:
3 5 5 11 16 25 26 49 51 54 64 68 73 80 80 83 86 87 98 98
DevPascal
// Sortowanie Przez Wybór
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program Selection_Sort;
const N = 20; // Liczebność zbioru.
var
d : array[1..N] of integer;
// Program główny
//---------------
var
i,j,x,pmin : integer;
begin
writeln(' Sortowanie przez wybor ');
writeln('------------------------');
writeln(' (C)2005 Jerzy Walaszek ');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0009.php 65 / 209
Algorytmy Sortujące - Sortowanie Przez Wybór 2014-10-03
Code::Blocks
// Sortowanie Przez Wybór
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
// Program główny
//---------------
int main()
{
int d[N],i,j,pmin;
cout << " Sortowanie przez wybor\n"
"------------------------\n"
" (C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 100;
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// Sortujemy
for(j = 0; j < N - 1; j++)
{
pmin = j;
for(i = j + 1; i < N; i++)
if(d[i] < d[pmin]) pmin = i;
swap(d[pmin], d[j]);
}
// Wyświetlamy wynik sortowania
cout << "Po sortowaniu:\n\n";
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
return 0;
}
Free Basic
' Sortowanie Przez Wybór
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 20 ' Liczebność zbioru.
Dim d(1 To N) As Integer
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0009.php 66 / 209
Algorytmy Sortujące - Sortowanie Przez Wybór 2014-10-03
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmselectionsort">
<h3 style="text-align: center">Sortowanie Przez Wybór</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Przez Wybór
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
function main()
{
var d = new Array(N);
var i,j,pmin,x,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 100);
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Sortujemy
for(j = 0; j < N - 1; j++)
{
pmin = j;
for(i = j + 1; i < N; i++)
if(d[i] < d[pmin]) pmin = i;
x = d[pmin]; d[pmin] = d[j]; d[j] = x;
}
// Wyświetlamy wynik sortowania
t += "Po sortowaniu:<BR><BR>";
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0009.php 67 / 209
Algorytmy Sortujące - Sortowanie Przez Wybór 2014-10-03
Sortuj
...
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0009.php 68 / 209
Algorytmy Sortujące - Sortowanie Przez Wybór 2014-10-03
writeln('Nazwa: ',NAZWA);
writeln(K1);
writeln(K2);
writeln;
writeln(K3);
// Wydruk do pliku
writeln(f,'Nazwa: ',NAZWA);
writeln(f,K1);
writeln(f,K2);
writeln(f,'');
writeln(f,K3);
for i := 1 to MAX_LN do
begin
n := LN[i];
// Czas sortowania zbioru posortowanego
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na początku - średnia z 10 obiegów
tpp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[1] := random * n + 1;
tpp += Sort;
end;
tpp /= 10;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na końcu - średnia z 10 obiegów
tpk := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[n] := random * n + 1;
tpk += Sort;
end;
tpk /= 10;
// Czas sortowania zbioru nieuporządkowanego - średnia z 10 obiegów
tnp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := random;
tnp += Sort;
end;
tnp /= 10;
writeln(n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
writeln(f,n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
end;
writeln(K4);
writeln(f,K4);
writeln(f,'Koniec');
closefile(f);
writeln;
writeln('Koniec. Wyniki w pliku WYNIKI.TXT');
end
else writeln('Na tym komputerze program testowy nie pracuje !');
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika):
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0009.php 69 / 209
Algorytmy Sortujące - Sortowanie Przez Wybór 2014-10-03
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu sortowania przez wybór
wyciągamy następujące wnioski:
optymistyczna - dla zbiorów uporządkowanych (z niewielką liczbą elementów nie na swoich miejscach) - na podstawie
czasów tpo, tpp, tpk
typowa - dla zbiorów o losowym rozkładzie elementów - na podstawie czasu tnp
pesymistyczna - dla zbiorów posortowanych odwrotnie - na podstawie czasu tod.
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
1. Wszystkie badane przez nas czasy sortowania są proporcjonalne do kwadratu liczby elementów porządkowanego zbioru.
Klasa czasowej złożoności obliczeniowej algorytmu sortowania przez wybór wynosi O(n2).
2. Wszystkie badane czasy sortowania są praktycznie takie same.
3. Algorytm nie uwzględnia faktu posortowania zbioru. Jednakże również nie wyróżnia on przypadku posortowania zbioru w
kierunku odwrotnym. Zatem można wysunąć wniosek, iż dla tego algorytmu złożoność czasowa nie zależy od rozkładu
elementów w sortowanym zbiorze.
4. Porównując prędkości algorytmów zoptymalizowanego sortowania bąbelkowego i sortowania przez wybór dochodzimy do
wniosku, iż ten drugi w przypadku zbiorów posortowanych odwrotnie oraz nieposortowanych jest kilkakrotnie szybszy.
Jednakże dla zbiorów w znacznym stopniu uporządkowanych algorytm sortowania bąbelkowego nie daje mu żadnych szans
- nic w tym dziwnego, ponieważ zgodnie z naszymi badaniami, algorytm sortowania bąbelkowego posiada w tych
przypadkach liniową klasę czasowej złożoności obliczeniowej, natomiast algorytm sortowania przez wybór ma dla każdego
zestawu danych czasową złożoność obliczeniową proporcjonalną do kwadratu liczby sortowanych elementów.
Temat:
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0009.php 70 / 209
Algorytmy Sortujące - Sortowanie Przez Wybór 2014-10-03
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0009.php 71 / 209
Algorytmy Sortujące - Sortowanie Przez Wstawianie 2014-10-03
Algorytm
Algorytm sortowania przez wstawianie można porównać do
sposobu układania kart pobieranych z talii. Najpierw bierzemy
pierwszą kartę. Następnie pobieramy kolejne, aż do wyczerpania
talii. Każdą pobraną kartę porównujemy z kartami, które już
trzymamy w ręce i szukamy dla niej miejsca przed pierwszą
kartą starszą (młodszą w przypadku porządku malejącego). Gdy
znajdziemy takie miejsce, rozsuwamy karty i nową wstawiamy
na przygotowane w ten sposób miejsce (stąd pochodzi nazwa
algorytmu - sortowanie przez wstawianie, ang. Insertion Sort).
Jeśli nasza karta jest najstarsza (najmłodsza), to umieszczamy
ją na samym końcu. Tak porządkujemy karty. A jak przenieść tę
ideę do świata komputerów i zbiorów liczbowych?
Algorytm sortowania przez wstawianie będzie składał się z dwóch pętli. Pętla główna (zewnętrzna) symuluje pobieranie kart, czyli
w tym wypadku elementów zbioru. Odpowiednikiem kart na ręce jest tzw. lista uporządkowana (ang. sorted list), którą
sukcesywnie będziemy tworzyli na końcu zbioru (istnieje też odmiana algorytmu umieszczająca listę uporządkowaną na początku
zbioru). Pętla sortująca (wewnętrzna) szuka dla pobranego elementu miejsca na liście uporządkowanej. Jeśli takie miejsce
zostanie znalezione, to elementy listy są odpowiednio rozsuwane, aby tworzyć miejsce na nowy element i element wybrany przez
pętlę główną trafia tam. W ten sposób lista uporządkowana rozrasta się. Jeśli na liście uporządkowanej nie ma elementu
większego od wybranego, to element ten trafia na koniec listy. Sortowanie zakończymy, gdy pętla główna wybierze wszystkie
elementy zbioru.
Algorytm sortowania przez wstawianie posiada klasę czasowej złożoności obliczeniowej równą O(n2). Sortowanie odbywa się w
miejscu.
1. Na początku sortowania lista uporządkowana zawiera tylko jeden, ostatni element zbioru. Jednoelementowa lista jest
zawsze uporządkowana.
2. Ze zbioru zawsze wybieramy element leżący tuż przed listą uporządkowaną. Element ten zapamiętujemy w zewnętrznej
zmiennej. Miejsce, które zajmował, możemy potraktować jak puste.
3. Wybrany element porównujemy z kolejnymi elementami listy uporządkowanej.
4. Jeśli natrafimy na koniec listy, element wybrany wstawiamy na puste miejsce - lista rozrasta się o nowy element.
5. Jeśli element listy jest większy od wybranego, to element wybrany wstawiamy na puste miejsce - lista rozrasta się o nowy
element.
6. Jeśli element listy nie jest większy od wybranego, to element listy przesuwamy na puste miejsce. Dzięki tej operacji puste
miejsce wędruje na liście przed kolejny element. Kontynuujemy porównywanie, aż wystąpi sytuacja z punktu 4 lub 5.
Przykład:
Dla przykładu wstawmy wg opisanej metody pierwszy element zbioru listę uporządkowaną utworzoną z pozostałych
elementów {8 4 5 6 9}. Elementy listy uporządkowanej zaznaczyliśmy kolorem zielonym. Puste miejsce
zaznaczyliśmy kolorem białym:
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0010.php 72 / 209
Algorytmy Sortujące - Sortowanie Przez Wstawianie 2014-10-03
8 5 jest5 mniejsze od 8, zatem wędruje na puste miejsce, które przesuwa się przed kolejny
4 5 <<< 6 9 element listy uporządkowanej, liczbę 6.
8 Porównujemy 8 z 6.
4 5 6 9
8 6 jest mniejsze od 8, wędruje na puste miejsce.
4 5 6 <<< 9
8 Porównujemy 8 z 9.
4 5 6 9
Tym razem element wybrany wędruje na puste miejsce, ponieważ jest mniejszy od
4 5 6 8 9 elementu 9 listy uporządkowanej. Operacja wstawiania jest zakończona. Lista rozrasta
się o jeden element.
Przykład:
Wykorzystajmy podane informacje do posortowania opisywaną metodą zbioru { 7 3 8 5 2 }.
5
7 3 8 2 Wybrany element porównujemy z elementem listy.
7 3 8 2 5 Na liście nie ma już więcej elementów do porównania, więc element wybrany wstawiamy
na puste miejsce. Lista uporządkowana zawiera już dwa elementy.
8 Ze zbioru wybieramy 8
7 3 2 5
8 8 porównujemy z 2
7 3 2 5
8
7 3 2 <<< 5 2 jest mniejsze, zatem przesuwamy je na puste miejsce.
8
7 3 2 5 8 porównujemy z 5
8
7 3 2 5 <<< 5 jest mniejsze, przesuwamy je na puste miejsce
7 3 2 5 8 Lista nie zawiera więcej elementów, zatem 8 wstawiamy na puste miejsce. Na liście
uporządkowanej mamy już trzy elementy.
3
7 2 5 8 Ze zbioru wybieramy 3
3 3 porównujemy z 2
7 2 5 8
3 2 jest mniejsze, wędruje zatem na puste miejsce
7 2 <<< 5 8
3 3 porównujemy z 5.
7 2 5 8
7 2 3 5 8 Tym razem mniejszy jest element wybrany. Znaleźliśmy jego miejsce na liście, więc
wstawiamy go na puste miejsce. Lista zawiera już 4 elementy.
7
2 3 5 8 Ze zbioru wybieramy ostatni element - liczbę 7.
7
2 3 5 8 7 porównujemy z 2
7
2 <<< 3 5 8 2 jest mniejsze, przesuwamy je na puste miejsce
7
2 3 5 8 7 porównujemy z 3
7
2 3 <<< 5 8 3 jest mniejsze, przesuwamy je na puste miejsce
7
2 3 5 8 7 porównujemy z 5
7
2 3 5 <<< 8 5 jest mniejsze, przesuwamy je na puste miejsce
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0010.php 73 / 209
Algorytmy Sortujące - Sortowanie Przez Wstawianie 2014-10-03
7
2 3 5 8 7 porównujemy z 8
Specyfikacja problemu
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n.
Dane wyjściowe
d[ ] - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n.
Zmienne pomocnicze
i, j - zmienne sterujące pętli, i, j Î N
x - zawiera wybrany ze zbioru element.
Lista kroków
K01: Dla j = n - 1, n - 2, ..., 1: wykonuj K02...K04
K02: x ← d[j]; i ← j + 1
K03: Dopóki ( i ≤ n ) ( x > d[i] ): wykonuj d[i - 1] ← d[i]; i ← i + 1
K04: d[i - 1] ← x
K05: Zakończ
Schemat blokowy
Pętlę główną rozpoczynamy od przedostatniej pozycji w zbiorze. Element na ostatniej
pozycji jest zalążkiem listy uporządkowanej. Dlatego licznik pętli nr 1 przyjmuje wartość
początkową j = n - 1.
Ze zbioru wybieramy element d[j] i umieszczamy go w zmiennej pomocniczej x. Miejsce
zajmowane przez ten element staje się puste.
Uwaga techniczna
W rzeczywistości na pozycji j pozostaje dalej ten sam element. Jednakże
zapamiętaliśmy jego wartość, zatem pozycja ta może być zapisana inną informacją -
elementu nie utracimy, ponieważ przechowuje go zmienna x. Dlatego pozycję tę możemy
potraktować jako pustą, tzn. z nieistotną zawartością.
Programy
Efekt uruchomienia programu
Sortowanie przez wstawianie
-----------------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
56 5 18 74 83 24 13 4 50 74 63 83 65 65 63 40 81 69 60 84
Po sortowaniu:
4 5 13 18 24 40 50 56 60 63 63 65 65 69 74 74 81 83 83 84
DevPascal
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0010.php 74 / 209
Algorytmy Sortujące - Sortowanie Przez Wstawianie 2014-10-03
Code::Blocks
// Sortowanie Przez Wstawianie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
// Program główny
//---------------
int main()
{
int d[N],i,j,x;
cout << " Sortowanie przez wstawianie\n"
"-----------------------------\n"
" (C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 100;
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0010.php 75 / 209
Algorytmy Sortujące - Sortowanie Przez Wstawianie 2014-10-03
Free Basic
' Sortowanie Przez Wstawianie
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 20 ' Liczebność zbioru.
Dim d(1 To N) As Integer
Dim i As Integer, j As Integer, x As Integer
Print " Sortowanie przez wstawianie "
Print "-----------------------------"
Print " (C)2005 Jerzy Walaszek "
Print
' Najpierw wypełniamy tablicę d() liczbami pseudolosowymi
' a następnie wyświetlamy jej zawartość
Randomize Timer
For i = 1 To N: d(i) = Int(Rnd * 100): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
' Sortujemy
For j = N - 1 To 1 Step -1
x = d(j)
i = j + 1
While (i <= N) And (x > d(i))
d(i - 1) = d(i)
i = i + 1
Wend
d(i - 1) = x
Next
' Wyświetlamy wynik sortowania
Print "Po sortowaniu:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
Print "Nacisnij Enter..."
Sleep
End
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frminsertionsort">
<h3 style="text-align: center">Sortowanie Przez Wstawianie</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0010.php 76 / 209
Algorytmy Sortujące - Sortowanie Przez Wstawianie 2014-10-03
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Przez Wstawianie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
function main()
{
var d = new Array(N);
var i,j,x,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 100);
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Sortujemy
for(j = N - 2; j >= 0; j--)
{
x = d[j];
i = j + 1;
while((i < N) && (x > d[i]))
{
d[i - 1] = d[i];
i++;
}
d[i - 1] = x;
}
// Wyświetlamy wynik sortowania
t += "Po sortowaniu:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Sortuj
...
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0010.php 77 / 209
Algorytmy Sortujące - Sortowanie Przez Wstawianie 2014-10-03
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0010.php 78 / 209
Algorytmy Sortujące - Sortowanie Przez Wstawianie 2014-10-03
end;
tpk /= 10;
// Czas sortowania zbioru nieuporządkowanego - średnia z 10 obiegów
tnp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := random;
tnp += Sort;
end;
tnp /= 10;
writeln(n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
writeln(f,n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
end;
writeln(K4);
writeln(f,K4);
writeln(f,'Koniec');
closefile(f);
writeln;
writeln('Koniec. Wyniki w pliku WYNIKI.TXT');
end
else writeln('Na tym komputerze program testowy nie pracuje !');
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika):
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu sortowania przez wstawianie
wyciągamy następujące wnioski:
optymistyczna - dla zbiorów uporządkowanych (z niewielką liczbą elementów nie na swoich miejscach) - na podstawie
czasów tpo, tpp, tpk
typowa - dla zbiorów o losowym rozkładzie elementów - na podstawie czasu tnp
pesymistyczna - dla zbiorów posortowanych odwrotnie - na podstawie czasu tod.
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0010.php 79 / 209
Algorytmy Sortujące - Sortowanie Przez Wstawianie 2014-10-03
3. Analiza wzrostu prędkości algorytmu sortowania przez wstawianie w stosunku do algorytmu sortowania przez wybór
pokazuje, iż obecny algorytm jest dużo lepszy w przypadku zbiorów w znacznym stopniu uporządkowanych. Również
zbiory o losowym rozkładzie elementów będą posortowane szybciej. Jedynie w przypadku pesymistycznym, gdy zbiór jest
posortowany odwrotnie, algorytm jest wolniejszy od algorytmu sortowania przez wybór. Te cechy czynią algorytm
sortowania przez wstawianie zalecanym, prostym algorytmem sortującym klasy O(n2).
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0010.php 80 / 209
Algorytmy Sortujące - Binarne Sortowanie przez Wstawianie 2014-10-03
Algorytm
Al gory t m sortowania przez wstawianie wykorzystuje
poszukiwanie liniowe w celu znalezienia miejsca wstawiania
wybranego elementu na liście uporządkowanej. Są trzy
możliwości:
1. Element wybrany jest mniejszy lub równy pierwszemu
elementowi listy uporządkowanej (w porz ądk u malejącym
element ten jest większy lub równy). W takim przypadku
wykonane zostanie tylko jedno porównanie i element wybrany
wróci z powrotem na swoje poprzednie miejsce.
2. Element wybrany jest większy od ostatniego elementu listy
uporządkowanej (w porządku malejącym element wybrany jest
mniejszy od ostatniego elementu listy uporządkowanej). Jeśli element wybrany znajduje się na j-tej pozycji, to algorytm musi
wykonać (n - j) porównań elementy wybranego z kolejnymi elementami listy oraz tyle samo przesunięć elementów listy na puste
miejsce.
3. W pozostałych przypadkach element wybrany trafia do wnętrza listy uporządkowanej. Statystycznie będzie to jej środek, zatem
średnio algorytm wykona 0,5 • (n - j) porównań i przesunięć elementów.
Ponieważ lista jest uporządkowana, możemy do jej przeszukania wykorzystać algorytm wyszukiwania binarnego, który ma
klasę czasowej złożoności obliczeniowej równą O(log n), jest zatem niesamowicie szybki. Zasada przeszukiwania binarnego jest
bardzo prosta. Wyznaczamy element środkowy listy. Sprawdzamy, czy wstawiany element jest mniejszy lub równy (przy
sortowaniu malejącym większy lub równy) od elementu środkowego listy. Jeśli tak, to za nowy przedział przyjmujemy pierwszą
część listy, w przeciwnym razie drugą. Operację tę powtarzamy dotąd, aż przedział poszukiwań skurczy się do 1 elementu (nie
można wtedy wyznaczać połówek). Pozycja tego elementu wyznaczy nam miejsce wstawienia. Musimy tylko przesunąć o jedną
pozycję wstecz wszystkie elementy od pozycji (j + 1) do znalezionego miejsca na liście, a następnie wstawić na zwolnione
miejsce element wybrany.
Brzmi super - zmniejszymy liczbę porównań. Czy jest to jednak opłacalne, zobaczymy później, po analizie czasów wykonania
algorytmu.
Specyfikacja problemu
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n.
Dane wyjściowe
d[ ] - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n.
Zmienne pomocnicze
i - indeks środkowego elementu listy uporządkowanej, i Î N
j - licznik obiegów pętli zewnętrznej, indeks wybranego elementu, j Î N
ip, ik - początkowa i końcowa pozycja indeksów w partycji listy uporządkowanej, ip, ik Î N
x - zawiera wybrany ze zbioru element
Lista kroków
K01: Dla j = n - 1, n - 2,...,1: wykonuj K02...K07
K02: x ← d[j]; ip ← j; ik ← n + 1
K03: Dopóki ik - ip > 1: wykonuj K04...K05
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0011.php 81 / 209
Algorytmy Sortujące - Binarne Sortowanie przez Wstawianie 2014-10-03
Schemat blokowy
Algorytm zoptymalizowanego sortowania przez wstawianie zbudowany jest z trzech pętli:
- pętla zewnętrzna nr 1 wybiera ze zbioru element leżący przed listą uporządkowaną.
- pętla wewnętrzna nr 2 poszukuje miejsca wstawienia wybranego elementu na liście
uporządkowanej wykorzystując algorytm wyszukiwania binarnego.
- pętla wewnętrzna nr 3 przesuwa elementy listy, aby zrobić miejsce dla elementu
wybranego.
Na początku algorytmu inicjujemy licznik j pętli zewnętrznej nr 1. Będzie ona przebiegała
przez kolejne indeksy elementów sortowanego zbioru od n - 1 do 1. Na początku tej pętli
umieszczamy w zmiennej pomocniczej x element wybrany. Element ten znajduje się tuż
przed listą uporządkowaną, która tworzona jest na końcu zbioru. Dalej ustawiamy dwa
graniczne indeksy ip i ik , które definiują pozycję elementów na liście uporządkowanej.
Zwróć uwagę, iż faktycznie indeks ip wskazuje pozycję przed listą, a ik wskazuje
pozycję za listą. Jest to potrzebne po to, aby pętla warunkowa nr 2 wykonała się
przynajmniej jeden raz.
Rozpoczyna się pętla warunkowa nr 2. Jest to typowa pętla while, w której warunek
kontynuacji sprawdzamy na początku. Pętla kontynuuje się dopóki partycja listy
uporządkowanej zdefiniowana indeksami ip i ik zawiera więcej niż 1 element, gdyż tylko
wtedy można dokonać podziału na dwie mniejsze partycje. Gdy pętla ta się zakończy,
zmienna ip zawiera indeks pozycji wstawiania na listę uporządkowaną elementu
wybranego, przechowywanego w zmiennej pomocniczej x.
Na początku pętli nr 2 wyznaczamy indeks elementu środkowego partycji i umieszczamy
go w zmiennej i. Następnie sprawdzamy, czy element wybrany, przechowywany w
zmiennej x, jest mniejszy lub równy (dla porządku malejącego większy lub równy) elementowi środkowemu partycji listy
uporządkowanej. Jeśli tak, to pozycja wstawiania znajduje się w pierwszej części partycji - wśród elementów o indeksach od ip do
i. W przeciwnym razie za nową partycję przyjmujemy drugą część - elementy o indeksach od i do ik . Pętlę kontynuujemy do
momentu, aż partycja będzie zawierała tylko jeden element. Wtedy nie ma już wątpliwości i pozycja wstawiania zawsze jest w
zmiennej ip.
Po znalezieniu tej pozycji uruchamia się pętla nr 3. Jej zadaniem jest przesunięcie o 1 pozycję w kierunku początku zbioru
wszystkich elementów listy uporządkowanej poczynając od jej początku aż do pozycji ip. Dzięki tej operacji pozycja ip zostanie
zwolniona i umieszczamy na niej element wybrany. Następnie zmniejszamy indeks j i kontynuujemy pętlę zewnętrzną aż do
posortowania całego zbioru.
Przykład:
Aby lepiej zrozumieć zasadę działania naszego algorytmu, przeanalizujmy prosty przykład wstawiania elementu na
listę uporządkowaną przy pomocy binarnego wyszukiwania pozycji wstawiania. Zbiór ma postać następującą: { 6 3
4 5 7 8 }. Wstawiamy pierwszy element - liczbę 6. Pozostałe elementy tworzą listę uporządkowaną. Na dole
szarymi cyferkami podajemy indeksy elementów:
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0011.php 82 / 209
Algorytmy Sortujące - Binarne Sortowanie przez Wstawianie 2014-10-03
Programy
Efekt uruchomienia programu
Binarne sortowanie przez wstawianie
-------------------------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
30 36 81 45 71 90 15 57 68 24 39 83 54 1 29 48 69 23 0 77
Po sortowaniu:
0 1 15 23 24 29 30 36 39 45 48 54 57 68 69 71 77 81 83 90
DevPascal
// Binarne Sortowanie Przez Wstawianie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program Insertion_Sort;
const N = 20; // Liczebność zbioru.
var
d : array[1..N] of integer;
// Program główny
//---------------
var
i,j,x,ip,ik : integer;
begin
writeln(' Binarne sortowanie przez wstawianie ');
writeln('-------------------------------------');
writeln(' (C)2005 Jerzy Walaszek ');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
// Sortujemy
for j := N - 1 downto 1 do
begin
x := d[j];
ip := j;
ik := N + 1;
while ik - ip > 1 do
begin
i := (ip + ik) div 2;
if x <= d[i] then ik := i else ip := i;
end;
for i := j to ip - 1 do d[i] := d[i + 1];
d[ip] := x;
end;
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln('Nacisnij Enter...');
readln;
end.
Code::Blocks
// Binarne Sortowanie Przez Wstawianie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
#include <iostream>
#include <iomanip>
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0011.php 83 / 209
Algorytmy Sortujące - Binarne Sortowanie przez Wstawianie 2014-10-03
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
// Program główny
//---------------
int main()
{
int d[N],i,j,ip,ik,x;
cout << " Binarne sortowanie przez wstawianie\n"
"-------------------------------------\n"
" (C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 100;
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// Sortujemy
for(j = N - 2; j >= 0; j--)
{
x = d[j];
ip = j;
ik = N;
while(ik - ip > 1)
{
i = (ip + ik) / 2;
if(x <= d[i]) ik = i; else ip = i;
}
for(i = j; i < ip; i++) d[i] = d[i + 1];
d[ip] = x;
}
// Wyświetlamy wynik sortowania
cout << "Po sortowaniu:\n\n";
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
return 0;
}
Free Basic
' Binarne Sortowanie Przez Wstawianie
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 20 ' Liczebność zbioru.
Dim d(1 To N) As Integer
Dim i As Integer, j As Integer, x As Integer
Dim ip As Integer, ik As Integer
Print " Binarne sortowanie przez wstawianie "
Print "-------------------------------------"
Print " (C)2005 Jerzy Walaszek "
Print
' Najpierw wypełniamy tablicę d() liczbami pseudolosowymi
' a następnie wyświetlamy jej zawartość
Randomize Timer
For i = 1 To N: d(i) = Int(Rnd * 100): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
' Sortujemy
For j = N - 1 To 1 Step -1
x = d(j): ip = j: ik = N + 1
While ik - ip > 1
i = Int((ip + ik) / 2)
If x < d(i) Then ik = i Else ip = i
Wend
For i = j To ip - 1: d(i) = d(i + 1): Next
d(ip) = x
Next
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0011.php 84 / 209
Algorytmy Sortujące - Binarne Sortowanie przez Wstawianie 2014-10-03
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmbinaryinsertionsort">
<h3 style="text-align: center">Binarne Sortowanie Przez Wstawianie</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Binarne Sortowanie Przez Wstawianie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
function main()
{
var d = new Array(N);
var i,j,ip,ik,x,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 100);
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Sortujemy
for(j = N - 2; j >= 0; j--)
{
x = d[j];
ip = j;
ik = N;
while(ik - ip > 1)
{
i = Math.floor((ip + ik) / 2);
if(x <= d[i]) ik = i; else ip = i;
}
for(i = j; i < ip; i++) d[i] = d[i + 1];
d[ip] = x;
}
// Wyświetlamy wynik sortowania
t += "Po sortowaniu:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Sortuj
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0011.php 85 / 209
Algorytmy Sortujące - Binarne Sortowanie przez Wstawianie 2014-10-03
...
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0011.php 86 / 209
Algorytmy Sortujące - Binarne Sortowanie przez Wstawianie 2014-10-03
n := LN[i];
// Czas sortowania zbioru posortowanego
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na początku - średnia z 10 obiegów
tpp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[1] := random * n + 1;
tpp += Sort;
end;
tpp /= 10;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na końcu - średnia z 10 obiegów
tpk := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[n] := random * n + 1;
tpk += Sort;
end;
tpk /= 10;
// Czas sortowania zbioru nieuporządkowanego - średnia z 10 obiegów
tnp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := random;
tnp += Sort;
end;
tnp /= 10;
writeln(n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
writeln(f,n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
end;
writeln(K4);
writeln(f,K4);
writeln(f,'Koniec');
closefile(f);
writeln;
writeln('Koniec. Wyniki w pliku WYNIKI.TXT');
end
else writeln('Na tym komputerze program testowy nie pracuje !');
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika):
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0011.php 87 / 209
Algorytmy Sortujące - Binarne Sortowanie przez Wstawianie 2014-10-03
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu binarnego sortowania przez
wstawianie wyciągamy następujące wnioski:
Cechy Algorytmu
Binarnego Sortowania Przez Wstawianie
klasa złożoności obliczeniowej optymistyczna O(n log n)
optymistyczna - dla zbiorów uporządkowanych (z niewielką liczbą elementów nie na swoich miejscach) - na podstawie
czasów tpo, tpp, tpk
typowa - dla zbiorów o losowym rozkładzie elementów - na podstawie czasu tnp
pesymistyczna - dla zbiorów posortowanych odwrotnie - na podstawie czasu tod.
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
O(n log n) O(n2) O(n log n) O(n log n) O(n2)
Binarne sortowanie przez wstawianie
tpo << tod tpp ≈ tpk tnp ≈ 1tod
2
1. Góra zrodziła mysz. Algorytm w wersji zoptymalizowanej posiada gorsze własności od wersji podstawowej. Zwróć uwagę, iż
jedyna różnica w klasach złożoności obliczeniowej zachodzi dla przypadku optymistycznego i to na niekorzyść. Teraz
mamy złożoność liniowo-logarytmiczną, która jest gorsza od złożoności liniowej w poprzedniej wersji algorytmu. Wynika z
tego prosty wniosek - nie opłaca się stosować algorytmu binarnego sortowania przez wstawianie. Ewentualne korzyści są
marnowane przez czas zużyty na wykonanie dodatkowych operacji. Wersja podstawowa jest lepsza i szybsza. Być może
zysk wystąpiłby przy sortowaniu zbiorów o bardzo dużej liczbie elementów (wtedy oszczędności na operacjach porównań
zaczęłyby odgrywać istotną rolę w ogólnym czasie sortowania), jednakże w takich przypadkach lepiej zastosować algorytm
sortujący klasy O(n log n), np. Sortowanie Szybkie.
2. Wszystkie parametry nowego algorytmu są gorsze od wersji podstawowej. Być może wersja ta okazałaby się szybsza w
sytuacji, gdy operacje porównania pochłaniają dużo więcej czasu w porównaniu z operacjami przesuwania elementów - na
przykład przy sortowaniu tablicy łańcuchów znakowych. Proponuję sprawdzenie tej hipotezy ambitnym czytelnikom
serwisu.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0011.php 88 / 209
Algorytmy Sortujące - Binarne Sortowanie przez Wstawianie 2014-10-03
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0011.php 89 / 209
Algorytmy Sortujące - Sortowanie metodą Shella 2014-10-03
Sortowanie Shella
Shell Sort
Podrozdziały
Algorytm Programy Badanie algorytmów sortujących
Dobór optymalnych odstępów Program w języku Pascal Podsumowanie
Specyfikacja problemu Program w języku C++ Zadania dla ambitnych
Lista kroków Program w języku Basic
Schemat blokowy Program w języku JavaScript
Algorytm
W latach 50-tych ubiegłego wieku informatyk Donald Shell zauważył, iż algorytm
sortowania przez wstawianie pracuje bardzo efektywnie w przypadku gdy zbiór jest w
dużym stopniu uporządkowany (sprawdź wyniki naszych badań czasów sortowania dla
tego algorytmu). Z kolei algorytm ten pracuje nieefektywnie w zbiorach
nieuporządkowanych, ponieważ elementy są przesuwane w każdym obiegu o jedną
pozycję przy wstawianiu elementu wybranego na listę uporządkowaną.
Pomysł Shella polegał na tym, iż sortowany zbiór dzielimy na podzbiory, których elementy
są odległe od siebie w sortowanym zbiorze o pewien odstęp h. Każdy z tych podzbiorów
sortujemy algorytmem przez wstawianie. Następnie odstęp zmniejszamy. Powoduje to
powstanie nowych podzbiorów (będzie ich już mniej). Sortowanie powtarzamy i znów
zmniejszamy odstęp, aż osiągnie on wartość 1. Wtedy sortujemy już normalnie za pomocą
Instertion Sort. Jednakże z uwagi na wcześniejsze obiegi sortujące mamy ułatwione zadanie, ponieważ zbiór został w dużym
stopniu uporządkowany. Dzięki początkowym dużym odstępom elementy były przesuwane w zbiorze bardziej efektywnie - na duże
odległości. W wyniku otrzymujemy najlepszy pod względem szybkości czasu wykonania algorytm sortujący w klasie O(n2).
Algorytm ten nosi również nazwę algorytmu sortowania przez wstawianie z malejącym odstępem (ang. Diminishing Increment
Sort).
Efektywność algorytmu sortowania metodą Shella zależy w dużym stopniu od ciągu przyjętych odstępów. Pierwotnie Shell
proponował pierwszy odstęp równy połowie liczby elementów w sortowanym zbiorze. Kolejne odstępy otrzymujemy dzieląc odstęp
przez 2 (dzielenie całkowitoliczbowe).
Przykład:
Posortujemy metodą Shella zbiór ośmiu liczb: { 4 2 9 5 6 3 8 1 } w porządku rosnącym. Zbiór posiada osiem
elementów, zatem przyjmiemy na wstępie odstęp h równy 4. Taki odstęp podzieli zbiór na 4 podzbiory, których
elementy będą elementami zbioru wejściowego odległymi od siebie o 4 pozycje. Każdy z otrzymanych podzbiorów
sortujemy algorytmem sortowania przez wstawianie. Ponieważ zbiory te są dwuelementowe, to sortowanie pędzie
polegało na porównaniu pierwszego elementu podzbioru z elementem drugim i ewentualną zamianę ich miejsc, jeśli
będą w niewłaściwym porządku.
Podział, h=4 Sortowanie Wynik
4 2 9 5 6 3 8 1 - Zbiór wejściowy
1 4 6 4 6 4 6
2 2 3 2 3 2 3
3 9 8 8 9 8 9
4 5 1 1 5 1 5
Zbiór wyjściowy - 4 2 8 1 6 3 9 5
Zmniejszamy odstęp h o połowę, więc h = 2. Zbiór podstawowy zostanie podzielony na dwa podzbiory. Każdy z
tych podzbiorów sortujemy przez wstawianie:
Zmniejszamy odstęp h o połowę, h = 1. Taki odstęp nie dzieli zbioru wejściowego na podzbiory, więc teraz będzie
sortowany przez wstawianie cały zbiór. Jednak algorytm sortujący ma ułatwioną pracę, ponieważ dzięki poprzednim
dwóm obiegom zbiór został częściowo uporządkowany - elementy małe zbliżyły się do początku zbioru, a elementy
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0012.php 90 / 209
Algorytmy Sortujące - Sortowanie metodą Shella 2014-10-03
duże do końca.
Przykład:
Obliczmy pierwszy odstęp dla zbioru o n = 200 elementach:
h1 = 1
h2 = 3h1 + 1 = 3 + 1 = 4 - mniejsze od 200, kontynuujemy
h3 = 3h2 + 1 = 12 + 1 = 13 - kontynuujemy
h4 = 3h3 + 1 = 39 + 1 = 40 - kontynuujemy
h5 = 3h4 + 1 = 120 + 1 = 121 - kontynuujemy
h6 = 3h5 + 1 = 363 + 1 = 364 - stop, ponieważ jest większe od 200
h = [h6 : 9] = [364 : 9] = [40 4/9] = 40 (zwróć uwagę, iż jest to zawsze element wcześniejszy o dwie
pozycje, czyli h4)
Kolejne odstępy obliczamy dzieląc całkowitoliczbowo bieżący odstęp przez 3. Taki właśnie sposób wyliczania
odstępów przyjmiemy w naszym algorytmie.
Specyfikacja problemu
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - zbiór n-elementowy, który będzie sortowany. Elementy zbioru mają indeksy od 1 do n.
Dane wyjściowe
d[ ] - posortowany zbiór n-elementowy. Elementy zbioru mają indeksy od 1 do n.
Zmienne pomocnicze
i - indeks elementu listy uporządkowanej, i Î N
j - zmienna sterująca pętli, j Î N
h - odstęp pomiędzy kolejnymi elementami podzbiorów, h Î N
x - zawiera wybrany ze zbioru element
Lista kroków
K01: h←1
K02: Powtarzaj h ← 3h + 1, aż h ≥ n
K03: h ← h div 9
K04: Jeśli h = 0, to h ← 1
K05: Dopóki h > 0: wykonuj K06...K10
K06: Dla j = n - h, n - h - 1, ..., 1: wykonuj K07...K09
K07: x ← d[j]; i ← j + h
K08: Dopóki (i ≤ n) i (x > d[i]): wykonuj d[i - h] ← d[i]; i ← i + h
K09: d[i - h] ← x
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0012.php 91 / 209
Algorytmy Sortujące - Sortowanie metodą Shella 2014-10-03
K10: h ← h div 3
K11: Zakończ
Schemat blokowy
Algorytm sortowania metodą Shella jest ulepszonym algorytmem
sortowania przez wstawianie. Aby się o tym przekonać, wystarczy
spojrzeć na schemat blokowy. Kolorem szarym zaznaczyliśmy na nim
bloki, które dokładnie odpowiadają algorytmowi sortowania przez
wstawianie. Jedyną modyfikacją jest wprowadzenie odstępu h zamiast
liczby 1.
Na początku algorytmu wyznaczamy wartość początkowego odstępu h.
Wykorzystujemy tu sugestie prof. Donalda Knutha.
Po wyznaczeniu h rozpoczynamy pętlę warunkową nr 1. Pętla ta jest
wykonywana dotąd, aż odstęp h przyjmie wartość 0. Wtedy kończymy
algorytm, zbiór będzie posortowany.
Wewnątrz pętli nr 1 umieszczony jest opisany wcześniej algorytm
sortowania przez wstawianie, który dokonuje sortowania elementów
poszczególnych podzbiorów wyznaczonych przez odstęp h. Po
zakończeniu sortowania podzbiorów odstęp h jest zmniejszany i
następuje powrót na początek pętli warunkowej nr 1.
Uwaga techniczna:
Każdy obieg pętli nr 2 sortuje przemiennie jeden element z kolejnych podzbiorów. Najpierw będą to elementy
przedostatnie w kolejnych podzbiorach wyznaczonych odstępem h, później wcześniejsze i wcześniejsze. Takie
podejście znacząco upraszcza algorytm sortowania.
Programy
Efekt uruchomienia programu
Sortowanie metoda Shella
--------------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
65 89 49 99 57 2 35 2 86 48 90 54 89 20 32 6 52 20 40 17
Po sortowaniu:
2 2 6 17 20 20 32 35 40 48 49 52 54 57 65 86 89 89 90 99
DevPascal
// Sortowanie Metodą Shella
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program Shell_Sort;
const N = 20; // Liczebność zbioru.
var
d : array[1..N] of integer;
// Program główny
//---------------
var
h,i,j,x : integer;
begin
writeln(' Sortowanie metoda Shella ');
writeln('--------------------------');
writeln(' (C)2005 Jerzy Walaszek ');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0012.php 92 / 209
Algorytmy Sortujące - Sortowanie metodą Shella 2014-10-03
Code::Blocks
// Sortowanie metodą Shella
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
// Program główny
//---------------
int main()
{
int d[N],h,i,j,x;
cout << " Sortowanie metoda Shella\n"
"--------------------------\n"
" (C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 100;
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// Wyznaczamy wartość początkowego przesunięcia
for(h = 1; h < N; h = 3 * h + 1);
h /= 9;
if(!h) h++; // istotne dla małych N, dla większych można pominąć!
// Sortujemy
while(h)
{
for(j = N - h - 1; j >= 0; j--)
{
x = d[j];
i = j + h;
while((i < N) && (x > d[i]))
{
d[i - h] = d[i];
i += h;
}
d[i - h] = x;
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0012.php 93 / 209
Algorytmy Sortujące - Sortowanie metodą Shella 2014-10-03
}
h /= 3;
}
// Wyświetlamy wynik sortowania
cout << "Po sortowaniu:\n\n";
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
return 0;
}
Free Basic
' Sortowanie Metodą Shella
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 20 ' Liczebność zbioru.
Dim d(1 To N) As Integer
Dim h As Integer, i As Integer, j As Integer, x As Integer
Print " Sortowanie metoda Shella "
Print "--------------------------"
Print " (C)2005 Jerzy Walaszek "
Print
' Najpierw wypełniamy tablicę d() liczbami pseudolosowymi
' a następnie wyświetlamy jej zawartość
Randomize Timer
For i = 1 To N: d(i) = Int(Rnd * 100): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
' Wyznaczamy wartość początkowego przesunięcia
h = 1
Do
h = 3 * h + 1
Loop Until h >= N
h = Int(h / 9)
If h = 0 Then h = 1 ' istotne dla małych N, dla większych można pominąć!
' Sortujemy
While h > 0
For j = N - h To 1 Step -1
x = d(j)
i = j + h
While (i <= N) And (x > d(i))
d(i - h) = d(i)
i = i + h
Wend
d(i - h) = x
Next
h = Int(h / 3)
Wend
' Wyświetlamy wynik sortowania
Print "Po sortowaniu:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
Print "Nacisnij Enter..."
Sleep
End
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmshellsort">
<h3 style="text-align: center">Sortowanie Metodą Shella</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0012.php 94 / 209
Algorytmy Sortujące - Sortowanie metodą Shella 2014-10-03
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Metodą Shella
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
function main()
{
var d = new Array(N);
var i,j,ip,ik,x,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 100);
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Wyznaczamy początkowe przesunięcie
for(h = 1; h < N; h = 3 * h + 1);
h = Math.floor(h / 9);
if(!h) h++; // istotne dla małych N, dla większych można pominąć!
// Sortujemy
while(h)
{
for(j = N - h - 1; j >= 0; j--)
{
x = d[j];
i = j + h;
while((i < N) && (x > d[i]))
{
d[i - h] = d[i];
i += h;
}
d[i - h] = x;
}
h = Math.floor(h / 3);
}
// Wyświetlamy wynik sortowania
t += "Po sortowaniu:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Sortuj
...
DevPascal
// Program testujący czas sortowania dla
// danego algorytmu sortującego
//--------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// w Tarnowie
//--------------------------------------
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0012.php 95 / 209
Algorytmy Sortujące - Sortowanie metodą Shella 2014-10-03
program TestCzasuSortowania;
uses Windows;
const
NAZWA = 'Sortowanie metodą Shella';
K1 = '-----------------------------------------------------------';
K2 = '(C)2011/2012 I Liceum Ogolnoksztalcace w Tarnowie';
K3 = '------n---------tpo---------tod---------tpp---------tpk---------tnp';
K4 = '-------------------------------------------------------------------';
MAX_LN = 8; // określa ostatnie LN
LN : array[1..8] of integer = (1000,2000,4000,8000,16000,32000,64000,128000);
var
d : array[1..128000] of real; // sortowana tablica
n : integer; // liczba elementów
qpf,tqpc : int64; // dane dla pomiaru czasu
qpc1,qpc2 : int64;
// Tutaj umieszczamy procedurę sortującą tablicę d
//-------------------------------------------------------
function Sort : extended;
var
i,j,h : integer;
x : real;
begin
QueryPerformanceCounter(addr(qpc1));
h := 1;
repeat
h := 3 * h + 1;
until h >= n;
h := (h div 9);
while(h > 0) do
begin
for j := n - h downto 1 do
begin
x := d[j];
i := j + h;
while (i <= n) and (x > d[i]) do
begin
d[i - h] := d[i];
i += h;
end;
d[i - h] := x;
end;
h := h div 3;
end;
QueryPerformanceCounter(addr(qpc2));
Sort := (qpc2 - qpc1 - tqpc) / qpf;
end;
// Program główny
//---------------
var
i,j,k : integer;
tpo,tod,tpp,tpk,tnp : extended;
f : Text;
begin
if QueryPerformanceFrequency(addr(qpf)) then
begin
QueryPerformanceCounter(addr(qpc1));
QueryPerformanceCounter(addr(qpc2));
tqpc := qpc2 - qpc1;
assignfile(f,'wyniki.txt'); rewrite(f);
// Wydruk na ekran
writeln('Nazwa: ',NAZWA);
writeln(K1);
writeln(K2);
writeln;
writeln(K3);
// Wydruk do pliku
writeln(f,'Nazwa: ',NAZWA);
writeln(f,K1);
writeln(f,K2);
writeln(f,'');
writeln(f,K3);
for i := 1 to MAX_LN do
begin
n := LN[i];
// Czas sortowania zbioru posortowanego
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0012.php 96 / 209
Algorytmy Sortujące - Sortowanie metodą Shella 2014-10-03
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika):
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu binarnego sortowania przez
wstawianie wyciągamy następujące wnioski:
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0012.php 97 / 209
Algorytmy Sortujące - Sortowanie metodą Shella 2014-10-03
optymistyczna - dla zbiorów uporządkowanych (z niewielką liczbą elementów nie na swoich miejscach) - na podstawie
czasów tpo, tpp, tpk
typowa - dla zbiorów o losowym rozkładzie elementów - na podstawie czasu tnp
pesymistyczna - dla zbiorów posortowanych odwrotnie - na podstawie czasu tod.
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
1. Wszystkie badane czasy sortowania są dosyć dobrze proporcjonalne do O(n1,12...1,16). Przyjęliśmy wartość średnią
O(n1,14) - jest to oszacowanie przybliżone, które należy brać z pewną rezerwą.
2. Czas sortowania zbioru uporządkowanego odwrotnie jest krótszy od czasu sortowania zbioru nieuporządkowanego. Wynika
stąd, iż nie jest to przypadek pesymistyczny dla tego algorytmu.
3. Zbiory w znacznym stopniu uporządkowane są sortowane dużo szybciej od zbiorów nieuporządkowanych.
4. Z oszacowań wzrostu prędkości wynika, iż algorytm sortowania metodą Shella jest bezkonkurencyjny w klasie
O(n2) algorytmów sortujących przy sortowaniu zbiorów nieuporządkowanych i zbiorów posortowanych odwrotnie, czyli w
przypadku ogólnym. Jednakże przy sortowaniu zbiorów w dużym stopniu uporządkowanych lepszym okazuje się
poprzednio opisany algorytm sortowania przez wstawianie. Oszacowania wyznaczonych wzrostów prędkości nie należy
traktować jako wzorów matematycznych - to tylko próba dopasowania pewnych funkcji, które w przyszłości należy
dokładniej zweryfikować.
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0012.php 98 / 209
Algorytmy Sortujące - Sortowanie metodą Shella 2014-10-03
http://edu.i-lo.tarnow.pl/inf/alg/003_sort/0012.php 99 / 209
Algorytmy Sortujące - Sortowanie przez scalanie 2014-10-03
Algorytm
Poczynając od tego rozdziału przechodzimy do opisu algorytmów szybkich, tzn. takich, które posiadają klasę czasowej
złożoności obliczeniowej równą O(n log n) lub nawet lepszą.
W informatyce zwykle obowiązuje zasada, iż prosty algorytm posiada dużą złożoność obliczeniową, natomiast algorytm
zaawansowany posiada małą złożoność obliczeniową, ponieważ wykorzystuje on pewne własności, dzięki którym szybciej
dochodzi do rozwiązania.
Wiele dobrych algorytmów sortujących korzysta z rekurencji, która powstaje wtedy, gdy do rozwiązania problemu algorytm
wykorzystuje samego siebie ze zmienionym zestawem danych.
Przykład:
Jako przykład może posłużyć rekurencyjne obliczanie silni. Silnię liczby n należącej do zbioru liczb naturalnych
definiujemy następująco:
n! = 1 × 2 × 3 × ... × (n - 1) × n
Na przykład: 5! = 1 × 2 × 3 × 4 × 5 = 120
Lista kroków
K01: Jeśli n < 2, silnia(n) ← 1 i zakończ
K02: silnia(n) ← n × silnia(n - 1) i zakończ
Dzięki rekurencji funkcja wyliczająca wartość silni staje się niezwykle prosta. Najpierw sprawdzamy warunek zakończenia
rekurencji, tzn. sytuację, gdy wynik dla otrzymanego zestawu danych jest oczywisty. W przypadku silni sytuacja taka wystąpi dla
n < 2 - silnia ma wartość 1. Jeśli warunek zakończania rekurencji nie wystąpi, to wartość wyznaczamy za pomocą
rekurencyjnego wywołania obliczania silni dla argumentu zmniejszonego o 1. Wynik tego wywołania mnożymy przez n i zwracamy
jako wartość silni dla n.
Wynaleziony w 1945 roku przez Johna von Neumanna algorytm sortowania przez scalanie jest algorytmem rekurencyjnym.
Wykorzystuje zasadę dziel i zwyciężaj, która polega na podziale zadania głównego na zadania mniejsze dotąd, aż rozwiązanie
stanie się oczywiste. Algorytm sortujący dzieli porządkowany zbiór na kolejne połowy dopóki taki podział jest możliwy (tzn.
podzbiór zawiera co najmniej dwa elementy). Następnie uzyskane w ten sposób części zbioru rekurencyjnie sortuje tym samym
algorytmem. Posortowane części łączy ze sobą za pomocą scalania tak, aby wynikowy zbiór był posortowany.
Przykład:
Połączmy za pomocą opisanego algorytmu dwa uporządkowane zbiory: { 1 3 6 7 9 } z { 2 3 4 6 8 }
Scalane Zbiór
Opis wykonywanych działań
zbiory tymczasowy
Porównujemy ze sobą najmniejsze elementy scalanych
[1] 3 6 7 9 zbiorów. Ponieważ zbiory te są już uporządkowane, to
2 3 4 6 8 najmniejszymi elementami będą zawsze ich pierwsze
elementy.
W zbiorze tymczasowym umieszczamy mniejszy
3 6 7 9 [1] element, w tym przypadku będzie to liczba 1.
2 3 4 6 8 Jednocześnie element ten zostaje usunięty z pierwszego
zbioru
3 6 7 9 1 Porównujemy kolejne dwa elementy i mniejszy
[2] 3 4 6 8 umieszczamy w zbiorze tymczasowym.
Następne porównanie i w zbiorze tymczasowym
[3] 6 7 9 1[2] umieszczamy liczbę 3. Ponieważ są to elementy równe,
3 4 6 8 to nie ma znaczenia, z którego zbioru weźmiemy element
3.
6 7 9 1 2[3] Teraz do zbioru tymczasowego trafi drugie 3.
[3] 4 6 8
6 7 9 1 2 3[3] W zbiorze tymczasowym umieszczamy mniejszy z
[4] 6 8 porównywanych elementów, czyli liczbę 4.
[6] 7 9 1 2 3 3[4] Porównywane elementy są równe, zatem w zbiorze
6 8 tymczasowym umieszczamy dowolny z nich.
7 9
[6] 8
1 2 3 3 4[6] Teraz drugą liczbę 6.
[7] 9
8
1 2 3 3 4 6[6] W zbiorze tymczasowym umieszczamy liczbę 7
9
[8]
1 2 3 3 4 6 6[7] Teraz 8
Z podanego przykładu możemy wyciągnąć wniosek, iż operacja scalania dwóch uporządkowanych zbiorów jest dosyć prosta.
Diabeł jak zwykle tkwi w szczegółach.
Przykład:
Prześledźmy prosty przykład. Mamy posortować zbiór o postaci: { 6 5 4 1 3 7 9 2 }
Sortowany zbiór
Opis wykonywanych operacji
d[1] d[2] d[3] d[4] d[5] d[6] d[7] d[8]
6 5 4 1 3 7 9 2 Zbiór wyjściowy.
6 5 4 1 3 7 9 2 Pierwszy podział.
6 5 4 1 3 7 9 2 Drugi podział
6 5 4 1 3 7 9 2 Trzeci podział.
5 6 1 4 3 7 2 9 Pierwsze scalanie.
1 4 5 6 2 3 7 9 Drugie scalanie.
1 2 3 4 5 6 7 9 Trzecie scalanie. Koniec.
Ponieważ w opisywanym tutaj algorytmie sortującym scalane podzbiory są przyległymi do siebie częściami innego zbioru, zatem
logiczne będzie użycie do ich definicji indeksów wybranych elementów tych podzbiorów:
Przez podzbiór młodszy rozumiemy podzbiór zawierający elementy o indeksach mniejszych niż indeksy elementów w podzbiorze
starszym.
ip ... is ... ik
pozostała część zbioru pozostała część zbioru
młodszy podzbiór starszy podzbiór
Indeks końcowego elementu młodszej połówki zbioru z łatwością wyliczamy - będzie on o 1 mniejszy od indeksu pierwszego
elementu starszej połówki.
Przykład:
ik = 8 ip = 1 is = 3 ip = 5 is = 7
ik = 4 ik = 8
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
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
Programy
Efekt uruchomienia programu
Sortowanie przez scalanie
---------------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
37 91 80 5 38 97 6 40 31 96 97 76 85 25 3 69 11 43 34 53
Po sortowaniu:
3 5 6 11 25 31 34 37 38 40 43 53 69 76 80 85 91 96 97 97
DevPascal
// Sortowanie Przez Scalanie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program Merge_Sort;
const N = 20; // Liczebność zbioru.
var
d,p : array[1..N] of integer;
// Procedura sortująca
//--------------------
procedure MergeSort(i_p,i_k : integer);
var
i_s,i1,i2,i : integer;
begin
i_s := (i_p + i_k + 1) div 2;
if i_s - i_p > 1 then MergeSort(i_p, i_s - 1);
if i_k - i_s > 0 then MergeSort(i_s, i_k);
i1 := i_p; i2 := i_s;
for i := i_p to i_k do
if (i1 = i_s) or ((i2 <= i_k) and (d[i1] > d[i2])) then
begin
p[i] := d[i2]; inc(i2);
end
else
begin
p[i] := d[i1]; inc(i1);
end;
for i := i_p to i_k do d[i] := p[i];
end;
// Program główny
//---------------
var
i : integer;
begin
writeln(' Sortowanie przez scalanie ');
writeln('---------------------------');
writeln(' (C)2005 Jerzy Walaszek ');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
// Sortujemy
MergeSort(1,N);
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln('Nacisnij Enter...');
readln;
end.
Code::Blocks
// Sortowanie przez scalanie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <cmath>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
int d[N],p[N];
// Procedura sortująca
//--------------------
void MergeSort(int i_p, int i_k)
{
int i_s,i1,i2,i;
i_s = (i_p + i_k + 1) / 2;
if(i_s - i_p > 1) MergeSort(i_p, i_s - 1);
if(i_k - i_s > 0) MergeSort(i_s, i_k);
i1 = i_p; i2 = i_s;
for(i = i_p; i <= i_k; i++)
p[i] = ((i1 == i_s) || ((i2 <= i_k) && (d[i1] > d[i2]))) ? d[i2++] : d[i1++];
for(i = i_p; i <= i_k; i++) d[i] = p[i];
}
// Program główny
//---------------
int main()
{
int i;
cout << " Sortowanie przez scalanie\n"
"---------------------------\n"
" (C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = rand() % 100;
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// Sortujemy
MergeSort(0,N-1);
// Wyświetlamy wynik sortowania
cout << "Po sortowaniu:\n\n";
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
return 0;
}
Free Basic
' Sortowanie Przez Scalanie
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 20 ' Liczebność zbioru.
Dim Shared d(1 To N) As Integer
Dim Shared p(1 To N) As Integer
Declare Sub MergeSort(Byval i_p As Integer, Byval i_k As Integer)
Dim i As Integer
Print " Sortowanie przez scalanie "
Print "---------------------------"
Print " (C)2005 Jerzy Walaszek "
Print
' Najpierw wypełniamy tablicę d() liczbami pseudolosowymi
' a następnie wyświetlamy jej zawartość
Randomize Timer
For i = 1 To N: d(i) = Int(Rnd * 100): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
' Sortujemy
MergeSort 1, N
' Wyświetlamy wynik sortowania
Print "Po sortowaniu:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
Print "Nacisnij Enter..."
Sleep
End
' Procedura sortująca
'--------------------
Public Sub MergeSort(Byval i_p As Integer, Byval i_k As Integer)
Dim i_s As Integer, i1 As Integer, i2 As Integer, i As Integer
i_s = Int((i_p + i_k + 1) / 2)
If i_s - i_p > 1 Then MergeSort i_p, i_s - 1
If i_k - i_s > 0 Then MergeSort i_s, i_k
i1 = i_p: i2 = i_s
For i = i_p To i_k
If (i1 = i_s) Or ((i2 <= i_k) And (d(i1) > d(i2))) Then
p(i) = d(i2): i2 = i2 + 1
Else
p(i) = d(i1): i1 = i1 + 1
End If
Next
For i = i_p To i_k: d(i) = p(i): Next
End Sub
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmmergesort">
<h3 style="text-align: center">Sortowanie Przez Scalanie</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Przez Scalanie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
var d = new Array(N);
var p = new Array(N);
// Procedura sortująca
//--------------------
function MergeSort(i_p, i_k)
{
var i_s,i1,i2,i;
i_s = Math.floor((i_p + i_k + 1) / 2);
if(i_s - i_p > 1) MergeSort(i_p, i_s - 1);
if(i_k - i_s > 0) MergeSort(i_s, i_k);
i1 = i_p; i2 = i_s;
for(i = i_p; i <= i_k; i++)
p[i] = ((i1 == i_s) || ((i2 <= i_k) && (d[i1] > d[i2]))) ? d[i2++] : d[i1++];
for(i = i_p; i <= i_k; i++) d[i] = p[i];
}
function main()
{
var i,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 0; i < N; i++) d[i] = Math.floor(Math.random() * 100);
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Sortujemy
MergeSort(0,N-1);
// Wyświetlamy wynik sortowania
t += "Po sortowaniu:<BR><BR>";
for(i = 0; i < N; i++) t += d[i] + " ";
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Sortuj
...
begin
n := LN[i];
// Czas sortowania zbioru posortowanego
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na początku - średnia z 10 obiegów
tpp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[1] := random * n + 1;
tpp += Sort;
end;
tpp /= 10;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na końcu - średnia z 10 obiegów
tpk := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[n] := random * n + 1;
tpk += Sort;
end;
tpk /= 10;
// Czas sortowania zbioru nieuporządkowanego - średnia z 10 obiegów
tnp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := random;
tnp += Sort;
end;
tnp /= 10;
writeln(n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
writeln(f,n:7,tpo:12:6,tod:12:6,tpp:12:6,tpk:12:6,tnp:12:6);
end;
writeln(K4);
writeln(f,K4);
writeln(f,'Koniec');
closefile(f);
writeln;
writeln('Koniec. Wyniki w pliku WYNIKI.TXT');
end
else writeln('Na tym komputerze program testowy nie pracuje !');
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika):
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu sortowania przez scalanie
wyciągamy następujące wnioski:
optymistyczna - dla zbiorów uporządkowanych (z niewielką liczbą elementów nie na swoich miejscach) - na podstawie
czasów tpo, tpp, tpk
typowa - dla zbiorów o losowym rozkładzie elementów - na podstawie czasu tnp
pesymistyczna - dla zbiorów posortowanych odwrotnie - na podstawie czasu tod.
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
O(n log n) O(n log n) O(n log n) O(n log n) O(n log n)
Sortowanie przez scalanie
tpo ≈ tod tpp ≈ tpk tnp ≈ 2tod
3
1. Wszystkie badane czasy są proporcjonalne do nlog2n, zatem wnioskujemy, iż algorytm sortowania przez scalanie posiada
klasę czasowej złożoności obliczeniowej równą O(n log n).
2. Najdłużej trwa sortowanie zbioru nieuporządkowanego. Jednakże badane czasy nie różnią się wiele pomiędzy sobą, co
sugeruje, iż algorytm nie jest specjalnie czuły na rozkład danych wejściowych.
3. Porównanie czasów działania algorytmów sortowania metodą Shella oraz sortowania przez scalanie doprowadza do
wniosku, iż ten drugi algorytm jest szybszy. W przypadku ogólnym zbiór zostanie posortowany 2,5 razy szybciej
(dokładniejsza analiza na pewno pokaże, iż nie są to stosunki stałe, lecz zależą on liczby elementów w sortowanym
zbiorze i rosną wraz ze wzrostem n). W przypadku zbioru posortowanego odwrotnie zysk jest dwukrotny. Szybkość
działania jest jednak okupiona większym zapotrzebowaniem na pamięć - złożoność pamięciowa jest klasy O(n), gdyż dla n
elementowego zbioru musimy dodatkowo zarezerwować tablicę n elementową dla zbioru będącego wynikiem scalania.
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Sortowanie stogowe
Heap Sort
Podrozdziały Tematy pokrewne
Drzewo binarne Tworzenie kopca
Odwzorowanie drzewa binarnego w tablicy Rozbiór kopca
Zrównoważone drzewa binarne Sortowanie przez kopcowanie
Ścieżki w drzewach binarnych
Podsumowanie nowej terminologii
Zadania dla ambitnych
Drzewo binarne
Binary Tree
Dotychczas operowaliśmy na prostych strukturach danych, takich jak tablice. W tablicy elementy ułożone są zgodnie z ich
numeracją, czyli indeksami. Jeśli za punkt odniesienia weźmiemy element d[i] (i = 2,3,..,n-1; n - liczba elementów w tablicy), to
elementem poprzedzającym go będzie element o mniejszym o 1 indeksie, czyli d[i - 1]. Elementem następnym będzie element o
indeksie o 1 większym, czyli d[i + 1]. Jest to tzw. hierarchia liniowa - elementy następują jeden za drugim. Graficznie możemy
przedstawić to tak:
Pierwszy element d[1] nie posiada poprzednika (ang. predecessor - elementu poprzedzającego w ciągu). Ostatni element d[n]
nie posiada następnika (ang. successor - elementu następnego w ciągu). Wszystkie pozostałe elementy posiadają poprzedniki i
następniki.
Drzewo binarne jest hierarchiczną strukturą danych, którego elementy będziemy nazywali węzłami (ang. node) lub
wierzchołkami. W hierarchii liniowej każdy element może posiadać co najwyżej jeden następnik. W drzewie binarnym każdy
węzeł może posiadać dwa następniki (stąd pochodzi nazwa drzewa - binarny = dwójkowy, zawierający dwa elementy), które
nazwiemy potomkami. dziećmi lub węzłami potomnymi danego węzła (ang. child node).
Węzły są połączone krawędziami symbolizującymi następstwo kolejnych elementów w strukturze drzewa
binarnego. Według rysunku po prawej stronie węzeł A posiada dwa węzły potomne: B i C. Węzeł B nosi nazwę
lewego potomka (ang. left child node), a węzeł C nosi nazwę prawego potomka (ang. right child node).
Z kolei węzeł B posiada węzły potomne D i E, a węzeł C ma węzły potomne F i G. Jeśli dany węzeł nie
posiada dalszych węzłów potomnych, to jest w strukturze drzewa binarnego węzłem terminalnym. Taki
węzeł nosi nazwę liścia (ang. leaf node). Na naszym rysunku liściami są węzły terminalne D, E, F i G.
Rodzicem, przodkiem (ang. parent node) lub węzłem nadrzędnym będziemy nazywać węzeł leżący na
wyższym poziomie hierarchii drzewa binarnego. Dla węzłów B I C węzłem nadrzędnym jest węzeł A. Podobnie
dla węzłów D i E węzłem nadrzędnym będzie węzeł B, a dla F i G będzie to węzeł C.
Węzeł nie posiadający rodzica nazywamy korzeniem drzewa binarnego (ang. root node). W naszym przykładzie korzeniem jest
węzeł A. Każde drzewo binarne, które zawiera węzły posiada dokładnie jeden korzeń.
Sprawdź, iż podane wzory są również spełnione w drzewach binarnych o większych rozmiarach niż prezentuje nasz przykład
(pomocna może być kartka papieru).
Przykład:
Skonstruować drzewo binarne z elementów zbioru {7 5 9 2 4 6 1}
Operacja Opis
Konstrukcję drzewa binarnego rozpoczynamy od korzenia, który jest pierwszym elementem zbioru, czyli liczbą 7.
7592461
Do korzenia dołączamy dwa węzły potomne, które leżą obok w zbiorze. Są to dwa kolejne elementy, 5 i 9.
7592461
Do lewego węzła potomnego (5) dołączamy jego węzły potomne. Są to kolejne liczby w zbiorze, czyli 2 i 4.
7592461
Pozostaje nam dołączyć do prawego węzła ostatnie dwa elementy zbioru, czyli liczby 6 i 1. Drzewo jest
kompletne.
7592461
Drzewo po lewej stronie nie posiada węzła d[7]. Ale posiada wszystkie węzły od d[1] d o d[6], jest zatem zrównoważone i
uporządkowane. Można je bez problemu przedstawić za pomocą tablicy elementów od d[1] do d[6].
Drzewo po prawej stronie nie posiada węzła d[5]. Takiego drzewa nie przedstawimy poprawnie za pomocą tablicy elementów od
d[1] do d[7], ponieważ nie mamy możliwości zaznaczenia (bez dodatkowych zabiegów), iż element d[5] nie należy do struktury
drzewa. Zatem nie będzie to uporządkowane i zrównoważone drzewo binarne.
W uporządkowanych i zrównoważonych drzewach binarnych bardzo prosto można sprawdzić, czy k-ty węzeł jest liściem. Będzie
tak, jeśli węzeł ten nie posiada węzłów potomnych. Zatem, jeśli drzewo binarne składa się z n węzłów, to wystarczy sprawdzić,
czy 2k > n. Jeśli tak, węzeł jest liściem. Jeśli nie, węzeł posiada potomka o indeksie 2k, zatem nie może być liściem.
Na powyższym rysunku zaznaczona została ścieżka biegnąca poprzez węzły {d[1], d[3], d[6], d[13]}. Ścieżka ta zawiera
cztery węzły, ma zatem długość równą 3.
Wysokością drzewa binarnego nazwiemy długość najdłuższej ścieżki od korzenia do liścia. W powyższym przykładzie najdłuższa
taka ścieżka ma długość 3, zatem zaprezentowane drzewo binarne ma wysokość równą 3.
Dla n węzłów zrównoważone drzewo binarne ma wysokość równą:
h = [log2n]
Zobacz również na: Tworzenie kopca | Rozbiór kopca | Sortowanie przez kopcowanie
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Sortowanie stogowe
Heap Sort
Podrozdziały Tematy pokrewne
Kopiec Programy Podsumowanie Drzewo binarne
Algorytm tworzenia kopca Program w języku Pascal Zadania dla ambitnych Rozbiór kopca
Specyfikacja problemu Program w języku C++ Sortowanie przez kopcowanie
Lista kroków Program w języku Basic
Schemat blokowy Program w języku JavaScript
Kopiec
Heap
Kopiec jest drzewem binarnym, w którym wszystkie węzły spełniają następujący warunek (zwany warunkiem kopca):
Węzeł nadrzędny jest większy lub równy węzłom potomnym (w porządku malejącym relacja jest odwrotna -
mniejszy lub równy).
Konstrukcja kopca jest nieco bardziej skomplikowana od konstrukcji drzewa binarnego, ponieważ musimy dodatkowo troszczyć
się o zachowanie warunku kopca. Zatem po każdym dołączeniu do kopca nowego węzła, sprawdzamy odpowiednie warunki i
ewentualnie dokonujemy wymian węzłów na ścieżce wiodącej od dodanego węzła do korzenia.
Przykład:
Skonstruować kopiec z elementów zbioru {7 5 9 6 7 8 10}
Operacja Opis
Budowę kopca rozpoczynamy od pierwszego elementu zbioru, który staje się korzeniem.
7 5 9 6 7 8 10
7 5 9 6 7 8 10
7 5 9 6 7 8 10
7 5 9 6 7 8 10
7 5 9 6 7 8 10
Po wymianie tych węzłów warunek kopca obowiązuje w całym drzewie - zadanie jest
wykonane.
Charakterystyczną cechą kopca jest to, iż korzeń zawsze jest największym (w porządku malejącym najmniejszym) elementem z
całego drzewa binarnego.
Algorytm
Poniżej przedstawiamy algorytm konstrukcji kopca z elementów zbioru.
Specyfikacja problemu
Dane wejściowe
d[ ] - Zbiór zawierający elementy do wstawienia do kopca. Numeracja elementów rozpoczyna się od 1.
n - Ilość elementów w zbiorze, n Î N
Dane wyjściowe
d[ ] - Zbiór zawierający kopiec
Zmienne pomocnicze
i - zmienna licznikowa pętli umieszczającej kolejne elementy zbioru w kopcu, i Î N, i Î {2,3,...,n}
j,k - indeksy elementów leżących na ścieżce od wstawianego elementu do korzenia, j,k Î C
x - zmienna pomocnicza przechowująca tymczasowo element wstawiany do kopca
Lista kroków
K01: Dla i = 2, ..., n: wykonuj K02...K05
K02: j ← i; k ← j div 2
K03: x ← d[i]
K04: Dopóki (k > 0) (d[k] < x): wykonuj
d[j] ← d[k]
j ←k
k ← j div 2
K05: d[j] ← x
K06: Zakończ
Uwaga: przy porządku malejącym należy zmienić drugi warunek w kroku K04 z d[k] < x na d[k] > x.
Schemat blokowy
Algorytm tworzy kopiec w tym samym zbiorze wejściowym d[ ]. Nie wymaga zatem dodatkowych
Następnie rozpoczynamy pętlę warunkową nr 2, której zadaniem jest znalezienie w kopcu miejsca do
wstawienia zapamiętanego elementu w zmiennej x. Pętla ta wykonuje się do momentu osiągnięcia
korzenia kopca (k = 0) lub znalezienia przodka większego od zapamiętanego elementu. Wewnątrz
pętli przesuwamy przodka na miejsce potomka, aby zachować warunek kopca, a następnie
przesuwamy pozycję j na pozycję zajmowaną wcześniej przez przodka. Pozycja k staje się pozycją
nowego przodka i pętla się kontynuuje. Po jej zakończeniu w zmiennej j znajduje się numer pozycji w
zbiorze d[ ], na której należy umieścić element w x.
Po zakończeniu pętli nr 1 w zbiorze zostaje utworzona struktura kopca.
Programy
Programy tworzą kopiec z pseudolosowo wygenerowanych 15 elementów zbioru. Wynik działania jest następnie prezentowany w
formie drzewa binarnego.
DevPascal
// Konstruowanie kopca
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program kopiec;
const N = 31; // liczba elementów
var
d : array[1..N] of integer;
i,j,k,x : integer;
begin
randomize;
writeln(' Tworzenie kopca');
writeln('----------------------');
writeln('(C)2005 Jerzy Walaszek');
writeln;
// Inicjujemy zbiór d[] liczbami pseudolosowymi od 0 do 9
for i := 1 to N do d[i] := random(10);
// Budujemy kopiec
for i := 2 to N do
begin
j := i; k := j div 2;
x := d[i];
while (k > 0) and (d[k] < x) do
begin
d[j] := d[k];
j := k; k := j div 2;
end;
d[j] := x;
end;
// Prezentujemy wyniki
x := (N + 1) div 2; k := 2;
for i := 1 to N do
begin
for j := 1 to x - 1 do write(' ');
write(d[i]);
for j := 1 to x do write(' ');
if i + 1 = k then
begin
k := k + k; x := x div 2; writeln;
end;
end;
// Gotowe
writeln; writeln;
write('KONIEC, nacisnij klawisz Enter...');
readln;
end.
Code::Blocks
// Konstruowanie kopca
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <iostream>
#include <cstdlib>
#include <time.h>
using namespace std;
int main()
{
const int N = 31; // liczba elementów
int d[N + 1],i,j,k,x;
srand((unsigned)time(NULL));
cout << " Tworzenie kopca\n"
"----------------------\n"
"(C)2005 Jerzy Walaszek\n\n";
// Inicjujemy zbiór d[] liczbami pseudolosowymi od 0 do 9
for(i = 1; i <= N; i++) d[i] = rand() % 10;
// Budujemy kopiec
for(i = 2; i <= N; i++)
{
j = i; k = j / 2;
x = d[i];
while((k > 0) && (d[k] < x))
{
d[j] = d[k];
j = k; k = j / 2;
}
d[j] = x;
}
// Prezentujemy wyniki
x = (N + 1) / 2; k = 2;
for(i = 1; i <= N; i++)
{
for(j = 1; j <= x - 1; j++) cout << " ";
cout << d[i];
for(j = 1; j <= x; j++) cout << " ";
if(i + 1 == k)
{
k += k; x /= 2; cout << endl;
}
}
// Gotowe
cout << endl << endl;
return 0;
}
Free Basic
' Konstruowanie kopca
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Const N = 31 ' liczba elementów
Dim d(N) As Integer
Dim i As Integer, j As Integer, k As Integer, x As Integer
Randomize
Print " Tworzenie kopca"
Print "----------------------"
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmheap">
<h3 style="text-align: center">Tworzenie kopca</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Buduj Kopiec" name="B1">
</p>
<p style="TEXT-ALIGN: center">
<font face="Courier New"><span id="t_out">.</span></font>
</p>
</form>
<script language=javascript>
// Konstruowanie kopca
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
function main()
{
var N = 31; // liczba elementów
var d = new Array(N+1);
var i,j,k,x,t;
// Inicjujemy zbiór d[] liczbami pseudolosowymi od 0 do 9
for(i = 1; i <= N; i++) d[i] = Math.floor(Math.random() * 10);
// Budujemy kopiec
for(i = 2; i <= N; i++)
{
j = i; k = Math.floor(j / 2);
x = d[i];
while((k > 0) && (d[k] < x))
{
d[j] = d[k];
j = k; k = Math.floor(j / 2);
}
d[j] = x;
}
// Prezentujemy wyniki
t = ""; x = (N + 1) / 2; k = 2;
for(i = 1; i <= N; i++)
{
for(j = 1; j <= x - 1; j++) t += " ";
t += d[i];
for(j = 1; j <= x; j++) t += " ";
if(i + 1 == k)
{
k += k; x = Math.floor(x / 2); t += "<BR>";
}
}
// Gotowe
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Tworzenie kopca
(C)2012 I LO w Tarnowie - I LO w Tarnowie
Buduj Kopiec
.
Podsumowanie
W rozdziale opisaliśmy bardzo prosty algorytm pozwalający konstruować kopiec. Algorytm ten posiada pesymistyczną klasę
złożoności czasowej O(n log n) (liniowo logarytmiczną) - każde wstawienie nowego elementu na stos może wymagać tyle
przesunięć, ile jest węzłów kopca na ścieżce od miejsca wstawiania do korzenia drzewa. Nie trudno zauważyć, iż liczba ta jest
równa lub mniejsza od wysokości kopca, czyli log2n. Ponieważ operację tę wykonujemy (n - 1) razy, to dostajemy górne
oszacowanie właśnie O(n log n).
W przypadku optymistycznym, gdy dodanie nowego elementu nie narusza warunku kopca, algorytm ma klasę czasowej
złożoności obliczeniowej O(n) (liniową). Jednakże cecha ta nie bardzo się nam przyda przy sortowaniu, ponieważ zbiór ze
strukturą kopca nie tworzy zbioru uporządkowanego liniowo - bardzo łatwo się możesz o tym przekonać analizując podany na
początku rozdziału przykład.
Zobacz również na: | Drzewo binarne | Rozbiór kopca | Sortowanie przez kopcowanie
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Sortowanie stogowe
Heap Sort
Podrozdziały Tematy pokrewne
Algorytm Programy Drzewo binarne
Specyfikacja problemu Program w języku Pascal Tworzenie kopca
Lista kroków Program w języku C++ Sortowanie przez kopcowanie
Schemat blokowy Program w języku Basic
Program w języku JavaScript
Algorytm
W tym rozdziale nauczymy się rozbierać kopiec. Zasada jest następująca:
1. Zamień miejscami korzeń z ostatnim liściem, który wyłącz ze struktury kopca. Elementem pobieranym z kopca jest
zawsze jego korzeń, czyli element największy.
2. Jeśli jest to konieczne, przywróć warunek kopca idąc od korzenia w dół.
3. Kontynuuj od kroku 1, aż kopiec będzie pusty.
Przykład:
Dokonaj rozbioru następującego kopca:
Operacja Opis
9
Poprzednia operacja zaburzyła strukturę
kopca. Idziemy zatem od korzenia w dół
struktury przywracając warunek kopca -
przodek większy lub równy od swoich
potomków. Praktycznie polega to na
zamianie przodka z największym
potomkiem. Operację kontynuujemy
dotąd, aż natrafimy na węzły spełniające
warunek kopca.
89
789
5789
45789
245789
Operację rozbioru kopca można przeprowadzić w miejscu. Jeśli mamy zbiór d[ ] ze strukturą kopca, to idąc od końca zbioru
(ostatnie liście drzewa) w kierunku początku zamieniamy elementy z pierwszym elementem zbioru (korzeń drzewa), a następnie
poruszając się od korzenia w dół przywracamy warunek kopca w kolejno napotykanych węzłach.
Specyfikacja problemu
Dane wejściowe
d[ ] - Zbiór zawierający poprawną strukturę kopca. Elementy są numerowane począwszy od 1.
n - Ilość elementów w zbiorze, n Î N
Dane wyjściowe
d[ ] - Zbiór zawierający elementy pobrane z kopca ułożone w porządku rosnącym
Zmienne pomocnicze
i - zmienna licznikowa pętli pobierającej kolejne elementy z kopca, i Î N, i Î {n,n-1,...,2}
j,k - indeksy elementów leżących na ścieżce w dół od korzenia, j,k Î N
m - indeks większego z dwóch elementów potomnych, m Î N
Lista kroków
K01: Dla i = n, n - 1, ..., 2: wykonuj K02...K08
K02: d[1] ↔ d[i]
K03: j ← 1; k ← 2
K04: Dopóki (k < i) wykonuj K05...K08
K05: Jeżeli (k + 1 < i) (d[k + 1] > d[k]), to m ← k + 1
inaczej m ← k
K06: Jeżeli d[m] ≤ d[j], to wyjdź z pętli K04 i kontynuuj następny obieg K01
K07: d[j] ↔ d[m]
K08: j ← m; k ← j + j
K09: Zakończ
Schemat blokowy
Rozbiór kopca wykonywany jest w dwóch zagnieżdżonych pętlach. Pętla nr 1 zamienia
miejscami kolejne liście ze spodu drzewa z korzeniem. Zadaniem pętli nr 2 jest przywrócenie w
strukturze warunku kopca.
Pętla nr 1 przebiega wstecz indeksy elementów zbioru d[ ] poczynając od indeksu n a
kończąc na indeksie 2. Element i-ty jest wymieniany z korzeniem kopca. Po tej wymianie
Programy
Efekt uruchomienia programu
Rozbior kopca
----------------------
(C)2005 Jerzy Walaszek
Zbior wejsciowy ze struktura kopca:
9 7 8 0 6 2 6 0 0 0 4 0 0 6 4
9
7 8
0 6 2 6
0 0 0 4 0 0 6 4
Zbior wyjsciowy po rozbiorze kopca:
0 0 0 0 0 0 2 4 4 6 6 6 7 8 9
DevPascal
// Rozbiór kopca
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program kopiec;
const N = 15; // liczba elementów
var
d : array[1..N] of integer;
i,j,k,m,x : integer;
begin
randomize;
writeln(' Rozbior kopca');
writeln('----------------------');
writeln('(C)2005 Jerzy Walaszek');
writeln;
// W zbiorze d konstruujemy kopiec
d[1] := 9;
for i := 2 to N do d[i] := random(d[i div 2] + 1);
// Prezentujemy kopiec
writeln('Zbior wejsciowy ze struktura kopca:');
writeln;
for i := 1 to N do write(d[i]:2);
writeln; writeln;
x := (N + 1) div 2; k := 2;
for i := 1 to N do
begin
for j := 1 to x - 1 do write(' ');
write(d[i]);
for j := 1 to x do write(' ');
if i + 1 = k then
begin
k := k + k; x := x div 2; writeln;
end;
end;
// Rozbieramy kopiec
for i := N downto 2 do
begin
x := d[1]; d[1] := d[i]; d[i] := x;
j := 1; k := 2;
while k < i do
begin
if (k + 1 < i) and (d[k + 1] > d[k]) then
m := k + 1
else
m := k;
if d[m] <= d[j] then break;
x := d[j]; d[j] := d[m]; d[m] := x;
j := m; k := j + j;
end;
end;
// Wyświetlamy wynik rozbioru kopca
writeln;
writeln('Zbior wyjsciowy po rozbiorze kopca:');
writeln;
for i := 1 to N do write(d[i]:2);
// Gotowe
writeln; writeln;
write('KONIEC, nacisnij klawisz Enter...'); readln;
end.
Code::Blocks
// Rozbiór kopca
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
int main()
{
const int N = 15; // liczba elementów
int d[N + 1],i,j,k,m,x;
srand((unsigned)time(NULL));
cout << " Rozbior kopca\n"
"----------------------\n"
"(C)2005 Jerzy Walaszek\n\n"
"Zbior wejsciowy ze struktura kopca:\n\n";
// W zbiorze d konstruujemy kopiec
d[1] = 9;
for(i = 2; i <= N; i++) d[i] = rand() % (d[i / 2] + 1);
// Prezentujemy kopiec
for(i = 1; i <= N; i++) cout << setw(2) << d[i];
cout << endl << endl;
x = (N + 1) / 2; k = 2;
for(i = 1; i <= N; i++)
{
for(j = 1; j < x; j++) cout << " ";
cout << d[i];
for(j = 1; j <= x; j++) cout << " ";
if(i + 1 == k)
{
k += k; x /= 2; cout << endl;
}
}
// Rozbieramy kopiec
for(i = N; i > 1; i--)
{
swap(d[1], d[i]);
j = 1; k = 2;
while(k < i)
{
if((k + 1 < i) && (d[k + 1] > d[k]))
m = k + 1;
else
m = k;
Free Basic
' Rozbiór kopca
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Const N = 15 ' liczba elementów
Dim d(N) As Integer, i As Integer, j As Integer
Dim k As Integer, m As Integer, x As Integer
Randomize
Print " Rozbior kopca"
Print "----------------------"
Print "(C)2005 Jerzy Walaszek"
Print
' W zbiorze d konstruujemy kopiec
d(1) = 9
For i = 2 To N: d(i) = Int(Rnd * (d(i \ 2) + 1)): Next
' Prezentujemy kopiec
Print "Zbior wejsciowy ze struktura kopca:"
Print
For i = 1 To N: Print Using "##";d(i);:Next
Print: Print
x = (N + 1) \ 2: k = 2
For i = 1 To N
For j = 1 To x - 1: Print " ";: Next
Print Using "#";d(i);
For j = 1 To x: Print " ";: Next
If i + 1 = k Then
k = k + k: x = x \ 2: Print
End If
Next
' Rozbieramy kopiec
For i = N To 2 Step -1
Swap d(1), d(i)
j = 1: k = 2
While k < i
If (k + 1 < i) And (d(k + 1) > d(k)) Then
m = k + 1
Else
m = k
End If
If d(m) <= d(j) Then Exit While
Swap d(j), d(m)
j = m: k = j + j
Wend
Next
' Wyświetlamy wynik rozbioru kopca
Print
Print "Zbior wyjsciowy po rozbiorze kopca:"
Print
For i = 1 To N: Print Using "##";d(i);:Next
Print: Print
' Gotowe
Print "KONIEC, nacisnij klawisz Enter..."
Sleep
End
JavaScript
<html>
<head>
</head>
<body>
function main()
{
var N = 15; // liczba elementów
var d = new Array(N + 1);
var i,j,k,x,t;
// W zbiorze d konstruujemy kopiec
d[1] = 9;
for(i = 2; i <= N; i++)
d[i] = Math.floor(Math.random() * (d[Math.floor(i / 2)] + 1));
// Prezentujemy kopiec
t = "";
for(i = 1; i <= N; i++) t += d[i] + " ";
t += "<BR><BR>";
x = Math.floor((N + 1) / 2); k = 2;
for(i = 1; i <= N; i++)
{
for(j = 1; j < x; j++) t += " ";
t += d[i];
for(j = 1; j <= x; j++) t += " ";
if(i + 1 == k)
{
k += k; x = Math.floor(x / 2); t += "<BR>";
}
}
// Rozbieramy kopiec
for(i = N; i > 1; i--)
{
x = d[1]; d[1] = d[i]; d[i] = x;
j = 1; k = 2;
while(k < i)
{
if((k + 1 < i) && (d[k + 1] > d[k]))
m = k + 1;
else
m = k;
if(d[m] <= d[j]) break;
x = d[j]; d[j] = d[m]; d[m] = x;
j = m; k = j + j;
}
}
// Wyświetlamy wynik rozbioru kopca
t += "<BR>";
for(i = 1; i <= N; i++) t += d[i] + " ";
// Gotowe
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Rozbiór kopca
(C)2012 I LO w Tarnowie - I LO w Tarnowie
Rozbierz Kopiec
.
Zobacz również na: Drzewo binarne | Tworzenie kopca | Sortowanie przez kopcowanie
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Sortowanie stogowe
Heap Sort
Podrozdziały Tematy pokrewne
Algorytm rozbioru kopca Programy Badanie algorytmów sortujących Drzewo binarne
Specyfikacja problemu Program w języku Pascal Podsumowanie Tworzenie kopca
Lista kroków Program w języku C++ Zadania dla ambitnych Rozbiór kopca
Program w języku Basic
Program w języku JavaScript
Algorytm
Jeśli przeczytałeś uważnie poprzednie dwa rozdziały, to zasadę sortowania przez kopcowanie zrozumiesz od razu - w zbiorze
tworzymy kopiec, a następnie dokonujemy jego rozbioru. W wyniku wykonania tych dwóch operacji zbiór zostanie posortowany.
Specyfikacja problemu
Dane wejściowe
d[ ] - Zbiór zawierający elementy do posortowania, które są numerowane począwszy od 1.
n - Ilość elementów w zbiorze, n Î N
Dane wyjściowe
d[ ] - Zbiór zawierający elementy posortowane rosnąco
Lista kroków
K01: Twórz_kopiec
K02: Rozbierz_kopiec
K03: Zakończ algorytm
Ponieważ sortowanie przez kopcowanie składa się z dwóch następujących bezpośrednio po sobie operacji o klasie czasowej
złożoności obliczeniowej O(n log n), to dla całego algorytmu klasa złożoności również będzie wynosić O(n log n).
Programy
Efekt uruchomienia programu
Sortowanie przez kopcowanie
-----------------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
48 40 48 29 21 11 12 63 77 89 99 97 80 62 64 32 28 5 16 36
Po sortowaniu:
5 11 12 16 21 28 29 32 36 40 48 48 62 63 64 77 80 89 97 99
DevPascal
// Sortowanie przez kopcowanie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program heap_sort;
const N = 20; // liczebność zbioru
var
d : array[1..N] of integer;
i,j,k,m,x : integer;
begin
writeln(' Sortowanie przez kopcowanie ');
writeln('-----------------------------');
writeln(' (C)2005 Jerzy Walaszek');
writeln;
writeln('Przed sortowaniem:'); writeln;
// Wypełniamy tablicę liczbami pseudolosowymi i wyświetlamy je
randomize;
for i := 1 to N do
begin
d[i] := random(100); write(d[i] : 4);
end;
// Budujemy kopiec
for i := 2 to N do
begin
j := i; k := j div 2;
x := d[i];
while (k > 0) and (d[k] < x) do
begin
d[j] := d[k];
j := k; k := j div 2;
end;
d[j] := x;
end;
// Rozbieramy kopiec
for i := N downto 2 do
begin
x := d[1]; d[1] := d[i]; d[i] := x;
j := 1; k := 2;
while k < i do
begin
if (k + 1 < i) and (d[k + 1] > d[k]) then
m := k + 1
else
m := k;
if d[m] <= d[j] then break;
x := d[j]; d[j] := d[m]; d[m] := x;
j := m; k := j + j;
end;
end;
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln('Nacisnij Enter...');
readln;
end.
Code::Blocks
// Sortowanie przez kopcowanie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
int main()
{
const int N = 20; // liczebność zbioru
int d[N + 1],i,j,k,m,x;
srand((unsigned)time(NULL));
cout << " Sortowanie przez kopcowanie \n"
"-----------------------------\n"
" (C)2005 Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Wypełniamy tablicę liczbami pseudolosowymi i wyświetlamy je
for(i = 1; i <= N; i++)
{
d[i] = rand() % 100; cout << setw(4) << d[i];
}
cout << endl;
// Budujemy kopiec
for(i = 2; i <= N; i++)
{
j = i; k = j / 2;
x = d[i];
while((k > 0) && (d[k] < x))
{
d[j] = d[k];
j = k; k = j / 2;
}
d[j] = x;
}
// Rozbieramy kopiec
for(i = N; i > 1; i--)
{
swap(d[1], d[i]);
j = 1; k = 2;
while(k < i)
{
if((k + 1 < i) && (d[k + 1] > d[k]))
m = k + 1;
else
m = k;
if(d[m] <= d[j]) break;
swap(d[j], d[m]);
j = m; k = j + j;
}
}
// Wyświetlamy wynik sortowania
cout << "Po sortowaniu:\n\n";
for(i = 1; i <= N; i++) cout << setw(4) << d[i];
cout << endl;
return 0;
}
Free Basic
' Sortowanie przez kopcowanie
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Const N = 20 ' liczebność zbioru
Dim d(N) As Integer, i As Integer, j As Integer
Dim k As Integer, m As Integer, x As Integer
Print " Sortowanie przez kopcowanie"
Print "-----------------------------"
Print " (C)2005 Jerzy Walaszek"
Print
Print "Przed sortowaniem:": Print
' Wypełniamy tablicę liczbami pseudolosowymi i wyświetlamy je
Randomize
For i = 1 To N
d(i) = Int(Rnd * 100): Print Using "####";d(i);
Next
Print
' Budujemy kopiec
For i = 2 To N
j = i: k = j \ 2
x = d(i)
While (k > 0) And (d(k) < x)
d(j) = d(k)
j = k: k = j / 2
Wend
d(j) = x
Next
' Rozbieramy kopiec
For i = N To 2 Step -1
Swap d(1), d(i)
j = 1: k = 2
While k < i
If (k + 1 < i) And (d(k + 1) > d(k)) Then
m = k + 1
Else
m = k
End If
If d(m) <= d(j) Then Exit While
Swap d(j), d(m)
j = m: k = j + j
Wend
Next
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmheapsort">
<h3 style="text-align: center">Sortowanie Przez Kopcowanie</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Przez Kopcowanie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
function main()
{
var N = 20; // liczba elementów
var d = new Array(N + 1);
var i,j,k,x,t;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
for(i = 1; i <= N; i++) d[i] = Math.floor(Math.random() * 100);
t = "Przed sortowaniem:<BR><BR>";
for(i = 1; i <= N; i++) t += d[i] + " ";
t += "<BR><BR>";
// Budujemy kopiec
for(i = 2; i <= N; i++)
{
j = i; k = Math.floor(j / 2);
x = d[i];
while((k > 0) && (d[k] < x))
{
d[j] = d[k];
j = k; k = Math.floor(j / 2);
}
d[j] = x;
}
// Rozbieramy kopiec
for(i = N; i > 1; i--)
{
x = d[1]; d[1] = d[i]; d[i] = x;
j = 1; k = 2;
while(k < i)
{
if((k + 1 < i) && (d[k + 1] > d[k]))
m = k + 1;
else
m = k;
if(d[m] <= d[j]) break;
x = d[j]; d[j] = d[m]; d[m] = x;
j = m; k = j + j;
}
}
// Wyświetlamy wynik sortowania
t += "Po sortowaniu:<BR><BR>";
for(i = 1; i <= N; i++) t += d[i] + " ";
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Sortuj
...
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika):
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu sortowania przez kopcowanie
wyciągamy następujące wnioski:
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
O(n log n) O(n log n) O(n log n) O(n log n) O(n log n)
Sortowanie przez kopcowanie
tpo ≈ 7tod tpp ≈ tpk tnp ≈ 4tod
6 3
1. Wszystkie badane czasy są proporcjonalne do nlog2n, zatem wnioskujemy, iż algorytm sortowania przez kopcowanie
posiada klasę czasowej złożoności obliczeniowej równą O(n log n).
2. Najdłużej trwa sortowanie zbioru nieposortowanego. Otrzymane czasy nie różnią się wiele od siebie, co sugeruje, iż
algorytm jest mało czuły na postać danych wejściowych.
3. Ciekawostką jest to, iż czas sortowania zbiorów posortowanych jest dłuższy od sortowania zbioru posortowanego odwrotnie
(jest to najkrótszy czas z otrzymanych, zatem możemy przyjąć, iż dla algorytmu sortowania przez kopcowanie
przypadkiem optymistycznym jest właśnie sortowanie zbioru posortowanego odwrotnie).
2 2 2 2 5
Sortowanie przez kopcowanie
źle źle źle źle źle
4. Z porównania czasów sortowania wynika jasno, iż przedstawiony tutaj algorytm sortowania przez kopcowanie jest około dwa
razy wolniejszy od wcześniej podanego algorytmu sortowania przez scalanie. Zaletą jest sortowanie w miejscu - poprzedni
algorytm wymaga dodatkowej pamięci, co może go dyskwalifikować przy sortowaniu dużych zbiorów danych. Do dalszych
porównań czasów sortowania będziemy dalej stosowali algorytm sortowania przez scalanie.
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Sortowanie szybkie
Quick Sort
Podrozdziały
Algorytm Programy Badanie algorytmów sortujących
Tworzenie partycji Program w języku Pascal Podsumowanie
Specyfikacja problemu Program w języku C++ Zadania dla ambitnych
Lista kroków Program w języku Basic
Schemat blokowy Program w języku JavaScript
Algorytm
Algorytm sortowania szybkiego opiera się na strategii "dziel i zwyciężaj" (ang. divide and conquer), którą możemy krótko
scharakteryzować w trzech punktach:
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
DZIEL : najpierw sortowany zbiór dzielimy na dwie części w taki sposób, aby wszystkie elementy leżące w
pierwszej części (zwanej lewą partycją) były mniejsze lub równe od wszystkich elementów drugiej
części zbioru (zwanej prawą partycją).
ZWYCIĘŻAJ : każdą z partycji sortujemy rekurencyjnie tym samym algorytmem.
POŁĄCZ : połączenie tych dwóch partycji w jeden zbiór daje w wyniku zbiór posortowany.
Sortowanie szybkie zostało wynalezione przez angielskiego informatyka, profesora Tony'ego Hoare'a w latach
60-tych ubiegłego wieku. W przypadku typowym algorytm ten 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(n2), co więcej, 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 -
prof. Tony Hoare czasem lepszym rozwiązaniem może być zastosowanie wcześniej opisanego algorytmu sortowania przez
kopcowanie, który nigdy nie degraduje się do klasy O(n2).
Tworzenie partycji
Do utworzenia partycji musimy ze zbioru wybrać jeden z elementów, który nazwiemy piwotem. W lewej partycji znajdą się
wszystkie elementy niewiększe od piwotu, a w prawej partycji umieścimy wszystkie elementy niemniejsze od piwotu. Położenie
elementów równych nie wpływa na proces sortowania, zatem 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:
Dzielenie na partycje polega na umieszczeniu dwóch wskaźników na początku zbioru - i oraz j. Wskaźnik i przebiega przez zbiór
poszukując wartości mniejszych od piwotu. Po znalezieniu takiej wartości jest ona wymieniana z elementem na pozycji j. Po tej
operacji wskaźnik j jest przesuwany na następną pozycję. Wskaźnik j zapamiętuje pozycję, na którą trafi następny element oraz
na końcu wskazuje miejsce, gdzie znajdzie się piwot. W trakcie podziału piwot jest bezpiecznie przechowywany na ostatniej
pozycji w zbiorze.
Przykład:
Dla przykładu podzielimy na partycje zbiór:
{72473146583926763}
7 2 4 7 3 1 4 6 3 8 3 9 2 6 7 6 5
4. i Wskaźnikiem i szukamy elementu mniejszego od piwotu
j
2 7 4 7 3 1 4 6 3 8 3 9 2 6 7 6 5
6. i Szukamy
j
2 4 7 7 3 1 4 6 3 8 3 9 2 6 7 6 5
7. i Wymieniamy i przesuwamy j.
j
2 4 7 7 3 1 4 6 3 8 3 9 2 6 7 6 5
8. i Szukamy
j
2 4 3 7 7 1 4 6 3 8 3 9 2 6 7 6 5
9. i Wymieniamy i przesuwamy j.
j
2 4 3 7 7 1 4 6 3 8 3 9 2 6 7 6 5
10. i Szukamy
j
2 4 3 1 7 7 4 6 3 8 3 9 2 6 7 6 5
11. i Wymieniamy i przesuwamy j.
j
2 4 3 1 7 7 4 6 3 8 3 9 2 6 7 6 5
12. i Szukamy
j
2 4 3 1 4 7 7 6 3 8 3 9 2 6 7 6 5
13. i Wymieniamy i przesuwamy j.
j
2 4 3 1 4 7 7 6 3 8 3 9 2 6 7 6 5
14. i Szukamy
j
2 4 3 1 4 3 7 6 7 8 3 9 2 6 7 6 5
15. i Wymieniamy i przesuwamy j.
j
2 4 3 1 4 3 7 6 7 8 3 9 2 6 7 6 5
16. i Szukamy
j
2 4 3 1 4 3 3 6 7 8 7 9 2 6 7 6 5
17. i Wymieniamy i przesuwamy j.
j
2 4 3 1 4 3 3 6 7 8 7 9 2 6 7 6 5
18. i Szukamy
j
2 4 3 1 4 3 3 2 7 8 7 9 6 6 7 6 5
19. i Wymieniamy i przesuwamy j.
j
Po zakończeniu podziału na partycje wskaźnik j wyznacza pozycję piwotu. Lewa partycja zawiera elementy mniejsze od piwotu i
rozciąga się od początku zbioru do pozycji j - 1. Prawa partycja zawiera elementy większe lub równe piwotowi i rozciąga się od
pozycji j + 1 do końca zbioru. Operacja podziału na partycje ma liniową klasę złożoności obliczeniowej - O(n).
Specyfikacja problemu
Sortuj_szybko(lewy, prawy)
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
Lista kroków
lewy + prawy
K01: i ← [ ]
2
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
Algorytm sortowania szybkiego wywołujemy podając za lewy indeks pierwszego elementu zbioru, a za prawy indeks elementu
ostatniego (czyli Sortuj_szybko(1,n)). Zakres indeksów jest dowolny - dzięki temu ten sam algorytm może również sortować
fragment zbioru, co wykorzystujemy przy sortowaniu wyliczonych partycji.
Schemat blokowy
Na element podziałowy wybieramy element leżący w środku dzielonej
partycji. Wyliczamy jego pozycję i zapamiętujemy ją tymczasowo w
zmiennej i. Robimy to po to, aby dwukrotnie nie wykonywać tych samych
rachunków.
Element d[i] zapamiętujemy w zmiennej piwot, a do d[i] zapisujemy ostatni
element partycji. Dzięki tej operacji piwot został usunięty ze zbioru.
Ustawiamy zmienną j na początek partycji. Zmienna ta zapamiętuje pozycję
podziału partycji.
W pętli sterowanej zmienną i przeglądamy kolejne elementy od pierwszego
do przedostatniego (ostatni został umieszczony na pozycji piwotu, a piwot
zapamiętany). Jeśli i-ty element jest mniejszy od piwotu, to trafia on na
początek partycji - wymieniamy ze sobą elementy na pozycjach i-tej i j-tej.
Po tej operacji przesuwamy punkt podziałowy partycji j.
Po zakończeniu pętli element z pozycji j-tej przenosimy na koniec partycji,
aby zwolnić miejsce dla piwotu, po czym wstawiamy tam piwot. Zmienna j
wskazuje zatem wynikową pozycję piwotu. Pierwotna partycja została
podzielona na dwie partycje:
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.
Programy
Efekt uruchomienia programu
Sortowanie szybkie
------------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
46 5 56 35 75 95 33 93 4 71 8 5 69 50 35 34 65 32 72 61
Po sortowaniu:
4 5 5 8 32 33 34 35 35 46 50 56 61 65 69 71 72 75 93 95
DevPascal
// Sortowanie Szybkie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program Quick_Sort;
const N = 20; // Liczebność zbioru.
var
d : array[1..N] of integer;
// Procedura sortowania szybkiego
//-------------------------------
procedure Sortuj_szybko(lewy, prawy : integer);
var
i,j,piwot,x : integer;
begin
i := (lewy + prawy) div 2;
piwot := d[i]; d[i] := d[prawy];
j := lewy;
for i := lewy to prawy - 1 do
if d[i] < piwot then
begin
x := d[i]; d[i] := d[j]; d[j] := x;
inc(j);
end;
d[prawy] := d[j]; d[j] := piwot;
if lewy < j - 1 then Sortuj_szybko(lewy, j - 1);
if j + 1 < prawy then Sortuj_szybko(j + 1, prawy);
end;
// Program główny
//---------------
var
i : integer;
begin
writeln(' Sortowanie szybkie');
writeln('------------------------');
writeln(' (C)2005 Jerzy Walaszek ');
writeln;
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
randomize;
for i := 1 to N do d[i] := random(100);
writeln('Przed sortowaniem:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
// Sortujemy
Sortuj_szybko(1,N);
// Wyświetlamy wynik sortowania
writeln('Po sortowaniu:'); writeln;
for i := 1 to N do write(d[i] : 4);
writeln;
writeln('Nacisnij Enter...');
readln;
end.
Code::Blocks
// Sortowanie Szybkie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 20; // Liczebność zbioru.
int d[N];
// Procedura sortowania szybkiego
//-------------------------------
void Sortuj_szybko(int lewy, int prawy)
{
int i,j,piwot;
i = (lewy + prawy) / 2;
piwot = d[i]; d[i] = d[prawy];
for(j = i = lewy; i < prawy; i++)
if(d[i] < piwot)
{
swap(d[i], d[j]);
j++;
}
d[prawy] = d[j]; d[j] = piwot;
if(lewy < j - 1) Sortuj_szybko(lewy, j - 1);
if(j + 1 < prawy) Sortuj_szybko(j + 1, prawy);
}
// Program główny
//---------------
int main()
{
int i;
srand((unsigned)time(NULL));
cout << " Sortowanie szybkie\n"
"------------------------\n"
" (C)2005 Jerzy Walaszek \n\n"
"Przed sortowaniem:\n\n";
// Najpierw wypełniamy tablicę d[] liczbami pseudolosowymi
// a następnie wyświetlamy jej zawartość
for(i = 0; i < N; i++) d[i] = rand() % 100;
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// Sortujemy
Sortuj_szybko(0,N - 1);
// Wyświetlamy wynik sortowania
cout << "Po sortowaniu:\n\n";
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
return 0;
}
Free Basic
' Sortowanie szybkie
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Declare Sub Sortuj_szybko(lewy As Integer, prawy As Integer)
Const N = 20 ' liczebność zbioru
Dim Shared d(N) As Integer
Dim i As Integer
Print " Sortowanie szybkie"
Print "-----------------------"
Print "(C)2005 Jerzy Walaszek"
Print
Print "Przed sortowaniem:": Print
' Wypełniamy tablicę liczbami pseudolosowymi i wyświetlamy je
Randomize
For i = 1 To N
d(i) = Int(Rnd * 100): Print Using "####";d(i);
Next
Print
' Sortujemy
Sortuj_szybko(1,N)
' Wyświetlamy wynik sortowania
Print "Po sortowaniu:": Print
For i = 1 To N: Print Using "####";d(i);: Next
Print
Print "Nacisnij Enter..."
Sleep
End
' Procedura sortowania szybkiego
'-------------------------------
Sub Sortuj_szybko(lewy As Integer, prawy As Integer)
Dim As Integer i, j, piwot
i = (lewy + prawy) \ 2
piwot = d(i): d(i) = d(prawy)
j = lewy
For i = lewy To prawy - 1
If d(i) < piwot Then
Swap d(i), d(j)
j += 1
End If
Next
d(prawy) = d(j): d(j) = piwot
If lewy < j - 1 Then Sortuj_szybko(lewy, j - 1)
If j + 1 < prawy Then Sortuj_szybko(j + 1, prawy)
End Sub
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmquicksort">
<h3 style="text-align: center">Sortowanie Szybkie</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Szybkie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 20; // Liczebność zbioru.
var d = new Array(N)
// Procedura sortowania szybkiego
//-------------------------------
function Sortuj_szybko(lewy, prawy)
{
var i,j,piwot,x;
i = Math.floor((lewy + prawy) / 2);
piwot = d[i]; d[i] = d[prawy];
for(j = i = lewy; i < prawy; i++)
if(d[i] < piwot)
{
x = d[i]; d[i] = d[j]; d[j] = x;
j++;
}
d[prawy] = d[j]; d[j] = piwot;
if(lewy < j - 1) Sortuj_szybko(lewy, j - 1);
if(j + 1 < prawy) Sortuj_szybko(j + 1, prawy);
}
// Program główny
//---------------
function main()
{
var i,t;
Sortowanie Szybkie
(C)2012 I LO w Tarnowie - I LO w Tarnowie
Sortuj
...
writeln;
write('Nacisnij klawisz ENTER...'); readln;
end.
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika):
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu sortowania szybkiego
wyciągamy następujące wnioski:
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
O(n log n) O(n log n) O(n log n) O(n log n) O(n log n)
Sortowanie szybkie
tpo ≈ tod tpp ≈ tpk tnp ≈ 8tod
3
1. Wszystkie otrzymane czasy sortowania są proporcjonalne do iloczynu n log2 n, wnioskujemy zatem, iż klasa złożoności
obliczeniowej algorytmu sortowania szybkiego jest równa O(n log n).
2. Czasy sortowania dla poszczególnych przypadków są mniej więcej tego samego rzędu, zatem nie wystąpił tutaj przypadek
pesymistyczny (zwróć uwagę na istotny fakt - to, co dla jednego algorytmu jest przypadkiem pesymistycznym, dla innego
wcale nie musi takie być).
3. Czas sortowania zbiorów nieuporządkowanych jest wyraźnie dłuższy od czasów sortowania zbiorów uporządkowanych
częściowo.
4. Czas sortowania zbioru uporządkowanego oraz uporządkowanego odwrotnie jest praktycznie taki sam.
8 5 5
Sortowanie przez scalanie ≈3 ≈ ≈ ≈ ≈1
Sortowanie szybkie
3 2 2
dobrze dobrze dobrze dobrze brak
5. Otrzymane wyniki potwierdzają, iż algorytm sortowania szybkiego jest najszybszym algorytmem sortującym. Jednakże w
przypadku ogólnym notujemy jedynie bardzo nieznaczny wzrost prędkości sortowania w stosunku do algorytmu sortowania
przez scalanie.
6. Ponieważ jak dotąd algorytm sortowania szybkiego jest najszybszym algorytmem sortującym, do dalszych porównań
czasów sortowania zastosujemy czasy uzyskane w tym algorytmie.
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Sortowanie rozrzutowe
Distribution Sort
Podrozdziały Tematy pokrewne
Algorytm Programy Sortowanie kubełkowe 1
Lista kroków Program w języku Pascal Listy
Program w języku C++ Sortowanie kubełkowe 2
Program w języku Basic Sortowanie przez zliczanie
Program w języku JavaScript Sortowanie pozycyjne
Algorytm
Wszystkie opisane dotychczas algorytmy sortujące opierają się na sprawdzaniu uporządkowania zbioru, które polega na
porównywaniu elementów. Udowodniono, iż barierą efektywności takich algorytmów w przypadku ogólnym (sortowanie zbioru o
losowym rozkładzie elementów) jest klasa złożoności obliczeniowej O(n log n). Zachodzi zatem naturalne pytanie: czy istnieją
inne sposoby sortowania o niższej klasie złożoności obliczeniowej?
Na samym początku III części "Sztuki Programowania Komputerów" prof. Donald Knuth opisuje znany od dawna sposób
porządkowania talii kart.
Ustalmy kolejność kolorów kart wg ich starszeństwa (pierwszy pik, ostatni trefl):
♠ - pik
♥ - kier
♦ - karo
♣ - trefl
Teraz ustalmy kolejność figur (dla uproszczenia przyjmujemy talię z 24 kart, chociaż algorytm jest również poprawny dla pełnej talii
52 kart):
A - as
K - król
D - dama
W - walet
T - dziesiątka
9 - dziewiątka
Przykład:
Załóżmy, iż mamy potasowaną losowo talię 24 kart:
♥D ♦K ♣K ♥9 ♠D ♣W ♠A ♥K ♣9 ♦T ♦A ♠K ♥T ♠W ♥A ♣D ♦D ♥W ♠9 ♣A ♦9 ♦W ♠T ♣T
Chcemy je posortować szybko tak, aby najpierw występowały piki, później kiery, kara a na końcu trefle. W obrębie
każdego koloru karty powinny być uporządkowane wg podanej powyżej kolejności. Postępujemy tak:
Najpierw kolejne karty układamy na 6 stosów wg figur (kolorem się chwilowo nie przejmujemy):
A K D W T 9
♠A ♦K ♥D ♣W ♦T ♥9
♦A ♣K ♠D ♠W ♥T ♣9
♥A ♥K ♣D ♥W ♠T ♠9
♣A ♠K ♦D ♦W ♣T ♦9
Poszczególne stosy łączymy ze sobą w talie kart - karty pobieramy z kupek w tej samej kolejności, w której były na
nie wstawiane (u nas wstawianie rozpoczęliśmy od góry, zatem również od góry rozbieramy każdą z kupek):
♠A ♦A ♥A ♣A ♦K ♣K ♥K ♠K ♥D ♠D ♣D ♦D ♣W ♠W ♥W ♦W ♦T ♥T ♠T ♣T ♥9 ♣9 ♠9 ♦9
♠ ♥ ♦ ♣
♠A ♥A ♦A ♣A
♠K ♥K ♦K ♣K
♠D ♥D ♦D ♣D
♠W ♥W ♦W ♣W
♠T ♥T ♦T ♣T
♠9 ♥9 ♦9 ♣9
♠A ♠K ♠D ♠W ♠T ♠9 ♥A ♥K ♥D ♥W ♥T ♥9 ♦A ♦K ♦D ♦W ♦T ♦9 ♣A ♣K ♣D ♣W ♣T ♣9
Lista kroków
K01: Przygotuj miejsce na tyle stosów, ile figur mogą mieć karty. Pierwszy stos będzie dla najstarszej karty, a ostatni dla
najmłodszej.
K02: Rozdziel poszczególne karty na przygotowane wcześniej stosy wg figur - np. wszystkie asy idą do stosu asów, króle idą do
stosu króli, itd.
K03: Złóż ze sobą karty w kolejnych stosach poczynając od stosu zawierającego najstarsze figury, a kończąc na stosie z
najmłodszymi figurami.
K04: Przygotuj miejsce dla czterech stosów kolorów. Stosy powinny być ułożone w kolejności starszeństwa kolorów, np. piki,
kiery, karo, trefle.
K05: Rozdziel karty na poszczególne stosy wg ich kolorów.
K06: Złóż ze sobą karty z kolejnych stosów poczynając od stosu zawierającego karty w najstarszym kolorze a kończąc na
stosie z kartami w najmłodszym kolorze. Talia zostanie uporządkowana
K07: Zakończ algorytm
Zwróć uwagę, iż przedstawiona metoda w ogóle nie porównuje elementów ze sobą. Elementy trafiają do odpowiednich stosów (są
rozrzucane - stąd nazwa sortowanie rozrzutowe) na podstawie swojej wartości, a nie na podstawie ich relacji z innymi elementami
zbioru. Dzięki takiemu podejściu zmieniona zostaje klasa czasowej złożoności obliczeniowej:
Sumarycznie cały algorytm będzie posiadał klasę złożoności O(n + m). Jest to klasa liniowa, zatem sortowanie będzie bardzo
szybkie. Co więcej, algorytm tego typu nie posiada przypadku pesymistycznego - czas sortowania każdego zbioru danych jest
porównywalny. Mankament tego rozwiązania stanowi dodatkowe zapotrzebowanie na pamięć O(n) - musimy zarezerwować
komórki na elementy przechowywane w stosach.
Programy
W programach wykorzystuje się opisany algorytm sortowania kart, który może stanowić część większego programu gry w karty (to
już pozostawiamy inwencji czytelnika). Od razu zastrzegam, iż problem sortowania kart rozwiązuje się o wiele prościej, jednakże
tutaj chodzi o przedstawienie sposobu realizacji opisanego algorytmu. Zatem program ma raczej wartość dydaktyczną niż
użytkową.
DevPascal
// Sortowanie Kart
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program skart;
uses Crt;
// Definicje typów danych
//--------------------------------------------------------
type
TKolor = (K_PIK,K_KIER,K_KARO,K_TREFL,K_PUSTY);
TFigura = (F_A,F_K,F_D,F_W,F_10,F_9,F_8,F_7,F_6,F_5,F_4,F_3,F_2,F_0);
TKarta = record
Kolor : TKolor;
Figura : TFigura;
end;
// Definicje zmiennych globalnych
//--------------------------------------------------------
var
talia : array[1..52] of TKarta;
gracz : array[1..4,1..13] of TKarta;
// Definicje procedur i funkcji
//--------------------------------------------------------
// Procedura inicjuje talię kart
//--------------------------------------------------------
procedure Inicjuj_talie;
var
i : integer;
k : TKolor;
f : TFigura;
begin
k := K_PIK; f := F_A;
for i := 1 to 52 do
begin
talia[i].Kolor := k; talia[i].Figura := f;
inc(f);
if f = F_0 then
begin
inc(k); f := F_A;
end
end;
end;
// Procedura tasuje talię kart
//--------------------------------------------------------
procedure Tasuj_talie;
var
i,a,b : integer;
x : TKarta;
begin
for i := 1 to 1000 do
begin
a := 1 + random(52); b := 1 + random(52);
x := talia[a]; talia[a] := talia[b]; talia[b] := x;
end;
end;
// Procedura rozdaje karty poszczególnym graczom
//--------------------------------------------------------
procedure Rozdaj_karty;
var
i,j,k : integer;
begin
k := 1;
for i := 1 to 4 do
for j := 1 to 13 do
begin
gracz[i,j] := talia[k];
inc(k);
end;
end;
// Procedura sortuje karty gracza wg kolorów i figur
//--------------------------------------------------------
procedure Sortuj_karty(g : integer);
var
karty : array[0..3,0..12] of TKarta;
lfig : array[TFigura] of integer;
lkol : array[TKolor] of integer;
f : TFigura;
k : TKolor;
i,j : integer;
begin
// Ustawiamy liczniki figur
for f := F_A to F_2 do lfig[f] := 0;
// Przeglądamy rękę gracza i umieszczamy kolejne karty w tablicy
// figur wg figury
for i := 1 to 13 do
begin
f := gracz[g,i].Figura;
karty[lfig[f],ord(f)] := gracz[g,i];
inc(lfig[f]);
end;
// Przeglądamy tablicę figur pobierając z niej karty do ręki gracza
i := 1;
for f := F_A to F_2 do
for j := 0 to lfig[f] - 1 do
begin
gracz[g,i] := karty[j,ord(f)];
inc(i);
end;
// Ustawiamy liczniki kolorów
for k := K_PIK to K_TREFL do lkol[k] := 0;
// Przeglądamy rękę gracza i umieszczamy kolejne karty w tablicy
// kolorów wg koloru karty
for i := 1 to 13 do
begin
k := gracz[g,i].Kolor;
karty[ord(k),lkol[k]] := gracz[g,i];
inc(lkol[k]);
end;
// Przeglądamy tablicę kolorów pobierając z niej karty do ręki gracza
i := 1;
for k := K_PIK to K_TREFL do
for j := 0 to lkol[k] - 1 do
begin
gracz[g,i] := karty[ord(k),j];
inc(i);
end;
end;
// Procedura wyświetla karty gracza wg jego numeru:
// 1 : gracz u góry (15,1)
// 2 : gracz po prawej (30,6)
// 3 : gracz u dołu (15,11)
// 4 : gracz po lewej (1,6)
// okna konsoli. Wydruk zajmuje 4 wiersze, w każdym do 15 znaków.
// Figurę 10 wyświetlamy jako T - każda karta powinna zajmować jeden
// znak, a zapis 10 wymaga dwóch znaków.
//--------------------------------------------------------
procedure Wyswietl_karty(g : integer);
const
kolory : string[4] = (#6#3#4#5);
figury : string[13] = ('AKDWT98765432');
px : array[1..4] of integer = (15,30,15,1);
py : array[1..4] of integer = (1,6,11,6);
var
i : integer;
k : TKolor;
begin
for k := K_PIK to K_TREFL do
begin
gotoXY(px[g], py[g] + ord(k));
write(kolory[1 + ord(k)],' ');
for i := 1 to 13 do
if gracz[g,i].Kolor = k then
write(figury[1 + ord(gracz[g,i].Figura)]);
end;
end;
//---------------
// Program główny
//---------------
var
i : integer;
begin
Randomize;
Inicjuj_talie;
Tasuj_talie;
Rozdaj_karty;
for i := 1 to 4 do
begin
Sortuj_karty(i);
Wyswietl_karty(i);
end;
gotoXY(1,16); writeln('Gotowe. Nacisnij klawisz Enter...');
readln;
end.
Code::Blocks
// Sortowanie Kart
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <iostream>
#include <windows.h>
#include <cstdlib>
#include <time.h>
using namespace std;
// Definicje typów danych
//--------------------------------------------------------
enum TKolor {K_PIK,K_KIER,K_KARO,K_TREFL,K_PUSTY};
enum TFigura {F_A,F_K,F_D,F_W,F_10,F_9,F_8,F_7,F_6,F_5,F_4,F_3,F_2,F_0};
struct TKarta
{
TKolor Kolor;
TFigura Figura;
};
//--------------------------------------------------------
void Sortuj_karty(int g)
{
TKarta karty[4][13];
int lfig[13], lkol[4],i,j;
TFigura f;
TKolor k;
// Ustawiamy liczniki figur
for(f = F_A; f < F_0; f++) lfig[f] = 0;
// Przeglądamy rękę gracza i umieszczamy kolejne karty w tablicy
// figur wg figury
for(i = 0; i < 13; i++)
{
f = gracz[g][i].Figura;
karty[lfig[f]++][f] = gracz[g][i];
}
// Przeglądamy tablicę figur pobierając z niej karty do ręki gracza
i = 0;
for(f = F_A; f < F_0; f++)
for(j = 0; j < lfig[f]; j++) gracz[g][i++] = karty[j][f];
// Ustawiamy liczniki kolorów
for(k = K_PIK; k < K_PUSTY; k++) lkol[k] = 0;
// Przeglądamy rękę gracza i umieszczamy kolejne karty w tablicy
// kolorów wg koloru karty
for(i = 0; i < 13; i++)
{
k = gracz[g][i].Kolor;
karty[k][lkol[k]++] = gracz[g][i];
}
// Przeglądamy tablicę kolorów pobierając z niej karty do ręki gracza
i = 0;
for(k = K_PIK; k < K_PUSTY; k++)
for(j = 0; j < lkol[k]; j++) gracz[g][i++] = karty[k][j];
}
// Procedura wyświetla karty gracza wg jego numeru:
// 1 : gracz u góry (15,1)
// 2 : gracz po prawej (30,6)
// 3 : gracz u dołu (15,11)
// 4 : gracz po lewej (1,6)
// okna konsoli. Wydruk zajmuje 4 wiersze, w każdym do 15 znaków.
// Figurę 10 wyświetlamy jako T - każda karta powinna zajmować jeden
// znak, a zapis 10 wymaga dwóch znaków.
//--------------------------------------------------------
void Wyswietl_karty(int g)
{
const char kolory[4] = {6,3,4,5};
const char * figury = "AKDWT98765432";
const int px[4] = {15,30,15,1};
const int py[4] = {1,6,11,6};
int i;
TKolor k;
for(k = K_PIK; k < K_PUSTY; k++)
{
gotoXY(px[g],py[g] + k);
cout << kolory[k] << " ";
for(i = 0; i < 13; i++)
if(gracz[g][i].Kolor == k) cout << figury[gracz[g][i].Figura];
}
}
//---------------
// Program główny
//---------------
int main()
{
int i;
srand((unsigned)time(NULL));
Inicjuj_talie();
Tasuj_talie();
Rozdaj_karty();
for(i = 0; i < 4; i++)
{
Sortuj_karty(i); Wyswietl_karty(i);
}
gotoXY(1,16);
return 0;
}
Free Basic
' Sortowanie Kart
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
' Definicje typów danych
'--------------------------------------------------------
Option Explicit
Enum TKolor
K_PIK
K_KIER
K_KARO
K_TREFL
K_PUSTY
End Enum
Enum TFigura
F_A
F_K
F_D
F_W
F_10
F_9
F_8
F_7
F_6
F_5
F_4
F_3
F_2
F_0
End Enum
Type TKarta
Kolor As TKolor
Figura As TFigura
End Type
' Definicje zmiennych globalnych
'--------------------------------------------------------
Dim Shared talia(52) As TKarta
Dim Shared gracz(4,13) As TKarta
' Definicje procedur i funkcji
'--------------------------------------------------------
' Procedura inicjuje talię kart
'--------------------------------------------------------
Declare Sub Inicjuj_talie()
Sub Inicjuj_talie()
Dim i As Integer
Dim k As TKolor
Dim f As TFigura
k = K_PIK: f = F_A
For i = 1 To 52
talia(i).Kolor = k: talia(i).Figura = f
f += 1
If f = F_0 Then
k += 1: f = F_A
End If
Next
End Sub
' Procedura tasuje talię kart
'--------------------------------------------------------
Declare Sub Tasuj_talie()
Sub Tasuj_talie()
Dim As Integer i,a,b
For i = 1 To 1000
a = 1 + Int(Rnd(1) * 52): b = 1 + Int(Rnd(1) * 52)
Swap talia(a), talia(b)
Next
End Sub
' Procedura rozdaje karty poszczególnym graczom
'--------------------------------------------------------
Declare Sub Rozdaj_karty()
Sub Rozdaj_karty()
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmdistsort">
<h3 style="text-align: center">Sortowanie Kart</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<div align="center">
<table border="0" id="table194" cellpadding="8"
style="border-collapse: collapse" bgcolor="#FFFFFF">
<tr>
<td> </td>
<td><span id="tg1">.</span></td>
<td> </td>
</tr>
<tr>
<td><span id="tg4">.</span></td>
<td bgcolor="#009900" style="border: 4px solid #006600"> </td>
<td><span id="tg2">.</span></td>
</tr>
<tr>
<td> </td>
<td><span id="tg3">.</span></td>
<td> </td>
</tr>
</table>
</div>
</form>
<script language="javascript">
// Sortowanie Kart
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
// Deklaracje zmiennych globalnych
//--------------------------------------------------------
var talia = new Array();
var gracz = new Array()
var i,j;
for(i = 0; i < 52; i++) talia[i] = new Object();
for(i = 0; i < 4; i++)
{
gracz[i] = new Array();
for(j = 0; j < 13; j++) gracz[i][j] = new Object();
}
// Definicje procedur i funkcji
//--------------------------------------------------------
// Procedura inicjuje talię kart
//--------------------------------------------------------
function Inicjuj_talie()
{
var i,k,f
k = f = 0;
for(i = 0; i < 52; i++)
{
talia[i].Kolor = k; talia[i].Figura = f;
f++;
if(f == 13)
{
k++; f = 0;
}
}
}
// Procedura tasuje talię kart
//--------------------------------------------------------
function Tasuj_talie()
{
var i,a,b;
var x = new Object();
for(i = 0; i < 1000; i++)
{
a = Math.floor(Math.random() * 52); b = Math.floor(Math.random() * 52);
x = talia[a]; talia[a] = talia[b]; talia[b] = x;
}
}
// Procedura rozdaje karty poszczególnym graczom
//--------------------------------------------------------
function Rozdaj_karty()
{
var i,j,k;
k = 0;
for(i = 0; i < 4; i++)
for(j = 0; j < 13; j++) gracz[i][j] = talia[k++];
}
// Procedura sortuje karty gracza wg kolorów i figur
//--------------------------------------------------------
function Sortuj_karty(g)
{
var karty = new Array();
var lfig = new Array()
var lkol = new Array()
var i,j,f,k;
for(i = 0; i < 4; i++)
{
karty[i] = new Array();
for(j = 0; j < 13; j++) karty[i][j] = new Object();
}
// Ustawiamy liczniki figur
for(f = 0; f < 13; f++) lfig[f] = 0;
// Przeglądamy rękę gracza i umieszczamy kolejne karty w tablicy
// figur wg figury
for(i = 0; i < 13; i++)
{
f = gracz[g][i].Figura;
karty[lfig[f]++][f] = gracz[g][i];
}
// Przeglądamy tablicę figur pobierając z niej karty do ręki gracza
i = 0;
for(f = 0; f < 13; f++)
for(j = 0; j < lfig[f]; j++) gracz[g][i++] = karty[j][f];
// Ustawiamy liczniki kolorów
for(k = 0; k < 4; k++) lkol[k] = 0;
// Przeglądamy rękę gracza i umieszczamy kolejne karty w tablicy
// kolorów wg koloru karty
for(i = 0; i < 13; i++)
{
k = gracz[g][i].Kolor;
karty[k][lkol[k]++] = gracz[g][i];
}
// Przeglądamy tablicę kolorów pobierając z niej karty do ręki gracza
i = 0;
for(k = 0; k < 4; k++)
for(j = 0; j < lkol[k]; j++) gracz[g][i++] = karty[k][j];
}
// Procedura wyświetla karty gracza wg jego numeru:
// 0 : gracz u góry
// 1 : gracz po prawej
// 2 : gracz u dołu
// 3 : gracz po lewej
// okna konsoli. Wydruk zajmuje 4 wiersze, w każdym do 15 znaków.
// Figurę 10 wyświetlamy jako T - każda karta powinna zajmować jeden
// znak, a zapis 10 wymaga dwóch znaków.
//--------------------------------------------------------
function Wyswietl_karty(g)
{
var kolory = new Array("♠","♥","♦","♣");
var figury = "AKDWT98765432";
var i,k,t;
t = "";
for(k = 0; k < 4; k++)
{
switch(k)
{
case 0:
case 3: t += "<font color='black'>"; break;
case 1:
case 2: t += "<font color='red'>"; break;
}
t += kolory[k] + " ";
for(i = 0; i < 13; i++)
if(gracz[g][i].Kolor == k) t += figury.charAt(gracz[g][i].Figura);
t += "</font><br>"
}
g++;
document.getElementById("tg" + g).innerHTML = t;
}
//---------------
// Program główny
//---------------
function main()
{
var i;
Inicjuj_talie();
Tasuj_talie();
Rozdaj_karty();
for(i = 0; i < 4; i++)
{
Sortuj_karty(i); Wyswietl_karty(i);
}
}
</script>
</body>
</html>
Sortowanie Kart
(C)2012 I LO w Tarnowie - I LO w Tarnowie
Sortuj
. .
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Sortowanie kubełkowe
Bucket Sort
Podrozdziały Tematy pokrewne
Algorytm Programy Sortowanie rozrzutowe
Specyfikacja problemu Program w języku Pascal Listy
Lista kroków Program w języku C++ Sortowanie kubełkowe 2
Schemat blokowy Program w języku Basic Sortowanie przez zliczanie
Program w języku JavaScript Sortowanie pozycyjne
Algorytm
Opisany w tym rozdziale algorytm sortowania kubełkowego pozwala sortować zbiory liczb całkowitych - najlepiej o dużej ilości
elementów, lecz o małym zakresie wartości. Zasada działania jest następująca:
1. Określamy zakres wartości, jakie mogą przyjmować elementy sortowanego zbioru. Niech wmin oznacza najmniejszą
wartość, a wmax niech oznacza wartość największą.
2. Dla każdej możliwej wartości przygotowujemy kubełek-licznik, który będzie zliczał ilość wystąpień tej wartości w
sortowanym zbiorze. Liczba liczników jest równa (wmax - wmin + 1). Każdy licznik jest początkowo ustawiony na wartość
zero.
3. Przeglądamy kolejno elementy zbioru od pierwszego do ostatniego. Dla każdego elementu zbioru zwiększamy o jeden
zawartość licznika o numerze równym wartości elementu. Na przykład, jeśli kolejny element zbioru ma wartość 3, to
zwiększamy licznik o numerze 3. W efekcie po przeglądnięciu wszystkich elementów zbioru liczniki będą zawierały ilość
wystąpień każdej z możliwych wartości. Jeśli dany licznik zawiera 0, to wartość równa numerowi licznika w zbiorze nie
występuje. Inaczej wartość ta występuje tyle razy, ile wynosi zawartość jej licznika.
4. Przeglądamy kolejne liczniki zapisując do zbioru wynikowego ich numery tyle razy, ile wynosi ich zawartość. Zbiór
wyjściowy będzie posortowany.
Przykład:
Dla przykładu posortujemy opisaną wyżej metodą zbiór { 2 6 4 3 8 7 2 5 7 9 3 5 2 6 }.
Najpierw określamy zakres wartości elementów (w tym celu możemy na przykład wyszukać w zbiorze element
najmniejszy i największy). U nas zakres wynosi:
wmin = 2, wmax = 9
Potrzebujemy zatem:
Teraz przeglądamy kolejne elementy zbioru zliczając ich wystąpienia w odpowiednich licznikach:
{26438725793526}
[2:3] [3:2] [4:1] [5:2] [6:2] [7:2] [8:1] [9:1]
Zapis [2:3] oznacza, iż licznik numer 2 zawiera liczbę 3, a to z kolei oznacza, iż liczba 2 pojawiła się w zbiorze 3
razy. Przeglądamy kolejne liczniki począwszy od licznika o najmniejszym numerze (w przypadku sortowania
malejącego przeglądanie rozpoczynamy od licznika o największym numerze) i zapisujemy do zbioru wynikowego
tyle razy numer licznika, ile wynosi jego zawartość:
Uwaga, pułapka:
Algorytm wymaga ponumerowania liczników kolejno od wmin do wmax. Niektóre języki
programowania (np. C++) nie pozwalają numerować dowolnie elementów tablic - zwykle numeracja
rozpoczyna się od liczby 0. W takich przypadkach musimy dokonać przeliczenia indeksu licznika,
na zliczaną przezeń wartość i na odwrót. Wzory są na szczęście bardzo proste:
Mamy wartość w i chcemy znaleźć indeks licznika, który ją zlicza:
indeks ← w - wmin
Mamy indeks licznika i chcemy znaleźć zliczaną wartość w:
w ← indeks + wmin
Specyfikacja problemu
Dane wejściowe
d[ ] - sortowany zbiór liczb całkowitych. Indeksy elementów rozpoczynają się od 1.
n - liczba elementów w zbiorze, n Î N
wmin - minimalna wartość elementów zbioru, wmin Î C
wmax - maksymalna wartość elementów zbioru, wmax Î C
Dane wyjściowe
d[ ] - posortowany zbiór liczb całkowitych.
Zmienne pomocnicze
lw [ ] - tablica liczników wartości o indeksach od wmin do wmax. Każdy licznik przechowuje liczbę całkowitą
i, j - zmienne licznikowe pętli, i,j Î C
Lista kroków
K01: Dla i = wmin , wmin + 1,...,wmax: wykonuj lw [i] ← 0
K02: Dla i = 1,2,...,n: wykonuj lw [ d[i] ] ← lw [ d[i] ] + 1
K03: j ← 1
K04: Dla i = wmin , wmin + 1,...,wmax: wykonuj K05
K05: Dopóki lw [i] > 0 wykonuj:
d[j] ← i
lw [i] ← lw [i] - 1
j ←j + 1
K06: Zakończ
Algorytm ma klasę czasowej złożoności obliczeniowej O(m + n), gdzie m oznacza ilość możliwych wartości, które mogą
przyjmować elementy zbioru, a n to ilość sortowanych elementów. Jeśli m jest małe w porównaniu z n (sortujemy dużo elementów
o małym zakresie wartości), to na czas sortowania będzie miała wpływ głównie ilość elementów n i klasa złożoności uprości się
do postaci O(n). Dzieje się tak dlatego, iż przy równomiernym rozkładzie dużej ilości elementów o małym zakresie wartości
liczniki będą równomiernie zapełnione (stan każdego licznika będzie dążył do [n/m]). Zatem algorytm wykona:
1. m operacji zerowania liczników - czas pomijalnie mały i przy dużym n nie wpływa istotnie na klasę algorytmu.
2. n operacji zwiększania liczników
3. n operacji przesłania numerów liczników do zbioru wynikowego - ilość pustych liczników będzie dążyła do zera.
W sytuacji odwrotnej, gdy sortujemy mało elementów o dużym zakresie wartości klasa złożoności zredukuje się z kolei do O(m) -
spróbuj uzasadnić ten wynik samemu na podstawie analizy działania poszczególnych fragmentów algorytmu.
Schemat blokowy
Algorytm realizujemy w czterech pętlach.
Pętla nr 1 zeruje kolejne liczniki lw[ ].
W pętli nr 2 przeglądamy kolejne elementy zbioru od pierwszego do ostatniego. Dla
każdego elementu zwiększamy licznik o numerze równym wartości elementu. Po
zakończeniu tej pętli liczniki zawierają liczbę wystąpień poszczególnych wartości
elementów w sortowanym zbiorze.
Zmienna j służy do umieszczania w zbiorze wyjściowym kolejnych elementów.
Umieszczanie rozpoczniemy od początku zbioru, dlatego zmienne ta przyjmuje wartość 1.
Pętla nr 3 przegląda kolejne liczniki. Jeśli zawartość danego licznika jest większa od zera,
to pętla nr 4 umieści w zbiorze wyjściowym odpowiednią ilość numerów licznika, które
odpowiadają zliczonym wartościom elementów ze zbioru wejściowego.
Po zakończeniu pętli nr 3 elementy w zbiorze wyjściowym są posortowane. Kończymy
algorytm.
Programy
Program sortuje 80 liczb z zakresu od -99 do 99.
DevPascal
// Sortowanie Kubełkowe
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program bucketsort;
const WMIN = -99;
const WMAX = 99;
const N = 80;
var
d : array[1..N] of integer;
lw : array[WMIN..WMAX] of integer;
i,j : integer;
begin
writeln(' Sortowanie kubelkowe ');
writeln('----------------------');
writeln('(C)2005 Jerzy Walaszek');
writeln;
// tworzymy zbiór wejściowy do sortowania
randomize;
for i := 1 to N do d[i] := WMIN + random(WMAX - WMIN + 1);
// wyświetlamy zawartość zbioru przed sortowaniem
writeln('Przed sortowaniem:');
writeln;
for i := 1 to N do write(d[i]:4);
writeln;
// sortujemy
// najpierw zerujemy liczniki
for i := WMIN to WMAX do lw[i] := 0;
// zliczamy w odpowiednich licznikach wystąpienia
// wartości elementów sortowanego zbioru
for i := 1 to N do inc(lw[d[i]]);
// zapisujemy do zbioru wynikowego numery niezerowych liczników
// tyle razy, ile wynosi ich zawartość
j := 1;
for i := WMIN to WMAX do
while lw[i] > 0 do
begin
d[j] := i; inc(j); dec(lw[i]);
end;
// wyświetlamy zawartość zbioru po sortowaniu
writeln('Po sortowaniu:');
writeln;
for i := 1 to N do write(d[i]:4);
// koniec
writeln;
writeln('Gotowe. Nacisnij klawisz ENTER...');
readln;
end.
Code::Blocks
// Sortowanie Kubełkowe
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
int main()
{
const int WMIN = -99;
const int WMAX = 99;
const int N = 80;
int d[N],lw[WMAX - WMIN + 1],i,j;
cout << " Sortowanie kubelkowe \n"
"----------------------\n"
"(C)2005 Jerzy Walaszek\n\n";
// tworzymy zbiór wejściowy do sortowania
srand((unsigned)time(NULL));
for(i = 0; i < N; i++) d[i] = WMIN + rand() % (WMAX - WMIN + 1);
// wyświetlamy zawartość zbioru przed sortowaniem
cout << "Przed sortowaniem:\n\n";
for(i = 0; i < N; i++) cout << setw(4) << d[i];
cout << endl;
// sortujemy
// najpierw zerujemy liczniki
for(i = WMIN; i <= WMAX; i++) lw[i - WMIN] = 0;
// zliczamy w odpowiednich licznikach wystąpienia
// wartości elementów sortowanego zbioru
for(i = 0; i < N; i++) lw[d[i] - WMIN]++;
// zapisujemy do zbioru wynikowego numery niezerowych liczników
// tyle razy, ile wynosi ich zawartość
j = 0;
for(i = WMIN; i <= WMAX; i++) while(lw[i - WMIN]--) d[j++] = i;
// wyświetlamy zawartość zbioru po sortowaniu
cout << "Po sortowaniu:\n\n";
for(i = 0; i < N; i++) cout << setw(4) << d[i];
// koniec
cout << endl;
return 0;
}
Free Basic
' Sortowanie Kubełkowe
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmbucketsort">
<h3 style="text-align: center">Sortowanie Kubełkowe</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Kubełkowe
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
function main()
{
var WMIN = -99;
var WMAX = 99;
var N = 80;
var d = new Array(N);
Sortowanie Kubełkowe
(C)2012 I LO w Tarnowie - I LO w Tarnowie
Sortuj
...
Temat:
Wyślij Kasuj
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Listy
Lists
Podrozdziały Tematy pokrewne
Budowa Listy Programy Sortowanie rozrzutowe
Dodawanie elementu do listy Program w języku Pascal Sortowanie kubełkowe 1
Usuwanie elementu z listy Program w języku C++ Sortowanie kubełkowe 2
Rodzaje list Program w języku Basic Sortowanie przez zliczanie
Program w języku JavaScript Sortowanie pozycyjne
Budowa listy
Pełny algorytm sortowania rozrzutowego wymaga nowej struktury danych zwanej listą. Lista zbudowana jest z ciągu elementów:
Dostęp do elementów listy nie jest swobodny. Zwykle z i-tego elementu możemy przejść do elementu następnego (i + 1) lub do
elementu poprzedniego (i - 1). O takiej strukturze danych mówimy, że jest sekwencyjna.
Każdy element listy oprócz swoich danych zawiera dwa dodatkowe pola.
Ostatni element listy zawiera wartość zero w polu następnika - nie istnieje element następny.
Pierwszy element listy zawiera wartość zero w polu poprzednika - nie istnieje element poprzedni.
Kolejność elementów na liście nie ma nic wspólnego z ich kolejnością w tablicy i może być zupełnie różna:
Lista oprócz samych elementów zawierających dane budowana jest często z dodatkowym elementem zwanym nagłówkiem.
Nagłówek zawiera jedynie indeks pierwszego elementu listy. Jest to jakby wejście, brama, poprzez którą wchodzimy na listę.
Jeśli nagłówek zawiera wartość zero, to lista nie posiada żadnego elementu. Mówimy wtedy, iż lista jest pusta.
Rodzaje list
Jeśli element listy posiada zdefiniowane pole następnika i poprzednika, to po liście zbudowanej z takich elementów możemy
poruszać się w obu kierunkach - zarówno w przód jak i wstecz. Lista nosi nazwę dwukierunkowej.
Na liście dwukierunkowej zdefiniowane są wszystkie opisane poprzednio operacje wstawiania i usuwania elementu.
Jeśli elementy listy mają zdefiniowane tylko pole następnika, to po liście można poruszać się tylko w jednym kierunku. Lista nosi
nazwę jednokierunkowej. W wielu sytuacjach taki uproszczony rodzaj listy jest zupełnie wystarczający (patrz - przykłady
programów).
Na liście jednokierunkowej zdefiniowana jest operacja wstawiania elementu na początku listy, na końcu listy oraz po i-tym
elemencie.
Jeśli pole następnika ostatniego elementu listy wskazuje pierwszy element, to lista staje się jednokierunkową listą cykliczną. Po
liście możemy się poruszać w kółko bez końca.
Listę dwukierunkową można zapętlić w obu kierunkach otrzymując dwukierunkową listę cykliczną:
Programy
Prezentowane programy demonstrują przykładowe zastosowania list. Generują one 19 3-literowych wyrazów zbudowanych z liter
A, B i C wybieranych losowo. Następnie tworzą trzy listy wyrazów rozpoczynających się kolejno na literę A, B i C.
Zwróć uwagę, iż program umieszcza na listach teksty w kolejności odwrotnej do ich występowania w ciągu wejściowym. Dzieje się
tak dlatego, iż zastosowaliśmy najprostszy wariant - dołączanie elementu na początek listy. Zatem nowe elementy pojawiają się
na liście przed elementami już na niej umieszczonymi. Jeśli chcesz zachować kolejność elementów na liście taką samą jak w
ciągu wejściowym, to ciąg wejściowy należy przeglądać od końca.
DevPascal
Code::Blocks
// Przykład zastosowania list
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <iostream>
#include <cstdlib>
#include <time.h>
using namespace std;
const int MAXN = 19;
int main()
{
struct
{
unsigned int nastepnik;
char dane[3];
} L[MAXN+1];
unsigned int ngl[3],i,j;
cout << "Demonstracja zastosowania list\n"
"------------------------------\n"
" (C)2005 mgr Jerzy Walaszek \n\n"
"3-literowe teksty losowe:\n\n";
// Tworzymy losowe ciągi 3-literowe w elementach tablicy L[]
srand((unsigned)time(NULL));
for(i = 1; i <= MAXN; i++)
for(j = 0; j < 3; j++)
L[i].dane[j] = 65 + rand() % 3;
// Wyświetlamy ciągi
for(i = 1; i <= MAXN; i++)
cout << " " << L[i].dane[0] << L[i].dane[1] << L[i].dane[2];
cout << endl << endl;
// Zerujemy nagłówki list
for(i = 0; i < 3; i++) ngl[i] = 0;
// Tworzymy listy wyrazów na A, B i C
for(i = 1; i <= MAXN; i++)
{
j = L[i].dane[0] - 65;
L[i].nastepnik = ngl[j];
ngl[j] = i;
}
// Odczytujemy kolejne listy i wyświetlamy je w oknie konsoli
for(i = 0; i < 3; i++)
{
cout << "Na litere " << (char)(i + 65) << " :";
j = ngl[i];
while(j)
{
cout << " " << L[j].dane[0] << L[j].dane[1] << L[j].dane[2];
j = L[j].nastepnik;
}
cout << endl << endl;
}
// Gotowe, kończymy program
cout << endl;
return 0;
}
Free Basic
' Przykład zastosowania list
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Const MAXN = 19
' Tutaj definiujemy typ elementów listy
Type TElement
nastepnik As Uinteger
dane As String * 3
End Type
' Tutaj deklarujemy zmienne
Dim L(MAXN) As TElement
Dim As Uinteger ngl(0 To 2),i,j
Print "Demonstracja zastosowania list"
Print "------------------------------"
Print " (C)2005 mgr Jerzy Walaszek "
Print
Print "3-literowe teksty losowe:"
Print
' Tworzymy losowe ciągi 3-literowe w elementach tablicy L[]
Randomize
For i = 1 To MAXN
L(i).dane = CHR$(65 + Int(Rnd(1) * 3)) + _
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmblistdemo">
<h3 style="text-align: center">Demonstracja zastosowania list</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Start" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Przykład zastosowania list
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var MAXN = 19;
var ngl = new Array();
var L = new Array();
var i,j,t;
for(i = 1; i <= MAXN; i++) L[i] = new Object();
function main()
{
// Tworzymy losowe ciągi 3-literowe w elementach tablicy L[]
for(i = 1; i <= MAXN; i++)
{
L[i].dane = "";
for(j = 0; j < 3; j++)
L[i].dane += String.fromCharCode(65 + Math.floor(Math.random() * 3));
}
// Wyświetlamy ciągi
t = "3-literowe teksty losowe:<BR><BR>";
for(i = 1; i <= MAXN; i++) t += L[i].dane + " ";
t += "<BR><BR>";
Start
...
Temat:
Uwaga: ← tutaj wpisz wyraz ilo , inaczej list zostanie zignorowany
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Sortowanie kubełkowe
Bucket Sort
Podrozdziały Tematy pokrewne
Algorytm Programy Badanie algorytmów Sortowanie rozrzutowe
Dodawanie elementu do listy Program w języku Pascal sortujących Sortowanie kubełkowe 1
uporządkowanej Program w języku C++ Podsumowanie Listy
Specyfikacja problemu Program w języku Basic Zadania dla ambitnych Sortowanie przez
Lista kroków Program w języku zliczanie
Schemat blokowy JavaScript Sortowanie pozycyjne
Algorytm
Podany w poprzednim rozdziale algorytm sortowania kubełkowego jest bardzo uproszczony i pozwala sortować tylko liczby
całkowite. Teraz opiszemy pełny algorytm sortowania kubełkowego pozbawiony tych ograniczeń. Najpierw kilka założeń i definicji:
Sortujemy zbiór d[ ] liczb rzeczywistych. Niech wmin oznacza dolny kres wartości elementów d[ ], a wmax niech oznacza kres
górny. Wartości te tworzą przedział liczbowy <wmin, wmax>, w który wpadają wszystkie elementy zbioru. Przedział dzielimy na
m segmentów, które nazwiemy kubełkami. W tym celu obliczamy szerokość kubełka:
wmax - wmin
szkb = m
czyli
Ponieważ poszczególne kubełki tworzą przedziały prawostronnie otwarte, potrzebujemy ostatniego kubełka K[m] dla wartości
maksymalnej wmax, którą mogą osiągać elementy w sortowanym zbiorze. Wynika stąd, iż kubełków będzie m + 1
ponumerowanych kolejno od 0 do m.
Liczba d[j] należy do i-tego kubełka, jeśli spełniona jest nierówność:
i= [ d[j]sz-kbwmin]
Po wyznaczeniu przedziału kubełków, zerujemy kubełki (tzn. tworzymy w nich puste listy elementów). Następnie przeglądamy
kolejno wszystkie elementy zbioru, wyznaczamy dla nich odpowiednie kubełki i wstawiamy te elementy to kubełków. Operację
wstawiania do kubełka wykonujemy tak, aby wewnątrz kubełka elementy tworzyły listę posortowaną. Jeśli kubełków będzie
odpowiednio dużo, to zawierać będą niewiele elementów, zatem operacja tworzenia listy uporządkowanej nie zajmie dużo czasu.
Po wykonaniu przebiegu dystrybucyjnego w poszczególnych kubełkach będziemy posiadali uporządkowane listy elementów.
Wykonujemy przebieg zbierający - przeglądamy poszczególne kubełki pobierając z list przechowywane w nich elementy i
umieszczając je w zbiorze wynikowym. W efekcie otrzymamy zbiór posortowany.
Przykład:
Dla zobrazowania zasady sortowania kubełkowego posortujemy tą metodą poniższy zbiór liczb:
wmin = 1/2
wmax = 41/2
element - wmin
i= [ szkb ]
23/4 - 1/2
[ 1 ] = [2 / ] = 2
23/4 : i = 1
4
1/ - / 1 1
1 / :i=[ 1 ]=[ / ]=0
1 4 2 3
4 4
/ - /4 1
/ :i=[ 1 ]=[ / ]=0
5 2
4 3
5 10
4/ - / 1 1
4 / :i=[ 1 ] = [4] = 4
1 2 2
2
1/ - / 4 1
1 / :i=[ 1 ] = [1 / ] = 1
4 5 2 3
5 10
/ - /1 1
/ :i=[ 1 ] = [0] = 0
1 2 2
2
Pobieramy elementy z kolejnych kubełków i umieszczamy je w zbiorze wynikowym. Zbiór jest posortowany.
Dane wejściowe
L[ ] - tablica elementów, z których zbudowana jest lista. Każdy element posiada pole następnik oraz pole dane.
K[ ] - tablica nagłówków list. Elementy K[ ] są liczbami całkowitymi.
ine - indeks nowego elementu w tablicy L[ ], który będzie dodany do listy kubełka, ine N
Dane wyjściowe
Na liście kubełka K[ikb] zostaje umieszczony nowy element o wartości pola dane równej we. Lista jest posortowana
rosnąco
Zmienne pomocnicze
ip - indeks poprzedniego elementu na liście, ip C
ib - indeks bieżącego elementu na liście, ib C
Specyfikacja problemu
Dane wejściowe
n - liczba elementów do posortowania; n Î N
d[ ] - tablica n-elementowa z elementami rzeczywistymi do posortowania. Indeks pierwszego elementu wynosi 1.
wmin - najmniejszy element w tablicy d[ ]; wmin Î R
wmax - największy element w tablicy d[ ]; wmax Î R
Dane wyjściowe
d[ ] - tablica z posortowanymi rosnąco elementami
Zmienne pomocnicze
L[ ] - n-elementowa tablica elementów list. Każdy element posiada całkowite pole następnik oraz rzeczywiste pole
dane. Indeksy elementów rozpoczynają się od wartości 1.
K[ ] - tablica (n + 1) elementowa zawierająca nagłówki list kubełków. Każdy element jest liczbą całkowitą. Indeksy
elementów rozpoczynają się od wartości 0.
szkb - szerokość przedziału objętego przez kubełek, szkb Î R
ikb - wyznaczony dla danego elementu numer kubełka, ikb Î C
ine - indeks wolnej pozycji w tablicy L[ ]; ine Î N
i,j - zmienne licznikowe pętli; i,j Î C
we - wartość sortowanego elementu; we Î R
Lista kroków
K01: Dla i = 0,1,...,n: wykonuj K[i] ← 0
w -w
K02: szkb ← max min
n
K03: ine ← 1
K04: Dla i = 1,2,...,n: wykonuj K05...K07
K05: we ← d[i]
w -w
K06: ikb ← [ e min]
szkb
K07: Wstaw wartość we na listę kubełka K[ikb]
K08: j ← 1
Schemat blokowy
Pierwszą operacją algorytmu jest wyzerowanie nagłówków list K[ ].
Następnie obliczamy szerokość przedziału kubełka i zapamiętujemy ją
w zmiennej szkb.
Elementy list będziemy tworzyli kolejno w tablicy L[ ]. Ustawiamy
zmienną ine na pierwszy element tej tablicy. Zmienna ta pełni rolę
wskaźnika wolnego elementu w puli L[ ].
Rozpoczynamy pętlę nr 1, której zadaniem jest umieszczenie
wszystkich elementów zbioru d[ ] w listach kubełków K[ ]. Element i-
ty z d[ ] zapamiętujemy w zmiennej roboczej we (aby zmniejszyć ilość
odwołań do tablicy d[ ]). Obliczamy numer kubełka ikb, do którego trafi
pobrany element we. Następnie wartość we wstawiamy na listę K[ikb]
za pomocą procedury wstawiania elementu na listę uporządkowaną.
Pętla nr 1 jest kontynuowana dotąd, aż wszystkie elementy zbioru d[ ]
znajdą się na odpowiednich listach kubełków K[ ]. Operacja
dystrybucji (rozrzucenia) elementów zostaje zakończona.
Druga faza algorytmu ma za zadanie pozbierać elementy z
poszczególnych list uporządkowanych zawartych w kubełkach K[ ].
Zmienna j pełni rolę indeksu wstawianych do d[ ] elementów. Dlatego
przyjmuje wartość 1 - początek tablicy d[ ].
Pętla nr 2 wyznacza kolejne kubełki K[ ].
Pętla wewnętrzna nr 3 przegląda elementy listy danego kubełka i
zapisuje je do zbioru d[ ].
Po zakończeniu pętli nr 2 w zbiorze d[ ] mamy posortowane elementy. Algorytm jest zakończony.
Programy
Efekt uruchomienia programu
Sortowanie Kubelkowe II
------------------------------
(C)2005 mgr Jerzy Walaszek
Przed sortowaniem:
-508.97 417.11 180.18 457.34 355.41 -813.29 -25.21 189.09 -464.17 -649.78
-126.22 811.71 244.45 889.28 77.94 286.25 -92.35 596.31 -4.15 28.44
353.39 -238.40 433.72 589.48 18.92 -198.85 -39.25 809.69 -260.31 -860.96
-487.18 257.20 683.59 -344.36 297.30 -235.84 -8.61 15.26 -939.39 -126.28
Po sortowaniu:
-939.39 -860.96 -813.29 -649.78 -508.97 -487.18 -464.17 -344.36 -260.31 -238.40
-235.84 -198.85 -126.28 -126.22 -92.35 -39.25 -25.21 -8.61 -4.15 15.26
18.92 28.44 77.94 180.18 189.09 244.45 257.20 286.25 297.30 353.39
355.41 417.11 433.72 457.34 589.48 596.31 683.59 809.69 811.71 889.28
DevPascal
// Sortowanie kubełkowe - wersja II
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
const
N = 40;
WMIN = -1000.0;
WMAX = 1000.0;
// Tutaj definiujemy typ elementów listy
type
TElement = record
nastepnik : cardinal;
dane : real;
end;
// Tutaj deklarujemy zmienne
var
d : array[1..N] of real;
L : array[1..N] of TElement;
K : array[0..N] of integer;
we,szkb : real;
ikb,ine,ip,ib,i,j : integer;
begin
writeln(' Sortowanie Kubelkowe II ');
writeln('------------------------------');
writeln(' (C)2005 mgr Jerzy Walaszek ');
writeln;
// Generujemy zawartość tablicy d[] i wyświetlamy ją
randomize;
writeln('Przed sortowaniem:');
writeln;
for i := 1 to N do
begin
d[i] := WMIN + random() * (WMAX - WMIN);
write(d[i]:8:2);
end;
writeln;
// Zerujemy kubełki
for i := 0 to N do K[i] := 0;
// Obliczamy szerokość kubełka
szkb := (WMAX - WMIN) / N;
ine := 1;
// Rozrzucamy poszczególne elementy d[i] na listach K[]
for i := 1 to N do
begin
we := d[i];
ikb := round((we - WMIN) / szkb);
L[ine].nastepnik := 0; L[ine].dane := we;
ip := 0; ib := K[ikb];
while (ib > 0) and (L[ib].dane < we) do
begin
ip := ib; ib := L[ib].nastepnik;
end;
if ip = 0 then
begin
L[ine].nastepnik := ib; K[ikb] := ine
end
else if ib = 0 then
L[ip].nastepnik := ine
else
begin
L[ip].nastepnik := ine; L[ine].nastepnik := ib;
end;
inc(ine);
end;
// wybieramy dane z kubełków i umieszczamy je w d[]
j := 1;
for ikb := 0 to N do
begin
i := K[ikb];
while i > 0 do
begin
d[j] := L[i].dane;
inc(j); i := L[i].nastepnik;
end;
end;
// Koniec. Wyświetlamy wyniki
writeln('Po sortowaniu:');
writeln;
for i := 1 to N do write(d[i]:8:2);
writeln;
writeln('KONIEC. Nacisnij klawisz Enter...');
readln;
end.
Code::Blocks
// Sortowanie kubełkowe - wersja II
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
Free Basic
' Sortowanie kubełkowe - wersja II
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 40
Const WMIN = -1000
Const WMAX = 1000
' Tutaj definiujemy typ elementów listy
Type TElement
nastepnik As Uinteger
dane As Double
End Type
' Tutaj deklarujemy zmienne
Dim As Double d(N),we,szkb
Dim As TElement L(N)
Dim As Integer K(0 To N),ikb,ine,ip,ib,i,j
Print " Sortowanie Kubelkowe II "
Print "------------------------------"
Print " (C)2005 mgr Jerzy Walaszek "
Print
' Generujemy zawartość tablicy d[] i wyświetlamy ją
Randomize
Print "Przed sortowaniem:"
Print
For i = 1 To N
d(i) = WMIN + Rnd(1) * (WMAX - WMIN)
Print Using "#####.##"; d(i);
Next
Print
' Zerujemy kubełki
For i = 0 To N: K(i) = 0: Next
' Obliczamy szerokość kubełka
szkb = (WMAX - WMIN) / N
ine = 1
' Rozrzucamy poszczególne elementy d[i] na listach K[]
For i = 1 To N
we = d(i)
ikb = Int((we - WMIN) / szkb)
L(ine).nastepnik = 0: L(ine).dane = we
ip = 0: ib = K(ikb)
While (ib > 0) And (L(ib).dane < we)
ip = ib: ib = L(ib).nastepnik
Wend
If ip = 0 Then
L(ine).nastepnik = ib: K(ikb) = ine
Elseif ib = 0 Then
L(ip).nastepnik = ine
Else
L(ip).nastepnik = ine: L(ine).nastepnik = ib
End If
ine += 1
Next
' wybieramy dane z kubełków i umieszczamy je w d[]
j = 1
For ikb = 0 To N
i = K(ikb)
While i > 0
d(j) = L(i).dane
j += 1: i = L(i).nastepnik
Wend
Next
' Koniec. Wyświetlamy wyniki
Print "Po sortowaniu:"
Print
For i = 1 To N: Print Using "#####.##"; d(i);: Next
Print
Print "KONIEC. Nacisnij dowolny klawisz..."
Sleep
End
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
Sortowanie Kubełkowe II
(C)2012 I LO w Tarnowie - I LO w Tarnowie
Sortuj
...
if ip = 0 then
begin
L[ine].nastepnik := ib; K[ikb] := ine
end
else if ib = 0 then
L[ip].nastepnik := ine
else
begin
L[ip].nastepnik := ine; L[ine].nastepnik := ib;
end;
inc(ine);
end;
j := 1;
for ikb := 0 to n do
begin
i := K[ikb];
while i > 0 do
begin
d[j] := L[i].dane;
inc(j); i := L[i].nastepnik;
end;
end;
QueryPerformanceCounter(addr(qpc2));
Sort := (qpc2 - qpc1 - tqpc) / qpf;
end;
// Program główny
//---------------
var
i,j,k : integer;
tpo,tod,tpp,tpk,tnp : extended;
f : Text;
begin
if QueryPerformanceFrequency(addr(qpf)) then
begin
QueryPerformanceCounter(addr(qpc1));
QueryPerformanceCounter(addr(qpc2));
tqpc := qpc2 - qpc1;
assignfile(f,'wyniki.txt'); rewrite(f);
// Wydruk na ekran
writeln('Nazwa: ',NAZWA);
writeln(K1);
writeln(K2);
writeln;
writeln(K3);
// Wydruk do pliku
writeln(f,'Nazwa: ',NAZWA);
writeln(f,K1);
writeln(f,K2);
writeln(f,'');
writeln(f,K3);
for i := 1 to MAX_LN do
begin
n := LN[i];
// Czas sortowania zbioru posortowanego
wmin := 1; wmax := n;
for j := 1 to n do d[j] := j;
tpo := Sort;
// Czas sortowania zbioru posortowanego odwrotnie
for j := 1 to n do d[j] := n - j;
tod := Sort;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na początku - średnia z 10 obiegów
tpp := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[1] := random * n + 1;
tpp += Sort;
end;
tpp /= 10;
// Czas sortowania zbioru posortowanego
// z przypadkowym elementem na końcu - średnia z 10 obiegów
tpk := 0;
for j := 1 to 10 do
begin
for k := 1 to n do d[k] := k;
d[n] := random * n + 1;
tpk += Sort;
end;
tpk /= 10;
Otrzymane wyniki są następujące (dla komputera o innych parametrach wyniki mogą się różnić co do wartości czasów wykonania,
dlatego w celach porównawczych proponuję uruchomić podany program na komputerze czytelnika):
Podsumowanie
Analizując wyniki obliczeń w arkuszu kalkulacyjnym otrzymanych czasów sortowania dla algorytmu sortowania kubełkowego
wyciągamy następujące wnioski:
Własności algorytmu
Algorytm tpo tod tpp tpk tnp
O(n) O(n) O(n) O(n) O(n)
Sortowanie kubełkowe
tpo ≈ tod tpp ≈ tpk tnp ≈ 10tod
3
1. Analiza wyników obliczeń w arkuszu kalkulacyjnym pozwala stwierdzić, iż wszystkie czasy sortowania są proporcjonalne
do liczby elementów. Zatem algorytm ten posiada liniową klasę złożoności obliczeniowej O(n).
2. Czas sortowania zbioru nieuporządkowanego jest wyraźnie dłuższy od czasów sortowania zbiorów uporządkowanych.
3. Algorytm jest mało czuły na niewielkie zaburzenia w zbiorach uporządkowanych.
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Algorytm
Zasadę pracy algorytmu sortowania przez zliczanie jest najlepiej przedstawić za pomocą przykładu.
Przykład:
Posortować rosnąco zbiór danych:
{6361490182649375927324187085836253}
Czynności wstępne
Dla każdej wartości w zbiorze przygotowujemy licznik i ustawiamy go na 0.
[0:0] [1:0] [2:0] [3:0] [4:0] [5:0] [6:0] [7:0] [8:0] [9:0]
Obieg zliczający
Przeglądamy kolejne elementy zbioru i zliczamy ich wystąpienia w odpowiednich licznikach. Np. element 6 powoduje
zwiększenie o 1 licznika nr 6. Po wykonaniu tego obiegu w poszczególnych licznikach mamy ilość wystąpień każdej
wartości. W naszym przykładzie otrzymamy:
[0:2] [1:3] [2:4] [3:5] [4:3] [5:3] [6:4] [7:3] [8:4] [9:3]
Teraz poczynając od drugiego licznika sumujemy zawartość licznika oraz jego poprzednika i otrzymujemy:
[0:2] [1:5] [2:9] [3:14] [4:17] [5:20] [6:24] [7:27] [8:31] [9:34]
W wyniku tej operacji w każdym liczniku otrzymaliśmy ilość wartości mniejszych lub równych numerowi licznika,
które występują w zbiorze wejściowym. Na przykład:
[0:2] - w zbiorze wejściowym są dwie wartości 0
[1:5] - w zbiorze wejściowym jest pięć wartości mniejszych lub równych 1
[2:9] - w zbiorze wejściowym jest dziewięć wartość mniejszych lub równych 2, itd.
Zwróć uwagę, iż stan licznika określa teraz ostatnią pozycję w zbiorze uporządkowanym, na której należy umieścić
wartość równą numerowi licznika:
Wartość: 0 0 1 1 1 2 2 2 2 3 3 3 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 8 8 8 8 9 9 9
Pozycja: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
Obieg dystrybucyjny
Przeglądamy jeszcze raz zbiór wejściowy idąc od ostatniego elementu do pierwszego (aby zachować stabilność
sortowania). 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. Dzięki temu następna taka wartość trafi na
wcześniejszą pozycję.
W naszym przykładzie rozpoczynamy od ostatniej liczby 3. Jej licznik ma zawartość [3:14]. Zatem liczbę 3
umieszczamy w zbiorze wynikowym na pozycji 14 i zmniejszamy o 1 stan licznika otrzymując [3:13]. Kolejna liczba
3 trafi teraz na pozycję 13, itd.
Dla liczby 5 stan licznika wynosi [5:20]. Umieszczamy ją zatem na 20 pozycji i licznik zmniejszamy o 1 otrzymując
[5:19].
Postępujemy w ten sam sposób z pozostałymi elementami zbioru. W efekcie zbiór wynikowy będzie posortowany
rosnąco:
{0011122223333344455566667778888999}
Podsumowanie
Przyjrzawszy się dokładnie algorytmowi sortowania przez zliczanie możesz zastanawiać się, dlaczego postępujemy
w tak dziwny sposób? Przecież mając zliczone wystąpienia każdej wartości w licznikach, możemy je od razu
przepisać do zbioru wyjściowego, jak zrobiliśmy w pierwszej wersji algorytmu sortowania kubełkowego. Miałbyś
rację, gdyby chodziło jedynie o posortowanie liczb. Jest jednak inaczej.
Celem nie jest posortowanie jedynie samych wartości elementów. Sortowane wartości są zwykle tzw. kluczami, czyli
wartościami skojarzonymi z elementami, które wyliczono na podstawie pewnego kryterium Sortując klucze chcemy
posortować zawierające je elementy. Dlatego do zbioru wynikowego musimy przepisać całe elementy ze zbioru
wejściowego, gdyż w praktyce klucze stanowią jedynie część (raczej małą) danych zawartych w elementach. Zatem
algorytm sortowania przez zliczanie wyznacza docelowe pozycje elementów na podstawie reprezentujących je
kluczy, które mogą się wielokrotnie powtarzać. Następnie elementy są umieszczane na właściwym miejscu w
zbiorze wyjściowym. Prześledź dokładnie podany poniżej algorytm oraz przykładowe programy, a wszystko powinno
się wyjaśnić.
Specyfikacja problemu
Dane wejściowe
d[ ] - zbiór elementów do posortowania. Każdy element posiada pole klucz, wg 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
k min - minimalna wartość klucza, k min C
k max - maksymalna wartość klucza, k max C
Dane wyjściowe
b[ ] - zbiór z posortowanymi elementami ze zbioru d[ ]. Indeksy elementów rozpoczynają się od 1.
Zmienne pomocnicze
i - zmienna dla pętli iteracyjnych, i C
L[ ] - tablica liczników wartości kluczy. Elementy są liczbami całkowitymi. Indeksy przebiegają kolejne wartości od
k min do k max.
Lista kroków
K01: Dla i = k min, k min + 1,...,k max: wykonuj L[i] ← 0
K02: Dla i = 1,2,...,n: wykonuj L[d[i].klucz] ← L[d[i].klucz] + 1
K03: Dla i = k min + 1, k min + 2,...,k max: 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
Schemat blokowy
Algorytm sortowania przez zliczanie zbudowany jest z kolejno
następujących po sobie pętli iteracyjnych.
W pętli nr 1 przygotowujemy liczniki wystąpień poszczególnych
kluczy. Ustawiamy je na 0.
W pętli nr 2 przeglądamy kolejne elementy zbioru zwiększając o 1
licznik o numerze równym wartości klucza w sortowanym elemencie
zbioru. 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
n a ostatnie pozycje elementów z danym kluczem w zbiorze
wyjściowym.
W pętli nr 4 ponownie przeglądamy zbiór wejściowy (idąc od końca do
początku, aby zachować kolejność elementów równych - inaczej
algorytm nie byłby stabilny) i przesyłamy elementy ze zbioru
wejściowego do zbioru wyjściowego na pozycję o numerze zawartym
w liczniku skojarzonym z kluczem elementu. Po przesłaniu licznik
zmniejszamy o 1, aby kolejny element o tej samej wartości klucza
trafił na poprzednią pozycję (idziemy wstecz, zatem kolejność
elementów o tym samym kluczu zostanie zachowana). Po
zakończeniu tej pętli dane w zbiorze wynikowym są posortowane
rosnąco. Kończymy zatem algorytm.
Zwróć uwagę, iż algorytm sortowania przez zliczanie nie porównuje ze sobą żadnych elementów zbioru. Teoretycznie
udowodniono, iż algorytmy sortujące, które porównują ze sobą elementy zbioru nie mogą osiągać w przypadku ogólnym lepszej
klasy złożoności obliczeniowej O(n log n). Ten algorytm nie porównuje elementów, zatem łamie tę granicę.
Aby wyznaczyć klasę złożoności obliczeniowej algorytmu sortowania przez zliczanie, przyjrzyjmy się mu bliżej.
Pętla nr 1 wykonuje m = kmax - kmin + 1 operacji zerowania liczników. Jej czas wykonania jest proporcjonalny do m (ilość
możliwych wartości kluczy), zatem ma klasę O(m).
Pętla nr 2 przegląda n elementów zbioru i dla każdego elementu wykonuje operację zwiększania licznika wg numeru klucza.
Klasa złożoności wynosi O(n).
Pętla nr 3 wykonuje m - 1 dodawań - klasa złożoności O(m).
Pętla nr 4 przepisuje elementy ze zbioru wejściowego do wyjściowego i zmniejsza o 1 wartości liczników. Ilość wykonanych
operacji wynosi n, zatem jest to klasa złożoności O(n).
Z tego porównania widzimy jasno, iż na czas wykonania algorytmu ma wpływ zarówno m (ilość wartości kluczy) jak i n (ilość
sortowanych elementów). Stąd klasa złożoności obliczeniowej wynosi O(m + n).
Programy
Prezentowane programy generują pseudolosowe ciągi 3-literowych wyrazów zbudowanych z liter A, B oraz C. Dla każdego
wygenerowanego wyrazu jest wyliczany klucz określający jego alfabetyczną kolejność:
Wyraz AAA AAB AAC ABA ABB ... CCA CCB CCC
Klucz 0 1 2 3 4 ... 24 25 26
Wyrazy są rosnąco sortowane wg wartości klucza.
DevPascal
// Sortowanie przez zliczanie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
const
N = 80;
KMIN = 0;
KMAX = 26;
// Tutaj definiujemy typ elementu
type
TElement = record
klucz : cardinal;
wyraz : string[3];
end;
// Zmienne
var
d,b : array[1..N] of TElement;
L : array[KMIN..KMAX] of cardinal;
i,j,v : integer;
s : string;
begin
writeln(' Sortowanie Przez Zliczanie ');
writeln('------------------------------');
writeln(' (C)2005 mgr Jerzy Walaszek ');
writeln;
writeln('Przed sortowaniem:');
writeln;
// Generujemy losowe elementy do sortowania oraz ich klucze
randomize;
for i := 1 to N do
begin
s := '';
for j := 1 to 3 do s := s + char(65 + random(3));
d[i].wyraz := s;
d[i].klucz := 0;
v := 1;
for j := 3 downto 1 do
begin
inc(d[i].klucz, v * (ord(d[i].wyraz[j]) - 65));
v := 3 * v;
end;
end;
// Wyświetlamy wygenerowane elementy
for i := 1 to N do write(d[i].wyraz:4);
writeln;
// Zerujemy liczniki
for i := KMIN to KMAX do L[i] := 0;
// Zliczamy wystąpienia kluczy
for i := 1 to N do inc(L[d[i].klucz]);
// Obliczamy pozycje końcowe elementów
for i := KMIN + 1 to KMAX do inc(L[i], L[i - 1]);
// Przepisujemy elementy z d[ ] do b[ ]
for i := N downto 1 do
begin
b[L[d[i].klucz]] := d[i];
dec(L[d[i].klucz]);
end;
// Wyświetlamy wyniki w b[ ]
writeln('Po sortowaniu:');
writeln;
for i := 1 to N do write(b[i].wyraz:4);
writeln;
writeln('Koniec. Nacisnij klawisz Enter...');
readln;
end.
Code::Blocks
// Sortowanie przez zliczanie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <iostream>
#include <cstdlib>
#include <time.h>
using namespace std;
int main()
{
const int N = 80;
const int KMIN = 0;
const int KMAX = 26;
struct
{
unsigned klucz;
char wyraz[3];
} d[N],b[N];
unsigned L[KMAX - KMIN + 1];
int i,j,v;
cout << " Sortowanie Przez Zliczanie\n"
"------------------------------\n"
" (C)2005 mgr Jerzy Walaszek\n\n"
"Przed sortowaniem:\n\n";
// Generujemy losowe elementy do sortowania oraz ich klucze
srand((unsigned)time(NULL));
for(i = 0; i < N; i++)
{
for(j = 0; j < 3; j++) d[i].wyraz[j] = 65 + rand() % 3;
d[i].klucz = 0;
v = 1;
for(j = 2; j >= 0; j--)
{
d[i].klucz += v * (d[i].wyraz[j] - 65);
v *= 3;
}
}
// Wyświetlamy wygenerowane elementy
for(i = 0; i < N; i++)
cout << ' ' << d[i].wyraz[0] << d[i].wyraz[1] << d[i].wyraz[2];
cout << endl;
// Zerujemy liczniki
for(i = KMIN; i <= KMAX; i++) L[i - KMIN] = 0;
// Zliczamy wystąpienia kluczy
for(i = 0; i < N; i++) L[d[i].klucz - KMIN]++;
// Obliczamy pozycje końcowe elementów
for(i = KMIN + 1; i <= KMAX; i++) L[i - KMIN] += L[i - KMIN - 1];
// Przepisujemy elementy z d[ ] do b[ ]
for(i = N - 1; i >= 0; i--) b[(L[d[i].klucz - KMIN]--) - 1] = d[i];
// Wyświetlamy wyniki w b[ ]
cout << "Po sortowaniu:\n\n";
for(i = 0; i < N; i++)
cout << ' ' << b[i].wyraz[0] << b[i].wyraz[1] << b[i].wyraz[2];
cout << endl;
return 0;
}
Free Basic
' Sortowanie przez zliczanie
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 80
Const KMIN = 0
Const KMAX = 26
' Tutaj definiujemy typ elementu
Type TElement
klucz As Uinteger
wyraz As String * 3
End Type
' Zmienne
Dim As TElement d(N),b(N)
Dim As Uinteger L(KMIN To KMAX)
Dim As Integer i,j,v
Dim s As String
Print " Sortowanie Przez Zliczanie "
Print "------------------------------"
Print " (C)2005 mgr Jerzy Walaszek "
Print
Print "Przed sortowaniem:"
Print
' Generujemy losowe elementy do sortowania oraz ich klucze
Randomize
For i = 1 To N
s = ""
For j = 1 To 3: s += CHR$(65 + Int(Rnd(1) * 3)): Next
d(i).wyraz = s
d(i).klucz = 0
v = 1
For j = 3 To 1 Step -1
d(i).klucz += v * (Asc(MID$(d(i).wyraz,j,1)) - 65)
v *= 3
Next
Next
' Wyświetlamy wygenerowane elementy
For i = 1 To N: Print " "; d(i).wyraz;: Next
Print
' Zerujemy liczniki
For i = KMIN To KMAX: L(i) = 0: Next
' Zliczamy wystąpienia kluczy
For i = 1 To N: L(d(i).klucz) += 1: Next
' Obliczamy pozycje końcowe elementów
For i = KMIN + 1 To KMAX: L(i) += L(i - 1): Next
' Przepisujemy elementy z d[ ] do b[ ]
For i = N To 1 Step -1
b(L(d(i).klucz)) = d(i)
L(d(i).klucz) -= 1
Next
' Wyświetlamy wyniki w b[ ]
Print "Po sortowaniu:"
Print
For i = 1 To N: Print " ";b(i).wyraz;: Next
Print
Print "Koniec. Nacisnij dowolny klawisz..."
Sleep
End
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmcountingsort">
<h3 style="text-align: center">Sortowanie Przez Zliczanie</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie przez zliczanie
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 80;
var KMIN = 0;
var KMAX = 26;
var d = new Array();
var b = new Array();
var L = new Array;
var i;
for(i = 0; i < N; i++)
{
d[i] = new Object();
b[i] = new Object();
}
function main()
{
var i,j,t,v;
// Generujemy losowe elementy do sortowania oraz ich klucze
for(i = 0; i < N; i++)
{
d[i].wyraz = String.fromCharCode(65 + Math.floor(Math.random() * 3)) +
String.fromCharCode(65 + Math.floor(Math.random() * 3)) +
String.fromCharCode(65 + Math.floor(Math.random() * 3));
d[i].klucz = 0;
v = 1;
for(j = 2; j >= 0; j--)
{
d[i].klucz += v * (d[i].wyraz.charCodeAt(j) - 65);
v *= 3;
}
}
// Wyświetlamy wygenerowane elementy
t = "Przed sortowaniem:<BR><BR>";
for(i = 0; i < N; i++) t += " " + d[i].wyraz;
t += "<BR><BR>";
// Zerujemy liczniki
for(i = KMIN; i <= KMAX; i++) L[i - KMIN] = 0;
// Zliczamy wystąpienia kluczy
for(i = 0; i < N; i++) L[d[i].klucz - KMIN]++;
// Obliczamy pozycje końcowe elementów
for(i = KMIN + 1; i <= KMAX; i++) L[i - KMIN] += L[i - KMIN - 1];
// Przepisujemy elementy z d[ ] do b[ ]
for(i = N - 1; i >= 0; i--) b[(L[d[i].klucz - KMIN]--) - 1] = d[i];
// Wyświetlamy wyniki w b[ ]
t += "Po sortowaniu:<BR><BR>";
for(i = 0; i < N; i++) t += " " + b[i].wyraz;
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Sortuj
...
Temat:
Uwaga: ← tutaj wpisz wyraz ilo , inaczej list zostanie zignorowany
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Sortowanie Pozycyjne
Radix Sort
Podrozdziały Tematy pokrewne
Algorytm Programy Badanie algorytmów sortujących Sortowanie rozrzutowe
Specyfikacja problemu Program w języku Pascal Podsumowanie Sortowanie kubełkowe 1
Lista kroków Program w języku C++ Zadania dla ambitnych Listy
Schemat blokowy Program w języku Basic Sortowanie kubełkowe 2
Program w języku JavaScript Sortowanie przez zliczanie
Algorytm
Pozycją (ang. radix) nazywamy miejsce cyfry w zapisie liczby. Sortowanie pozycyjne polega na sortowaniu elementów wg
kolejnych (licząc od końca) cyfr liczby. Algorytm sortujący musi być stabilny, tzn. nie może zmieniać kolejności elementów
równych, w przeciwnym razie efekty poprzednich sortowań zostaną utracone.
Przykład:
Posortować algorytmem sortowania pozycyjnego zbiór liczb:
{ 547 398 247 153 121 792 421 }
Liczby posortujemy wg kolejnych od końca cyfr:
Przykład:
Posortować algorytmem sortowania pozycyjnego zbiór liczb binarnych:
{100111 111011 011101 101011 001111 101000 110011 100001 101101 111111 000011}
start koniec
100111 101000 101000 101000 100001 100001 000011 000011
111011 100111 011101 100001 110011 000011 001111 001111
011101 111011 100001 111011 000011 100111 011101 011101
101011 011101 101101 101011 100111 101000 100001 100001
001111 101011 100111 110011 101000 101011 100111 100111
101000 001111 111011 000011 111011 101101 101000 101000
110011 110011 101011 011101 101011 001111 101011 101011
100001 100001 001111 101101 011101 110011 101101 101101
101101 101101 110011 100111 101101 111011 110011 110011
111111 111111 111111 001111 001111 011101 111011 111011
000011 000011 000011 111111 111111 111111 111111 111111
Sortowanie wg bitów jest bardzo proste, jednakże mało efektywne. Lepszym rozwiązaniem jest przyjęcie za podstawę p liczby
będącej potęgą 2, np. 24 lub 28. W takim przypadku sortujemy wg grup bitów (dla 24 - grupy 4 bitów, dla 28 - grupy 8 bitów).
Zgodnie z podanym wcześniej wzorem klasa czasowej złożoności obliczeniowej będzie równa:
Oba wzory sprowadzają się do O(n log n) (udowodnij to), lecz sortowanie wykonywane jest odpowiednio 4 i 8 razy szybciej
(mniejsze współczynniki proporcjonalności c).
Jako algorytm sortujący idealnie pasuje opisany w poprzednim rozdziale algorytm sortowania przez zliczanie.
Specyfikacja problemu
Dane wejściowe
n - liczba elementów w sortowanym zbiorze, n Î N
d[ ] - sortowany zbiór danych. Indeksy elementów rozpoczynają się od wartości 1.
MAXel - maksymalna wartość sortowanych elementów
Dane wyjściowe
d[ ] - posortowany rosnąco zbiór danych
Zmienne pomocnicze
b[ ] - pomocniczy zbiór danych. Indeksy elementów rozpoczynają się od 1.
m - maska bitowa do wydzielania bitu z elementu, m Î N
i - zmienna sterująca pętli, i Î N
L0 - licznik bitów 0, L0 Î N
L1 - licznik bitów 1, L1 Î N
Lista kroków
Sortuj(z1[ ], z2[ ], m)
K01: L0 ← 0; L1 ← 0
K02: Dla i = 1,2,...,n: wykonuj K03
K03: Jeśli z1[i] m ≠ 0, to
L1 ← L1 + 1
inaczej
L0 ← L0 + 1
K04: L1 ← L1 + L0
K05: Dla i = n, n - 1,...,1: wykonuj K06
K06: Jeśli z1[i] m ≠ 0, to
z2[L1] ← z1[i]; L1 ← L1 - 1
inaczej
z2[L0] ← z1[i]; L0 ← L0 - 1
K07: Zakończ
Schemat blokowy
Zasada pracy algorytmu sortowania pozycyjnego jest bardzo prosta. Ustawiamy maskę bitową m tak, aby bit b0 miał wartość 1.
Następnie w pętli warunkowej wywołujemy procedurę sortowania przez zliczanie przekazując jej jako parametry sortowany zbiór
danych d[ ] oraz zbiór pomocniczy b[ ], w którym znajdzie się wynik sortowania, i maskę m. Przesuwamy bity maski o jeden w
lewo. Jeszcze raz wywołujemy procedurę sortującą (zwróć uwagę na zamienioną kolejność zbiorów - sortowany jest b[ ], a wynik
trafia z powrotem do d[ ]) i przesuwamy bity maski o jeden w lewo.
Procedura sortująca działa wg opisanego w poprzednim rozdziale algorytmu sortowania przez zliczanie:
Zerujemy liczniki bitu 0 oraz bitu 1. W pierwszej pętli zliczamy bity 0 i 1. Następnie obliczamy ostatnie pozycje dla elementów
zawierających odpowiednio bit 0 i bit 1. Przepisujemy elementy ze zbioru wejściowego na odpowiadające im pozycje w zbiorze
wyjściowym.
Programy
Efekt uruchomienia programu
Sortowanie pozycyjne
------------------------
(C)2005 Jerzy Walaszek
Przed sortowaniem:
479 964 207 34 789 26 943 371 364 245 413 774 740 391 98 512 952 109 426 671
340 59 614 712 577 384 730 460 463 515 312 737 635 510 60 214 514 226 178 494
652 468 869 792 852 638 417 22 542 633 507 6 98 905 448 358 569 129 723 323
373 777 147 251 862 341 428 992 402 128 196 528 572 65 690 484 569 733 898 238
Po sortowaniu:
6 22 26 34 59 60 65 98 98 109 128 129 147 178 196 207 214 226 238 245
251 312 323 340 341 358 364 371 373 384 391 402 413 417 426 428 448 460 463 468
479 484 494 507 510 512 514 515 528 542 569 569 572 577 614 633 635 638 652 671
690 712 723 730 733 737 740 774 777 789 792 852 862 869 898 905 943 952 964 992
DevPascal
// Sortowanie Pozycyjne
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
program RadixSort;
const
N = 80; // ilość elementów
MAXEL = 999; // maksymalna wartość elementu
type
TZbior = array[1..N] of cardinal;
// Procedura sortująca
procedure Sortuj(var z1,z2 : TZbior; m : cardinal);
var
L : array[boolean] of cardinal;
i : cardinal;
t : boolean;
begin
L[false] := 0; L[true] := 0;
for i := 1 to N do inc(L[(z1[i] and m) > 0]);
inc(L[true], L[false]);
for i := N downto 1 do
begin
t := (z1[i] and m) > 0;
z2[L[t]] := z1[i];
dec(L[t]);
end;
end;
var
d,b : TZbior;
i : integer;
m : cardinal;
begin
writeln(' Sortowanie pozycyjne ');
writeln('------------------------');
writeln(' (C)2005 Jerzy Walaszek ');
writeln;
// Generujemy pseudolosową zawartość zbioru d[ ]
randomize;
for i := 1 to N do d[i] := random(MAXEL + 1);
writeln('Przed sortowaniem:');
writeln;
for i := 1 to N do write(d[i]:4);
// Ustawiamy maskę na najmłodszy bit
m := 1;
// Sortujemy
while m <= MAXEL do
begin
Sortuj(d,b,m); m := m shl 1;
Sortuj(b,d,m); m := m shl 1;
end;
// Wyświetlamy wyniki
writeln;
writeln('Po sortowaniu:');
writeln;
for i := 1 to N do write(d[i]:4);
writeln;
writeln('KONIEC. Nacisnij klawisz Enter...'); readln;
end.
Code::Blocks
// Sortowanie Pozycyjne
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <time.h>
using namespace std;
const int N = 80; // ilość elementów
const int MAXEL = 999; // maksymalna wartość elementu
// Procedura sortująca
void Sortuj(unsigned z1[], unsigned z2[], unsigned m)
{
unsigned L[2],i;
L[0] = L[1] = 0;
for(i = 1; i <= N; i++) L[(z1[i] & m) > 0]++;
L[1] += L[0];
for(i = N; i >= 1; i--) z2[L[(z1[i] & m) > 0]--] = z1[i];
}
int main()
{
unsigned d[N+1],b[N+1],i,m;
cout << " Sortowanie pozycyjne \n"
"------------------------\n"
" (C)2005 Jerzy Walaszek \n\n"
"Przed sortowaniem:\n\n";
// Generujemy pseudolosową zawartość zbioru d[ ]
srand((unsigned)time(NULL));
for(i = 1; i <= N; i++)
{
d[i] = rand() % (MAXEL + 1);
cout << setw(4) << d[i];
}
// Ustawiamy maskę na najmłodszy bit
m = 1;
// Sortujemy
while(m <= MAXEL)
{
Sortuj(d,b,m); m <<= 1;
Sortuj(b,d,m); m <<= 1;
}
// Wyświetlamy wyniki
cout << "\nPo sortowaniu:\n\n";
for(i = 1; i <= N; i++) cout << setw(4) << d[i];
cout << endl;
return 0;
}
Free Basic
' Sortowanie Pozycyjne
'--------------------------------------------------------
' (C)2012 I LO w Tarnowie
' I Liceum Ogólnokształcące
' im. K. Brodzińskiego
' w Tarnowie
'--------------------------------------------------------
Option Explicit
Const N = 80 ' ilość elementów
Const MAXEL = 999 ' maksymalna wartość elementu
' Procedura sortująca
Declare Sub Sortuj(z1() As Uinteger, z2() As Uinteger, Byval m As Uinteger)
Sub Sortuj(z1() As Uinteger, z2() As Uinteger, Byval m As Uinteger)
Dim As Uinteger L0,L1,i
L0 = 0: L1 = 0
For i = 1 To N
If (z1(i) And m) > 0 Then
L1 += 1
Else
L0 += 1
End If
Next
L1 += L0
For i = N To 1 Step -1
If (z1(i) And m) > 0 Then
z2(L1) = z1(i): L1 -= 1
Else
z2(L0) = z1(i): L0 -= 1
End If
Next
End Sub
Dim As Uinteger d(N),b(N),i,m
Print " Sortowanie pozycyjne "
Print "------------------------"
Print " (C)2005 Jerzy Walaszek "
Print
' Generujemy pseudolosową zawartość zbioru d( )
Randomize
For i = 1 To N: d(i) = Int(Rnd(1) * (MAXEL + 1)): Next
Print "Przed sortowaniem:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
' Ustawiamy maskę na najmłodszy bit
m = 1
' Sortujemy
While m <= MAXEL
Sortuj(d(),b(),m): m = m Shl 1
Sortuj(b(),d(),m): m = m Shl 1
Wend
' Wyświetlamy wyniki
Print
Print "Po sortowaniu:"
Print
For i = 1 To N: Print Using "####"; d(i);: Next
Print
Print "KONIEC. Nacisnij dowolny klawisz..."
Sleep
End
JavaScript
<html>
<head>
</head>
<body>
<form style="BORDER-RIGHT: #ff9933 1px outset;
PADDING-RIGHT: 4px; BORDER-TOP: #ff9933 1px outset;
PADDING-LEFT: 4px; PADDING-BOTTOM: 1px;
BORDER-LEFT: #ff9933 1px outset; PADDING-TOP: 1px;
BORDER-BOTTOM: #ff9933 1px outset;
BACKGROUND-COLOR: #ffcc66" name="frmradixsort">
<h3 style="text-align: center">Sortowanie Pozycyjne</h3>
<p style="TEXT-ALIGN: center">
(C)2012 I LO w Tarnowie - I LO w Tarnowie
</p>
<hr>
<p style="TEXT-ALIGN: center">
<input onclick="main()" type="button" value="Sortuj" name="B1">
</p>
<p id="t_out" style="TEXT-ALIGN: center">...</p>
</form>
<script language=javascript>
// Sortowanie Pozycyjne
//--------------------------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------------------------------------
var N = 80; // ilość elementów
var MAXEL = 999; // maksymalna wartość elementu
// Procedura sortująca
function Sortuj(z1,z2,m)
{
var L0,L1,i;
L0 = L1 = 0;
for(i = 1; i <= N; i++) if((z1[i] & m) > 0) L1++; else L0++;
L1 += L0;
for(i = N; i >= 1; i--)
if((z1[i] & m) > 0) z2[L1--] = z1[i]; else z2[L0--] = z1[i];
}
function main()
{
var d = new Array(N + 1);
var b = new Array(N + 1);
var i,m,t;
t = "Przed sortowaniem:<BR><BR>";
// Generujemy pseudolosową zawartość zbioru d[ ]
for(i = 1; i <= N; i++)
{
d[i] = Math.floor(Math.random() *(MAXEL + 1));
t += " " + d[i];
}
// Ustawiamy maskę na najmłodszy bit
m = 1;
// Sortujemy
while(m <= MAXEL)
{
Sortuj(d,b,m); m <<= 1;
Sortuj(b,d,m); m <<= 1;
}
// Wyświetlamy wyniki
t += "<BR><BR>Po sortowaniu:<BR><BR>";
for(i = 1; i <= N; i++) t += " " + d[i];
document.getElementById("t_out").innerHTML = t;
}
</script>
</body>
</html>
Sortowanie Pozycyjne
(C)2012 I LO w Tarnowie - I LO w Tarnowie
Sortuj
...
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.
Podsumowanie
Podrozdziały
Porównanie klas czasowej złożoności obliczeniowej
Własności algorytmów sortujących
Porównanie szybkości sortowania
Wnioski
Literatura
Klasa złożoności
Lp. Nazwa algorytmu Stabilność Sortowanie Zalecane?
optymistyczna typowa pesymistyczna w miejscu
5. Bubble Sort III O(n) ... O(n2) O(n2) O(n2) TAK TAK TAK/NIE
10. Binary Insertion Sort O(n log n) O(n2) O(n2) TAK TAK NIE
4. Bubble Sort III tpo << tod tpp << tpk tnp ≈3tod
5
5. Bubble Sort IV tpo << tod tpp ≈5tpk tnp ≈3tod
3 5
6. Bidirectional Bubble Sort tpo << tod tpp ≈9tpk tnp ≈3tod
8 5
7. Selection Sort tpo ≈ tod ≈ tpp ≈ tpk ≈ tnp
9. Binary Insertion Sort tpo << tod tpp ≈ tpk tnp ≈1tod
2
DevPascal
// Program wyznaczający kolejność algorytmów sortujących
// wg czasu sortowania zbioru 10000 liczb naturalnych
// w warunkach kontrolowanego uruchomienia
//--------------------------------------
// (C)2012 I LO w Tarnowie
// I Liceum Ogólnokształcące
// w Tarnowie
//--------------------------------------
program KolAlgSort;
uses Windows;
const
N = 10000; // Ilość sortowanych elementów
WMIN = 0; // Wartość minimalna elementów
WMAX = 10000; // Wartość maksymalna elementów
type
TElement = record
nastepnik : cardinal;
dane : int64;
end;
TZbior = array[1..N] of int64;
var
b,d,e : array[1..N] of int64;
qpf,tqpc : int64;
qpc1,qpc2 : int64;
// Poszczególne algorytmy sortujące
procedure StupidSort;
var
i : integer;
x : int64;
begin
i := 1;
repeat
if d[i] > d[i + 1] then
begin
x := d[i]; d[i] := d[i + 1]; d[i + 1] := x;
i := 1;
continue;
end;
inc(i);
until i = N;
end;
//----------------------------------------------------
procedure BubbleSort1;
var
i,j : integer;
x : int64;
begin
for j := 1 to N - 1 do
for i := 1 to N - 1 do
if d[i] > d[i + 1] then
begin
x := d[i]; d[i] := d[i + 1]; d[i + 1] := x;
end;
end;
//----------------------------------------------------
procedure BubbleSort2;
var
i,j : integer;
x : int64;
begin
for j := N - 1 downto 1 do
for i := 1 to j do
if d[i] > d[i + 1] then
begin
x := d[i]; d[i] := d[i + 1]; d[i + 1] := x;
end;
end;
//----------------------------------------------------
procedure BubbleSort3;
var
i,j,p : integer;
x : int64;
begin
for j := N - 1 downto 1 do
begin
p := 1;
for i := 1 to j do
if d[i] > d[i + 1] then
begin
x := d[i]; d[i] := d[i + 1]; d[i + 1] := x;
p := 0;
end;
if p = 1 then break;
end;
end;
//----------------------------------------------------
procedure BubbleSort4;
var
i,p,pmin,pmax : integer;
x : int64;
begin
pmin := 1; pmax := N - 1;
repeat
p := 0;
for i := pmin to pmax do
if d[i] > d[i + 1] then
begin
x := d[i]; d[i] := d[i + 1]; d[i + 1] := x;
if p = 0 then pmin := i;
p := i;
end;
if pmin > 1 then dec(pmin);
pmax := p - 1;
until p = 0;
end;
//----------------------------------------------------
procedure BidirectionalBubbleSort;
var
i,p,pmin,pmax : integer;
x : int64;
begin
pmin := 1; pmax := N - 1;
repeat
p := 0;
for i := pmin to pmax do
if d[i] > d[i + 1] then
begin
x := d[i]; d[i] := d[i + 1]; d[i + 1] := x;
p := i;
end;
if p = 0 then break;
pmax := p - 1;
p := 0;
for i := pmax downto pmin do
if d[i] > d[i + 1] then
begin
x := d[i]; d[i] := d[i + 1]; d[i + 1] := x;
p := i;
end;
pmin := p + 1;
until p = 0;
end;
//----------------------------------------------------
procedure SelectionSort;
var
i,j,pmin : integer;
x : int64;
begin
for j := 1 to N - 1 do
begin
pmin := j;
for i := j + 1 to N do
if d[i] < d[pmin] then pmin := i;
x := d[pmin]; d[pmin] := d[j]; d[j] := x;
end;
end;
//----------------------------------------------------
procedure InsertionSort;
var
i,j : integer;
x : int64;
begin
for j := N - 1 downto 1 do
begin
x := d[j];
i := j + 1;
while (i <= N) and (x > d[i]) do
begin
d[i - 1] := d[i];
inc(i);
end;
d[i - 1] := x;
end;
end;
//----------------------------------------------------
procedure BinaryInsertionSort;
var
i,j,ip,ik : integer;
x : int64;
begin
for j := N - 1 downto 1 do
begin
x := d[j];
ip := j;
ik := N + 1;
while ik - ip > 1 do
begin
i := (ik + ip) div 2;
if x <= d[i] then ik := i else ip := i;
end;
for i := j to ip - 1 do d[i] := d[i + 1];
d[ip] := x;
end;
end;
//----------------------------------------------------
procedure ShellSort;
var
i,j,h : integer;
x : int64;
begin
h := 1;
repeat
h := 3 * h + 1;
until h >= N;
h := (h div 9);
while(h > 0) do
begin
for j := N - h downto 1 do
begin
x := d[j];
i := j + h;
while (i <= N) and (x > d[i]) do
begin
d[i - h] := d[i];
i += h;
end;
d[i - h] := x;
end;
h := h div 3;
end;
end;
//----------------------------------------------------
procedure MergeSort(i_p,i_k : integer);
var
i_s,i1,i2,i : integer;
begin
i_s := (i_p + i_k + 1) div 2;
if i_s - i_p > 1 then MergeSort(i_p, i_s - 1);
if i_k - i_s > 0 then MergeSort(i_s, i_k);
i1 := i_p; i2 := i_s;
for i := i_p to i_k do
if (i1 = i_s) or ((i2 <= i_k) and (d[i1] > d[i2])) then
begin
b[i] := d[i2]; inc(i2);
end
else
begin
b[i] := d[i1]; inc(i1);
end;
for i := i_p to i_k do d[i] := b[i];
end;
//----------------------------------------------------
procedure HeapSort;
var
i,j,k,m : integer;
x : int64;
begin
for i := 2 to N do
begin
j := i; k := j div 2;
x := d[i];
while (k > 0) and (d[k] < x) do
begin
d[j] := d[k];
j := k; k := j div 2;
end;
d[j] := x;
end;
for i := N downto 2 do
begin
x := d[1]; d[1] := d[i]; d[i] := x;
j := 1; k := 2;
while k < i do
begin
if (k + 1 < i) and (d[k + 1] > d[k]) then
m := k + 1
else
m := k;
if d[m] <= d[j] then break;
x := d[j]; d[j] := d[m]; d[m] := x;
j := m; k := j + j;
end;
end;
end;
//----------------------------------------------------
procedure QuickSort(lewy, prawy : integer);
var
i,j : integer;
piwot,x : int64;
begin
i := (lewy + prawy) div 2;
piwot := d[i]; d[i] := d[prawy];
j := lewy;
for i := lewy to prawy - 1 do
if d[i] < piwot then
begin
x := d[i]; d[i] := d[j]; d[j] := x;
inc(j);
end;
d[prawy] := d[j]; d[j] := piwot;
if lewy < j - 1 then QuickSort(lewy, j - 1);
if j + 1 < prawy then QuickSort(j + 1, prawy);
end;
//----------------------------------------------------
procedure BucketSort1;
var
lw : array[WMIN..WMAX] of integer;
i,j : integer;
begin
for i := WMIN to WMAX do lw[i] := 0;
for i := 1 to N do inc(lw[d[i]]);
j := 1;
for i := WMIN to WMAX do
while lw[i] > 0 do
begin
d[j] := i; inc(j); dec(lw[i]);
end;
end;
//----------------------------------------------------
procedure BucketSort2;
var
L : array[1..N] of TElement;
K : array[WMIN..WMAX] of integer;
we : int64;
ine,ip,ib,i,j : integer;
begin
for i := WMIN to WMAX do K[i] := 0;
ine := 1;
for i := 1 to N do
begin
we := d[i];
L[ine].nastepnik := 0; L[ine].dane := we;
ip := 0; ib := K[we];
while (ib > 0) and (L[ib].dane < we) do
begin
ip := ib; ib := L[ib].nastepnik;
end;
if ip = 0 then
begin
L[ine].nastepnik := ib; K[we] := ine
end
else if ib = 0 then
L[ip].nastepnik := ine
else
begin
L[ip].nastepnik := ine; L[ine].nastepnik := ib;
end;
inc(ine);
end;
j := 1;
for ib := WMIN to WMAX do
begin
i := K[ib];
while i > 0 do
begin
d[j] := L[i].dane;
inc(j); i := L[i].nastepnik;
end;
end;
end;
//----------------------------------------------------
procedure CountingSort;
var
L : array[WMIN..WMAX] of cardinal;
i : integer;
begin
for i := WMIN to WMAX do L[i] := 0;
for i := 1 to N do inc(L[d[i]]);
for i := WMIN + 1 to WMAX do L[i] += L[i - 1];
for i := N downto 1 do
begin
b[L[d[i]]] := d[i]; dec(L[d[i]]);
end;
end;
//----------------------------------------------------
procedure SortujBit(var z1,z2 : TZbior; m : int64);
var
L : array[boolean] of cardinal;
i : cardinal;
t : boolean;
begin
L[false] := 0; L[true] := 0;
for i := 1 to N do inc(L[(z1[i] and m) > 0]);
L[true] += L[false];
for i := N downto 1 do
begin
t := (z1[i] and m) > 0;
z2[L[t]] := z1[i];
dec(L[t]);
end;
end;
procedure RadixSort;
var
m : int64;
begin
m := 1;
while m <= WMAX do
begin
SortujBit(d,b,m); m := m shl 1;
SortujBit(b,d,m); m := m shl 1;
end;
end;
// Funkcja pomiaru czasu sortowania
function Sort(nr : integer) : extended;
begin
QueryPerformanceCounter(addr(qpc1));
case nr of
1 : StupidSort;
2 : BubbleSort1;
3 : BubbleSort2;
4 : BubbleSort3;
5 : BubbleSort4;
6 : BidirectionalBubbleSort;
7 : SelectionSort;
8 : InsertionSort;
9 : BinaryInsertionSort;
10 : ShellSort;
11 : MergeSort(1,N);
12 : HeapSort;
13 : QuickSort(1,N);
14 : BucketSort1;
15 : BucketSort2;
16 : CountingSort;
17 : RadixSort;
end;
QueryPerformanceCounter(addr(qpc2));
Sort := (qpc2 - qpc1 - tqpc) / qpf;
end;
// Program główny
//---------------
const
NazwaAlgorytmu : array[1..17] of string =
('Stupid Sort','Bubble Sort I','Bubble Sort II','Bubble Sort III',
'Bubble Sort IV','Bidirectional Bubble Sort','Selection Sort',
'Insertion Sort','Binary Insertion Sort','Shell Sort','Merge Sort',
'Heap Sort','Quick Sort','Bucket Sort I','Bucket Sort II',
'Counting Sort','Radix Sort');
type
TAlgorytm = record
nazwa : string;
czas : extended;
end;
var
algorytm : array[1..18] of TAlgorytm;
i,j : integer;
f : Text;
begin
writeln('Porownanie czasu sortowania wybranych algorytmow');
writeln('-------------------------------------------------------');
writeln('(C)2005 mgr Jerzy Walaszek I LO w Tarnowie');
writeln;
if QueryPerformanceFrequency(addr(qpf)) then
begin
QueryPerformanceCounter(addr(qpc1));
QueryPerformanceCounter(addr(qpc2));
tqpc := qpc2 - qpc1;
assignfile(f,'wyniki.txt'); rewrite(f);
writeln(f,'Porownanie czasu sortowania wybranych algorytmow');
writeln(f,'-------------------------------------------------------');
writeln(f,'(C)2005 mgr Jerzy Walaszek I LO w Tarnowie');
writeln(f,'');
randomize;
for i := 1 to N do e[i] := random(WMAX + 1);
for i := 1 to 17 do
with algorytm[i] do
begin
d := e;
nazwa := NazwaAlgorytmu[i];
czas := Sort(i);
writeln(czas:11:6,' : ',nazwa);
writeln(f,czas:11:6,' : ',nazwa);
end;
for j := 16 downto 1 do
begin
algorytm[18] := algorytm[j];
i := j + 1;
while (i <= 17) and (algorytm[18].czas > algorytm[i].czas) do
begin
algorytm[i - 1] := algorytm[i];
inc(i);
end;
algorytm[i - 1] := algorytm[18];
end;
writeln;
writeln('Ranking:');
writeln;
writeln(f,'');
writeln(f,'Ranking:');
writeln(f,'');
for i := 1 to 17 do
begin
writeln(i:2,'. ',algorytm[i].czas:11:6,' - ',algorytm[i].nazwa);
writeln(f,i:2,'. ',algorytm[i].czas:11:6,' - ',algorytm[i].nazwa);
end;
CloseFile(f);
end
else
writeln('Na tym komputerze nie mozna wykonac pomiaru!');
writeln;
Konkurs szybkości sortowania wygrywają algorytmy sortowania dystrybucyjnego - jest to dosyć oczywiste, ponieważ mają one
liniową klasę złożoności obliczeniowej. Z algorytmów porównujących klasy liniowo logarytmicznej na pierwszej pozycji wyłania się
algorytm sortowania szybkiego. Z algorytmów klasy kwadratowej pierwsze miejsce zajął algorytm binarnego sortowania przez
wstawianie. Tuż za nim jest algorytm sortowania przez wstawianie. Algorytmy sortowania bąbelkowego zamykają naszą listę.
Algorytm sortowania głupiego jest daleko, daleko w tyle.
Wnioski
Najszybsze algorytmy sortujące to algorytmy sortowania dystrybucyjnego. Jednakże szybkość ich działania jest okupiona dużym
zapotrzebowaniem na pamięć. Algorytmy te mają liniową klasę czasowej złożoności obliczeniowej.
Z algorytmów sortujących w miejscu najszybszym w typowych warunkach jest algorytm sortowania szybkiego. Posiada liniowo
logarytmiczną klasę czasowej złożoności obliczeniowej. Jednakże dla niekorzystnych danych może się degradować do klasy
kwadratowej.
Wolniejszym (około dwa razy) algorytmem sortowania klasy liniowo logarytmicznej jest algorytm sortowania stogowego. Algorytm
ten nie degraduje się do niższej klasy złożoności obliczeniowej i może być alternatywą dla algorytmu sortowania szybkiego.
Bardzo obiecujące wyniki otrzymaliśmy dla algorytmu sortowania metodą Shella.
W klasie kwadratowej złożoności obliczeniowej zalecany jest do stosowania algorytm sortowania przez wstawianie. Jest bardzo
prosty w implementacji i jednocześnie wystarczająco szybki. Dla zbiorów w znacznym stopniu uporządkowanych wykazuje liniową
klasę złożoności obliczeniowej. Dlatego nadaje się np. do sortowania zbioru uporządkowanego, do którego dodajemy nowy
element - zbiór będzie posortowany szybciej niż przez algorytm sortowania szybkiego (porównaj czasy tpp i tpk dla 32000
elementów). Ten wniosek jasno pokazuje, iż nie ma uniwersalnych algorytmów sortujących.
Algorytmów sortowania bąbelkowego raczej należy unikać.
Literatura
Donald Knuth, The Art of Computer Programming, Vol.3: Sorting and Searching, Addison-Wesley, 1998
Robert Segewick, Algorithms, Addison-Wesley, 1984
Jerzy Grębosz, Symfonia C++, Oficyna Kallimach, Kraków 1999
Niklaus Wirth, Algorytmy + struktury danych = programy, WNT Warszawa 1980,
Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).
W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby
rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień
szeroko opisywanych w podręcznikach.