Wydajniejsze sortowania
kajt
08.07.2010
Widzę, że powstał temat na temat sortowania bąbelkowego. Jest to prosty, ale niestety wyjątkowo mało wydajny algorytm dlatego zaprezentuję tutaj alternatywne, o wiele szybsze metody. Algorytmy zostały umieszczone w osobnych funkcjach, do każdej z nich przekazywane są takie parametry jak ilość elementów oraz sposób ułożenia elementów sortowanych. Są tam też funkcje wypełniające tablice, wypisujące je na ekranie oraz mierzące czas (gdyż wszystko pochodzi z mojego programu do pomiaru wydajności tych algorytmów). Uwaga! nie jest to czysty program, są to funkcje gotowego programu z pozostałościami po jego elementach.
Dlaczego te algorytmy są lepsze? Oto wykres zależności czasu trwania obliczeń od ilości elementów:

Wykres zależności czasu trwania obliczeń od ilości elementów z pominięciem sortowania bąbelkowego:

Sortowanie szybkie:
Sortowanie przez scalanie:
Sortowanie przez kopcowanie:
Sortowanie Shell'a:
Funkcje pomocnicze:
Dlaczego te algorytmy są lepsze? Oto wykres zależności czasu trwania obliczeń od ilości elementów:
Wykres zależności czasu trwania obliczeń od ilości elementów z pominięciem sortowania bąbelkowego:
Sortowanie szybkie:
//------------------------------------------------------------------------------ //------------SORTOWANIE SZYBKIE ----------------------------------------------- //------------------------------------------------------------------------------ /** Wywoływana rekurencyjnie funkcja szybkiego sortowania tab - sortowana tablica index_pierwszego elementu :) index_ostatniego elementu :) */ void quick_alg(int index_pierwszego, int index_ostatniego, int* tab){ int i, i2, tmp, srodek; i = (index_pierwszego + index_ostatniego) / 2; srodek = tab[i]; tab[i] = tab[index_ostatniego]; for( i2 = index_pierwszego, i = index_pierwszego ; i < index_ostatniego; i++){ if( tab[i] < srodek){ tmp = tab[i]; tab[i] = tab[i2]; tab[i2] = tmp; i2++; } } tab[index_ostatniego] = tab[i2]; tab[i2] = srodek; if( (i2 + 1) < index_ostatniego){ quick_alg(i2 + 1, index_ostatniego, tab); //rekurencyjne wykonanie quicksorta dla prawej partycji } if( index_pierwszego < (i2 - 1)){ quick_alg(index_pierwszego, (i2 - 1), tab); //rekurencyjne wykonanie quicksorta dla lewej partycji } } /** Funkcja zwraca wartość double, jest to czas wykonania operacji Argument n jest ilością elementów w tablicy Argument przyp jest przypadkiem ulozenia elementow 0-posortowane 1-losowo 2-posortowane wspak */ double quick(int n, short int przyp){ int *tab; // tablica na której będzie wykonywany algorytm clock_t time_przed; // pomiar czasu prz sortowaniem clock_t time_po; // pomiar czasu po sortowaniu double time_roznica; // różnica czasu przed i po sortowaniu zwracana przez funkcję difftime tab = new int [n]; wypelnij(tab, przyp, n, 0); // wypełnianie tablicy wypisz(tab, n); // ewentualne wyswietlanie elementow tablicy dla sprawdzenia poprawnosci sortowania time_przed = pobierz_czas(); // pomiar czasu przed sortowaniem quick_alg(0,n-1, tab); // quicksort time_po = pobierz_czas(); // pomiar czasu po sortowaniu wypisz(tab, n); // ewentualne wyswietlanie elementow tablicy dla sprawdzenia poprawnosci sortowania delete [] tab; time_roznica = difft(time_po, time_przed); cout << time_roznica; return time_roznica; }
Sortowanie przez scalanie:
//------------------------------------------------------------------------------ //------------SORTOWANIE PRZEZ SCALANIE ---------------------------------------- //------------------------------------------------------------------------------ /** Wywoływana rekurencyjnie funkcja sortowania przez scalanie tab - sortowana tablica pom - tablica pomocnicza index_pierwszego elementu :) index_ostatniego elementu :) */ void scalanie_alg(int index_pierwszego, int index_ostatniego, int* tab, int* pom){ int podzial; int j,k,i; podzial = (1 + index_pierwszego + index_ostatniego) / 2; // przygotowanie podzialu zbioru na 2 czesci if( index_ostatniego - podzial > 0){ // sortowanie rekurencyjne podzbioru drugiego scalanie_alg(podzial, index_ostatniego, tab, pom); } if( podzial - index_pierwszego > 1){ // sortowanie rekurencyjne podzbioru pierwszego scalanie_alg(index_pierwszego, podzial - 1, tab, pom); } j = index_pierwszego; k = podzial; for( i = index_pierwszego; i <= index_ostatniego; i++){ // scalanie if(((index_ostatniego >= k) && (tab[k] < tab[j])) || ( j == podzial)){ // jezeli index pierwszego elementu z pierwszego zbioru jest rowny podzial tzn ze jestesmy pom[i] = tab[k]; // na najnizszym poziomie rekurencji k++; }else{ pom[i] = tab[j]; j++; } } for( i = index_pierwszego; i <= index_ostatniego; i++){ // przepisywanie z tablicy pomocniczej do tab juz po scaleniu tab[i] = pom[i]; } } /** Funkcja zwraca wartość double, jest to czas wykonania operacji Argument n jest ilością elementów w tablicy Argument przyp jest przypadkiem ulozenia elementow 0-posortowane 1-losowo 2-posortowane wspak */ double scalanie(int n, short int przyp){ int *tab; // tablica na której będzie wykonywany algorytm clock_t time_przed; // pomiar czasu prz sortowaniem clock_t time_po; // pomiar czasu po sortowaniu double time_roznica; // różnica czasu przed i po sortowaniu zwracana przez funkcję difftime int *pom; // tablica pomocnicza tab = new int [n]; pom = new int [n]; wypelnij(tab, przyp, n, 0); // wypełnianie tablicy wypisz(tab, n); // ewentualne wyswietlanie elementow tablicy dla sprawdzenia poprawnosci sortowania time_przed = pobierz_czas(); // pomiar czasu przed sortowaniem scalanie_alg(0,n-1, tab, pom); time_po = pobierz_czas(); // pomiar czasu po sortowaniu wypisz(tab, n); // ewentualne wyswietlanie elementow tablicy dla sprawdzenia poprawnosci sortowania delete [] tab; time_roznica = difft(time_po, time_przed); cout << time_roznica; return time_roznica; }
Sortowanie przez kopcowanie:
//------------------------------------------------------------------------------ //------------SORTOWANIE PRZEZ KOPCOWANIE -------------------------------------- //------------------------------------------------------------------------------ /** Funkcja zwraca wartość double, jest to czas wykonania operacji Argument n jest ilością elementów w tablicy Argument przyp jest przypadkiem ulozenia elementow 0-posortowane 1-losowo 2-posortowane wspak */ double kopcowanie(int n, short int przyp){ int *tab; // tablica na której będzie wykonywany algorytm clock_t time_przed; // pomiar czasu prz sortowaniem clock_t time_po; // pomiar czasu po sortowaniu double time_roznica; // różnica czasu przed i po sortowaniu zwracana przez funkcję difftime int a,b,tmp2,tmp; tab = new int [n]; wypelnij(tab, przyp, n, 0); // wypełnianie tablicy wypisz(tab, n); // ewentualne wyswietlanie elementow tablicy dla sprawdzenia poprawnosci sortowania time_przed = pobierz_czas(); // pomiar czasu przed sortowaniem // Kopcowanie for( int i=2; i<=(n-1);i++){ a=i; tmp=tab[i]; b=a/2; while( (tmp>tab[b]) && (b>0)){ tab[a]=tab[b]; a=b; b=a/2; } tab[a]=tmp; } wypisz(tab, n); // ewentualne wyswietlanie elementow tablicy dla sprawdzenia poprawnosci sortowania //Odkopcowanie for( int i = (n-1); i>1; i--){ tmp2 = tab[1]; tab[1] = tab[i]; tab[i] = tmp2; a=1; b=2; while( b<i){ if( (b+1<i) &&(tab[b]<tab[b+1]) ){ tmp = 1 + b; } else{ tmp = b; } if( !( tab[tmp] > tab[a] ) ){ break; } tmp2 = tab[a]; tab[a] = tab[tmp]; tab[tmp] = tmp2; a=tmp; b=2*a; } } time_po = pobierz_czas(); // pomiar czasu po sortowaniu wypisz(tab, n); // ewentualne wyswietlanie elementow tablicy dla sprawdzenia poprawnosci sortowania delete [] tab; time_roznica = difft(time_po, time_przed); cout << time_roznica; return time_roznica; }
Sortowanie Shell'a:
//------------------------------------------------------------------------------ //------------SORTOWANIE SHELL ------------------------------------------------- //------------------------------------------------------------------------------ /** Funkcja zwraca wartość double, jest to czas wykonania operacji Argument n jest ilością elementów w tablicy Argument przyp jest przypadkiem ulozenia elementow 0-posortowane 1-losowo 2-posortowane wspak */ double shell(int n, short int przyp, int param){ int *tab; // tablica na której będzie wykonywany algorytm clock_t time_przed; // pomiar czasu prz sortowaniem clock_t time_po; // pomiar czasu po sortowaniu double time_roznica; // różnica czasu przed i po sortowaniu zwracana przez funkcję difftime int h,i,licz,a; // h -odległość tab = new int [n]; wypelnij(tab, przyp, n, 0); // wypełnianie tablicy wypisz(tab, n); // ewentualne wyswietlanie elementow tablicy dla sprawdzenia poprawnosci sortowania time_przed = pobierz_czas(); // pomiar czasu przed sortowaniem h = 1; //ustawianie parametru h while( h < n){ h=1 + param*h; } h = h/9; if(!h) h++; // istotne jedynie dla bardzo małych tablic while( h){ for( licz = n - 1 - h; !(licz < 0); licz--){ i = licz + h; a = tab[licz]; for( ;(n > i) && (tab[i] < a);){ //sortowanie przez wstawianie tab[i - h] = tab[i]; i += h; } tab[i - h] = a; } h = h/3; } time_po = pobierz_czas(); // pomiar czasu po sortowaniu wypisz(tab, n); // ewentualne wyswietlanie elementow tablicy dla sprawdzenia poprawnosci sortowania delete [] tab; time_roznica = difft(time_po, time_przed); cout << time_roznica; return time_roznica; }
Funkcje pomocnicze:
/* Pobiera czas metoda zalezna od globalnej flagi */ time_t pobierz_czas(){ if( fczas==1){ return clock (); } else{ return time(NULL); } } /* Oblicza czas wykonywania sie algorytmu dla metod clock() i time() */ double difft(time_t time_po, time_t time_przed){ if( fczas==1){ return (difftime(time_po, time_przed) / CLOCKS_PER_SEC); } else{ return difftime(time_po, time_przed); } } //--------------------------------------- //------------WYPELNIANIE TABLICY-------- //--------------------------------------- void wypelnij(int* tab,short int przyp, int n, int zakres){ switch(przyp){ // posortowane case 0: for( int i=0; i<n;i++){ if(zakres!=0){ tab[i]= i%zakres; } else{ tab[i]= i; } } break; // losowo case 1: for( int i=0; i<n;i++){ if(zakres!=0){ tab[i]= rand()%zakres; } else{ tab[i]= rand(); } } break; // wspak case 2: for( int i=0; i<n;i++){ if(zakres!=0){ tab[i]= (n-i)%zakres; } else{ tab[i]= (n-i); } } break; } } //---------------------------------------------------- //------------WYPISYWANIE POSORTOWANEJ TABLICY-------- //---------------------------------------------------- /** Funkcja wypisujaca elementy posortowanej tablicy, spam do testowania poprawnosci sortowania n -liczba elementow tab - nasza tablica */ void wypisz(int* tab, int n){ if( wyswietl==1){ cout << endl << endl <<endl; for( int i=0;i<n;i++){ cout<<tab[i]; if( (i%8)==0){ cout << "\n"; } else{ cout << "\t"; } } cout << "czas: "; } }
G[o]Q
08.07.2010
poprostu sortowanie babelkowe jest metoda o slabej zlozonosci obliczeniowej.
Fajne porownanie
Fajne porownanie

kajt
08.07.2010
O(n^2) i O(n log(n)) to spora różnica
choć oczywiście przy małej ilości elementów niezauważalna. Kiedyś jednak zdarzyło mi się sortować większe tablice, w większości przypadków nie ma to dużego znaczenia, ale czasami kolosalne.

JSokol
12.10.2010
Kajt, na wykresach masz literówkę: "sella" 
Ale prof Janiak pewnie i tak nie zauważył

Ale prof Janiak pewnie i tak nie zauważył

MaDaFaKa
29.11.2010
Dobrze przedstawiłeś poszczególne rodzaje sortowań na wykresie, lecz strasznie utrudniasz sobie życie tymi kodami C++'owymi. Dobrym wyjaśnieniem tego będzie kod od algorytmu QiuckSort, który u Ciebie zajmuje około 100 linijek i jest strasznie chaotyczny. Są takie biblioteki jak <windows.h>, które zdecydowanie ułatwiają życie programistom. Dla porównania pokaże Twój i mój kod.
/////////////////TWÓJ/////////////////
/////////////////MÓJ/////////////////
Choć obydwa są tak samo wydajne, to mój program jest o wiele mniej chaotyczny i oszczędza sporo czasu w porównaniu do Twojego.
Tyle z mojej strony.
Użytkownik MaDaFaKa edytował ten post 29.11.2010 21:00
/////////////////TWÓJ/////////////////
//------------------------------------------------------------------------------
//------------SORTOWANIE SZYBKIE -----------------------------------------------
//------------------------------------------------------------------------------
/**
Wywoływana rekurencyjnie funkcja szybkiego sortowania
tab - sortowana tablica
index_pierwszego elementu
index_ostatniego elementu
*/
void quick_alg(int index_pierwszego, int index_ostatniego, int* tab){
int i, i2, tmp, srodek;
i = (index_pierwszego + index_ostatniego) / 2;
srodek = tab[i];
tab[i] = tab[index_ostatniego];
for( i2 = index_pierwszego, i = index_pierwszego ; i < index_ostatniego; i++){
if( tab[i] < srodek){
tmp = tab[i];
tab[i] = tab[i2];
tab[i2] = tmp;
i2++;
}
}
tab[index_ostatniego] = tab[i2];
tab[i2] = srodek;
if( (i2 + 1) < index_ostatniego){
quick_alg(i2 + 1, index_ostatniego, tab); //rekurencyjne wykonanie quicksorta dla prawej partycji
}
if( index_pierwszego < (i2 - 1)){
quick_alg(index_pierwszego, (i2 - 1), tab); //rekurencyjne wykonanie quicksorta dla lewej partycji
}
}
/**
Funkcja zwraca wartość double, jest to czas wykonania operacji
Argument n jest ilością elementów w tablicy
Argument przyp jest przypadkiem ulozenia elementow 0-posortowane 1-losowo 2-posortowane wspak
*/
double quick(int n, short int przyp){
int *tab; // tablica na której będzie wykonywany algorytm
clock_t time_przed; // pomiar czasu prz sortowaniem
clock_t time_po; // pomiar czasu po sortowaniu
double time_roznica; // różnica czasu przed i po sortowaniu zwracana przez funkcję difftime
tab = new int [n];
wypelnij(tab, przyp, n, 0); // wypełnianie tablicy
wypisz(tab, n); // ewentualne wyswietlanie elementow tablicy dla sprawdzenia poprawnosci sortowania
time_przed = pobierz_czas(); // pomiar czasu przed sortowaniem
quick_alg(0,n-1, tab); // quicksort
time_po = pobierz_czas(); // pomiar czasu po sortowaniu
wypisz(tab, n); // ewentualne wyswietlanie elementow tablicy dla sprawdzenia poprawnosci sortowania
delete [] tab;
time_roznica = difft(time_po, time_przed);
cout << time_roznica;
return time_roznica;
}
/////////////////MÓJ/////////////////
#include <iostream>
#include <conio.h>
#include <time.h>
#include <windows.h>
using namespace std;
void qsort(int *tab, int left, int right)
{
if (left < right)
{
int m=left;
for (int i=left;i<=right;i++)
if (tab[i]<tab[left])
swap(tab[++m],tab[i]);
swap(tab[left],tab[m]);
qsort(tab,left,m-1);
qsort(tab,m+1,right);
}
}
int main()
{
int n;
cout<<"\nIle liczb chcesz posortowac?\n\n";
cin>>n;
int tabl[n];
float start = GetTickCount();
cout <<endl << "Wlaczam pomiar czasu dzialania programu!" << endl;
for (int i=0;i<n;i++)
{
cout<<"\nPodaj " << i+1 <<" liczbe: ";
cin >> tabl[i];
}
cout << "\n\n"; qsort(tabl,0,n-1);
for(int i=0;i<n;i++)
{
cout<< tabl[i]<<" ";
}
float stop = GetTickCount();
cout <<endl <<endl << "Koncze pomiar czasu!" << endl << endl;
cout << "Czas dzialania programu to: " << (stop - start) / 100 <<" s" << endl;
}
Choć obydwa są tak samo wydajne, to mój program jest o wiele mniej chaotyczny i oszczędza sporo czasu w porównaniu do Twojego.
Tyle z mojej strony.
Użytkownik MaDaFaKa edytował ten post 29.11.2010 21:00
kajt
19.12.2010
czyżby nie można było usunąć swojego postu?
Użytkownik kajt edytował ten post 19.12.2010 19:30
Użytkownik kajt edytował ten post 19.12.2010 19:30
-PainKiller-
19.12.2010
bezużyteczne kody podajecie, jak chcecie by ktoś z tego mógł skorzystać to napisz klase qsort