Przykładowe sprawozdanie Jan Pustelnik 30 marca 2007
Rozdział 1 Sformułowanie problemu Tematem pracy jest porównanie wydajności trzech tradycyjnych metod sortowania: InsertionSort, SelectionSort i BubbleSort. Dla uproszczenia zakładamy tradycyjny model kosztu obliczeniowego, w którym dana jest maszyna o stałym czasie wykonywania operacji dodawania liczb całkowitych dowolnej długości. Parametrem zmiennym będzie liczba elementów tablicy wejściowej, oznaczana dalej literą n. Wszystkie trzy porównywane algorytmy charakteryzują się we wspomnianym powyżej modelu przeciętną złożonością obliczeniową O(n 2 ), przy czym stały współczynnik kryjący się za notacją O( ) jest różny dla wszystkich trzech metod. Dla celów poniżej prezentowanych badań jako język implementacji wybrano ISO C++, kompilacji dokonano kompilatorem gcc zaś architekturą dla której generowany był kod jest architektura x86.
Rozdział 2 Krótki opis porównywanych metod sortowania Wszystkie metody będą prezentowane zgodnie z [2]. Założono istnienie dwóch funkcji: exch i compexch, zdefiniowanych jak niżej: 1 // S e d g e w i c k, s t r. 242 template <typename Item> void exch ( Item &A, Item &B) { Item t = A; A = B ; 6 B = t ; template <typename Item> void compexch ( Item &A, Item &B) { 11 i f (B < A) exch (A, B) ; 2.1 InsertionSort Poniżej fragment kodu implementujący metodę sortowania przez wstawianie (ang. insertion sort) w języku C++. Metoda ta będzie uruchamiana przez większy fragment kodu (tzw. sterownik - driver lub rusztowanie - scaffolding). 1 // S e d g e w i c k s t r o n a 249 // w c i e c i a K&R template <typename Item> void i n s e r t i o n ( Item a [ ], int l, int r ) { 6 f o r ( int i = r ; i > 1 ; i ) compexch ( a [ i 1], a [ i ] ) ; f o r ( int i = l +2; i <= r ; ++i ) { int j = i ; Item v = a [ i ] ; 11 while ( v < a [ j 1]) { a [ j ] = a [ j 1 ] ; j ;
2.2. SELECTIONSORT 3 16 a [ j ] = v ; 2.2 SelectionSort Poniżej prezentowany jest fragment kodu implementujący w C++ metodę sortowania przez wybór (ang. selection sort). // S e d g e w i c k, s t r. 246 2 template <typename Item> void s e l e c t i o n ( Item a [ ], int l, int r ) { f o r ( int i = l ; i < r ; ++i ) { int min = i ; f o r ( int j = i + 1 ; j <= r ; ++j ) 7 i f ( a [ j ] < a [ min ] ) min = j ; exch ( a [ i ], a [ min ] ) ; 2.3 BubbleSort I wreszcie ostatni framgent kodu w C++ - implementacja metody sortowania bąbelkowego (ang. bubble sort). Ciekawą analizę tej metody sortowania wraz z propozycjami zwiększenia wydajności prezentuje [1] // S e d g e w i c k, s t r. 251 template <typename Item> void bubble ( Item a [ ], int l, int r ) { 4 f o r ( int i = l ; i < r ; ++i ) f o r ( i n j = r ; j > i ; j ) compexch ( a [ j 1], a [ j ] ) ;
Rozdział 3 Metodologia użyta w porównaniu W porównaniu użyto dosyć prostej metodologii. Porównano wybrane metody sortowania dla danych wejściowych o zmiennym rozmiarze n, gdzie n zmieniało się w zakresie od 0 do 2 10 5, przy czym użyto dwóch podzakresów: od 0 do 2 10 4, co 1000 (1 10 3 ) i od 0 do 2 10 5, co 10000 (1 10 4 ). Zastosowano również trzy różne rodzaje zbiorów wejściowych: zbiór losowy (rozkład jednostajny), zbiór odwrotnie uporządkowany (uporządkowany malejąco) i zbiór uporządkowany (uporządkowany rosnąco). Dla każdego rozmiaru n powtórzono pomiar 10-krotnie w celu wyeliminowania przypadkowych zaburzeń. Jest to szczególnie ważne dla danych losowych.
Rozdział 4 Kilka słów o użytych narzędziach Programy kompilowane były kompilatorem gcc z użyciem opcji -O2: $ gcc -v Using built-in specs. Target: i486-linux-gnu... gcc version 4.0.3 (Ubuntu 4.0.3-1ubuntu5) Platformą wykonania był procesor Intel Dothan (Pentium M) 1.6 GHz, komputer zaopatrzony był w 1 GB pamięci RAM. Sprawozdanie przygotowano przy użyciu narzędzia L A TEX, wykresy przygotowano za pomocą narzędzia gnuplot.
Rozdział 5 Wyniki Na początek porównano wyniki dla róznych algorytmów sortowania, dla tego samego typu danych wejściowych o małym rozmiarze. Następnie przeanalizowano jak zachowuje się każdy z algorytmów dla różnego rodzaju danych wejściowych, znów o małym rozmiarze. 25 "insertion_random.dat" "selection_random.dat" "bubble_random.dat" 20 15 10 5 0 0 5000 10000 15000 20000 Rysunek 5.1: Dane losowe, trzy rózne algorytmy sortowania
7 10 9 "insertion_ordered.dat" "selection_ordered.dat" "bubble_ordered.dat" 8 7 6 5 4 3 2 1 0 0 5000 10000 15000 20000 Rysunek 5.2: Dane posortowane, trzy rózne algorytmy sortowania 20 18 "insertion_back.dat" "selection_back.dat" "bubble_back.dat" 16 14 12 10 8 6 4 2 0 0 5000 10000 15000 20000 Rysunek 5.3: Dane posortowane malejąco (odwrotnie), trzy rózne algorytmy sortowania
8 25 "bubble_random.dat" "bubble_ordered.dat" "bubble_back.dat" 20 15 10 5 0 0 5000 10000 15000 20000 Rysunek 5.4: Algorytm BubbleSort, różne dane 6 "insertion_random.dat" "insertion_ordered.dat" "insertion_back.dat" 5 4 3 2 1 0 0 5000 10000 15000 20000 Rysunek 5.5: Algorytm InsertionSort, różne dane
9 6 "selection_random.dat" "selection_ordered.dat" "selection_back.dat" 5 4 3 2 1 0 0 5000 10000 15000 20000 Rysunek 5.6: Algorytm SelectionSort, różne dane
Rozdział 6 Podsumowanie i wnioski Z zamieszczonych powyżej wykresów wyraźnie widać, że: Najwolniejszym z wszystkich algorytmów jest BubbleSort, najszybszym InsertionSort, przy czym w miarę wzrostu rozmiaru danych róznica między SelectionSort a InsertionSort rośnie zdecydowanie wolniej niż między BubbleSort a pozostałymi dwoma algorytmami BubbleSort najszybciej sortuje dane już posortowane (choć i tak zajmuje to sporo czasu), najwolniej dane uporządkowane losowo InsertionSort najlepiej radzi sobie z danymi uporządkowanymi - czas działania w tym wypadku wynosi praktycznie zero, najgorzej (co może dziwić jeśli nie przeczytaliśmy uważnie opisu algorytmu) - dane posortowane odwrotnie. dla SelectionSorta sposób uporządkowania danych nie ma znaczenia I od razu można sformułować pewną ogólną obserwację: nie należy raczej używać BubbleSorta (przynajmniej nie w podanej wyżej postaci), należy za to stosować InsertionSort (chyba, że mamy do dyspozycji coś lepszego), SelectionSort będzie interesujący, gdy ważnym kryterium będzie brak wrażliwości na rozkład danych (np. systemy czasu rzeczywistego!). Powyższe obserwacje warto byłoby potwierdzić próbą dla większych n-ów, ale już nie starczyło mi czasu - postaram się to zrobić w przyszłym tygodniu.
Bibliografia [1] Bentley, J., Perełki oprogramowania, WNT, Warszawa, rok nieznany [2] Sedgewick, R., Algorytmy w C++, ReadMe, Warszawa, 1999