Triedenie poľa smerníkov na komplexné čísla (riešené príklady)

Z Kiwiki
Verzia z 22:28, 16. august 2010, ktorú vytvoril Juraj (diskusia | príspevky)
(rozdiel) ← Staršia verzia | Aktuálna úprava (rozdiel) | Novšia verzia → (rozdiel)
Skočit na navigaci Skočit na vyhledávání
Imbox draft.png
Toto je projekt, na ktorom sa ešte stále pracuje!!

Aj keď sú v tomto dokumente použiteľné informácie, ešte nie je dokončený. Svoje návrhy môžete vyjadriť v diskusii o tejto stránke.

Algoritmy a programovanie - zbierka úloh


Štruktúry

Rekurzia

Dynamická alokácia pamäti

Vyhľadávanie

Triedenie

>Triedenie poľa komplexných čísel
>Triedenie poľa smerníkov na CPLX
>Triedenie poľa štruktúr
>Triedenie poľa smerníkov

Lineárny zoznam

Binárny strom

Numerické algoritmy

Zadanie

Vytvorte program, ktorý usporiada pole komplexných čísel. Úlohu riešte pomocou vstavanej funkcie jazyka C qsort a algoritmu Quick sort. Úlohu riešte pomocou jednorozmerného poľa smerníkov na komplexné číslo. Porovnajte dosiahnuté rýchlosť výpočtu tohoto riešenia a predchádzajúceho príkladu riešeného pomocou poľa komplexných čísel.

Analýza riešenia

V analýze problému úlohy sa sústredíme len na to, čo je v riešení tejto úlohy nové oproti riešeniu predchádzajúcej úlohy Triedenie poľa komplexných čísel. V našej úlohe pracujeme s dynamickými poliami ukazovateľov na dátový typ CPLX, čiže v konečnom dôsledku s ukazovateľmi na štruktúrové premenné typu CPLX. Z toho dôvodu potrebujeme upraviť väčšinu funkcií z riešenia predchádzajúcej úlohy Triedenie poľa komplexných čísel (riešené príklady).

Vstupné a výstupné údaje

V programe načítajte číslo n a následne n komplexných čísel. Pre uloženie týchto čísel vytvorte také veľké pole smerníkov na komplexné číslo, aké je potrebné.

Triedenie pomocou funkcie QuickSort

V našej triediacej funkcii QuickSort z minulého príkladu musíme použiť upravenú funkciu porovnaj . Jej úprava spočíva v zmene typu jej argumentov na typ ukazovateľ na štruktúrovú premennú typu CPLX, pretože vo funkcii main chceme vytvoriť a používať pole ukazovateľov na dátový typ CPLX.

Definícia funkcie porovnaj

Definícia funkcie porovnaj je teraz nasledovná

int porovnaj(CPLX *a, CPLX *b)
{
   if(abs(a)>abs(b))
      return 1;
   else        
      return 0;
}

Parametre funkcie

a, b – ukazovatele na dátový typ CPLX, čiže na komplexné čísla, ktoré budeme porovnávať.

Návratová hodnota

  • 1 - ak platí, že

hodnota komplex. čísla, na ktoré ukazuje ukazovateľ a > hodnota komplex. čísla, na ktoré ukazuje ukazovateľ b

  • 0 – v ostatných prípadoch

Funkcia abs

Vo funkcii porovnaj voláme funkciu abs, ktorá očakáva argument dátového typu CPLX *, preto musíme nasledovne upraviť aj definíciu tejto funkcie

double abs(CPLX *x)
{
   return sqrt( (x->re * x->re) + (x->im * x->im) );
}

Parametre funkcie

x – ukazovateľ na dátový typ CPLX, čiže na komplexné číslo, ktorého modul (veľkosť) funkcia vypočíta.

Návratová hodnota

modul, čiže veľkosť komplexného čísla, na ktoré ukazuje ukazovateľ x

Funkcia QuickSort

Použitie porovnávacej funkcie porovnaj v upravenej funkcii QuickSort vidíme v jej definícii

 1 void QuickSort(CPLX **data, int lavy, int pravy) 
 2 {
 3    if(lavy<pravy)
 4    {   
 5       int i = lavy, j = pravy;
 6       CPLX *p = data[ (lavy + pravy) / 2 ];
 7       do 
 8       {
 9          while ( porovnaj(p, data[i]) > 0 ) i++;
10          while ( porovnaj(data[j], p) > 0 ) j--;
11          if (i <= j) 
12          {
13             CPLX *tmp = data[i]; 
14             data[i] = data[j];
15             data[j] = tmp;	
16             i++;
17             j--;
18          }
19       }while (i <= j);
20       QuickSort(data, lavy, j);
21       QuickSort(data, i, pravy);
22    }
23 }

Parametre funkcie

  • data – ukazovateľ na ukazovateľ na dátový typ CPLX, čiže v našom prípade ukazovateľ na pole ukazovateľov na dátový typ CPLX, pričom prvky poľa (komplexné čísla), na ktoré ukazujú tieto ukazovatele funkcia QuickSort usporiadava
  • lavy – indexová premenná krajného ľavého prvku triedeného poľa
  • pravy – indexová premenná krajného pravého prvku triedeného poľa

Návratová hodnota

je typu void – funkcia nevráti žiadnu návratovú hodnotu

Funkcia porovnaj

Taktiež sme museli upraviť funkciu skontroluj, ktorá kontroluje správne usporiadanie prvkov poľa, na ktoré ukazujú ukazovatele typu CPLX *, čiže usporiadanie štruktúrových premenných typu CPLX, vzostupne podľa ich veľkosti. Upravená definícia tejto funkcie je nasledovná

1 int skontroluj(CPLX *data[],int n)
2 {
3    for(int i=1;i<n;i++)
4       if(porovnaj(data[i-1],data[i])>0)
5          return 0;
6    return 1;
7 }

Funkcia overuje či prvok poľa komplexných čísiel, na ktorý ukazuje ukazovateľ data[i] je väčší, ako prvok tohto poľa, na ktorý ukazuje ukazovateľ data[i-1]. Pri prvej dvojici prvkov neusporiadanej podľa tohto pravidla funkcia vráti 0, ak sú všetky prvky tohto poľa usporiadané vzostupne, funkcia vráti 1.

Parametre funkcie

  • data – ukazovateľ na pole ukazovateľov na dátový typ CPLX. Ukazovatele v tomto poli ukazujú na dátový typ CPLX, teda na štruktúrové premenné tohto typu, v ktorých sú uložené komplexné čísla
  • n – počet prvkov poľa ukazovateľov na dátový typ CPLX

Návratová hodnota

  • 1 - prvky poľa komplexných čísiel, na ktoré ukazujú ukazovatele v poli ukazovateľov data sú usporiadané vzostupne
  • 0 - prvky poľa komplexných čísiel, na ktoré ukazujú ukazovatele v poli ukazovateľov data nie sú usporiadané vzostupne

Triedenie pomocou knižničnej funkcie qsort

Jazyk C obsahuje vstavanú funkciu qsort, ktorá usporiadava pole prvkov pomocou algoritmu Quick sort.

Volanie funkcie qsort vo funkcii main nášho príkladu vyzerá nasledovne:

   qsort((void *)ccisla2ptp, n, sizeof(ccisla2ptp[0]), porovnajQptp);

V tomto volaní je dôležitá porovnávacia funkcia porovnajQptp, ktorej hlavička a iné vlastnosti sú určené, avšak jej telo sme si museli vytvoriť sami

int porovnajQptp(const void *a, const void *b)
{
   if(abs(*(CPLX **)a) > abs(*(CPLX **)b))
      return 1;
   if(abs(*(CPLX **)a) < abs(*(CPLX **)b))
      return -1;
   return 0;
}

V hlavičke funkcie predpísané typy parametrov sme si museli v tele funkcie qsort vo volaniach funkcie abs pretypovať na typy korektné pre túto funkciu. Najskôr pretypujeme ukazovatele const void * na ukazovateľ CPLX ** a potom tieto ukazovatele dereferencujeme na ukazovatele CPLX *, pretože takýto typ argumentu požaduje funkcia abs. Okrem iného je to aj typ prvku poľa ukazovateľov na CPLX. Postupnosť týchto pretypovaní je nasledovná:

   const void *a 	//’a‘ je ukazovateľ typu ‘const void *‘
   (CPLX **)a 	//pretypujeme ho na ukazovateľ typu ‘CPLX **‘
   *(CPLX **)a 	//a teraz ho dereferencujeme na ukazovateľ typu ‘CPLX *‘, pretože ukazovateľ takého 
 		//typu potrebuje v argumente funkcia ‘abs‘

Komplexné čísla nášmu programu nebude zadávať používateľ, ale ich vygeneruje zavolaná funkcia generujCPLX, ktorej definícia je nasledovná

void generujCPLX(CPLX *c)
{   
   c->re = 1+ ( rand()%MAX_CISLO_1 );
   c->im = 1+ ( rand()%MAX_CISLO_2 );
}

Parametre funkcie

c – ukazovateľ na dátový typ CPLX, čiže na štruktúrovú premennú typu CPLX (komplexné číslo), ktorej vnútorné premenné napĺňa funkcia vygenerovanými hodnotami.

Návratová hodnota

je typu void – funkcia nevráti žiadnu návratovú hodnotu

Príklad vstupu a výstupu programu

Vzorový vstup

velkost triedeneho pola: 100000

Vzorový výstup

QuickSort: trvanie: 0.01 s
qsort: trvanie: 8 s

Zdrojový kód riešenia

Úlohou programu je porovnať rýchlosť oboch implementovaných algoritmov triedenia. Aby mali oba algoritmy rovnaké počiatočné podmienky, vytvoríme si dve zhodné polia komplexných čísel (ccisla1ptp, ccisla2ptp). Potom bude každý algoritmus triediť rovnaké vstupné dáta.

  1 #include <iostream>
  2 #include <math.h>
  3 #include <time.h>
  4 
  5 using namespace std;
  6 struct CPLX{ double re, im; };
  7 
  8 //Funkcia ocakava v 1 argumente ukazovatel na na strukturovu premennu typu 'CPLX'.
  9 //Funkcia vygeneruje komplexne cislo pomocou generatora pseudonahodnych cisiel 'rand'.
 10 //nahodne cisla su generovane v rozsahu 0..RAND_MAX co je 32767
 11 void generujCPLX(CPLX *c)
 12 {   
 13    c->re = 1+ (rand());
 14    c->im = 1+ (rand());
 15 }
 16 
 17 //Funkcia ocakava v 1. argumente ukazovatel na strukturovu premennu typu 'CPLX'.
 18 //Funkcia vypocita modul komplexneho cisla (cize jeho velkost).
 19 double abs(CPLX *x)
 20 {
 21    return sqrt( (x->re * x->re) + (x->im * x->im) );
 22 }
 23 
 24 //Funkcia ocakava 2 ukazovatele na strukturove premenne typu 'CPLX' ako argumenty.
 25 //Funkcia podla velkosti vzajomne porovna strukturove premenne typu 'CPLX', na ktore ukazuju 
 26 //ukazovatele 'a' a 'b'.
 27 int porovnaj(CPLX *a, CPLX *b)
 28 {
 29    if(abs(a)>abs(b))
 30       return 1;
 31    else        
 32       return 0;
 33 }
 34 
 35 //Funkcia triedi pole strukturovych premennych algoritmom Quick sort.
 36 //Je jedno ci je jej prvym argumentom 'CPLX **data' alebo 'CPLX *data[]', pretoze oba syntakticky
 37 //v nasom pripade predstavuju ukazovatel na pole ukazovatelov na strukturove premenne typu 'CPLX'. 
 38 //Funkcia s oboma argumentami funguje spravne.
 39 void QuickSort(CPLX **data, int lavy, int pravy) 
 40 {
 41    if(lavy<pravy)
 42    {   
 43       int i = lavy, j = pravy;
 44       CPLX *p = data[ (lavy + pravy) / 2 ];
 45       do 
 46       {
 47          while ( porovnaj(p, data[i])>0 ) i++;
 48          while ( porovnaj(data[j], p)>0 ) j--;
 49          if (i <= j) 
 50          {    
 51             CPLX *tmp = data[i]; 
 52             data[i] = data[j];
 53             data[j] = tmp;	
 54             i++; j--;
 55          }
 56       }while (i <= j);
 57       QuickSort(data, lavy, j);
 58       QuickSort(data, i, pravy);
 59    }
 60 }
 61 
 62 //Funkcia ocakava v 1 argumente ukazovatel na pole ukazovatelov na strukturove premenne
 63 //typu 'CPLX'. V tele potom pracuje s prvkami 'data[i]'(cize s ukazovatelmi) tohto pola ukazovatelov
 64 //na strukturove premenne typu 'CPLX'.
 65 //Funkcia skontroluje spravne usporiadanie prvkov pola,
 66 //cize usporiadanie strukturovych premennych typu 'CPLX' vzostupne podla ich velkosti. 
 67    
 68 //Je jedno ci je jej prvym argumentom 'CPLX **data' alebo 'CPLX *data[]', pretoze oba syntakticky
 69 //v nasom pripade predstavuju ukazovatel na pole ukazovatelov na strukturove premenne typu 'CPLX'. 
 70 //Funkcia s oboma argumentami funguje spravne.
 71 int skontroluj(CPLX *data[],int n)
 72 {
 73 	for(int i=1;i<n;i++)
 74 		if(porovnaj(data[i-1],data[i])>0)
 75 			return 0;
 76 	return 1;
 77 }
 78 
 79 //porovnavacia funkcia do kniznicnej funkcie 'qsort'
 80 int porovnajQptp(const void *a, const void *b)
 81 {
 82    if(abs(*(CPLX **)a) > abs(*(CPLX **)b))
 83       return 1;
 84    if(abs(*(CPLX **)a) < abs(*(CPLX **)b))
 85       return -1;
 86    return 0;
 87 }
 88 
 89 int main()
 90 {
 91    int n, i;        
 92    cout<<"velkost triedeneho pola: ";
 93    cin>>n;
 94 
 95    //'ccisla1ptp' je ukazovatel na ukazovatel na CPLX
 96    CPLX **ccisla1ptp=new CPLX*[n]; //alokovanie miesta pre n-prvkove POLE UKAZOVATELOV na 
 97                                    //CPLX, na ktore ukazuje ukazovatel 'ccisla1ptp'
 98    for(i=0; i<n; i++)
 99       ccisla1ptp[i]=new CPLX; //inicializacia ukazovatela 'ccisla1ptp[i]', vkladame do neho ukazovatel na 
100                               //pamatove miesto pre premennu typu CPLX
101     
102    CPLX **ccisla2ptp=new CPLX*[n];
103    for(i=0; i<n; i++)
104       ccisla2ptp[i]=new CPLX;
105  
106    //nastavenie startovacieho cisla generatora pseudonahodnych cisiel 'rand', aby toto cislo bolo 
107    //vzdy ine, po novom zavolani funkcie 'main'. Takto tento generator vygeneruje vzdy ine pseudonahodne cisla
108    srand( (unsigned)time( NULL ) );
109 
110    //plnenie obsahu prvkov pola 'ccisla1ptp' a 'ccisla2ptp' vygenerovanymi komplexnymi cislami
111    for(i=0; i<n; i++)
112    {
113       generujCPLX(ccisla1ptp[i]);
114       ccisla2ptp[i]=ccisla1ptp[i];
115    }
116 
117    int k;
118    clock_t c1, c2;
119 
120    c1 = clock();
121    QuickSort(ccisla1ptp, 0, n-1);
122    c2 = clock();
123    k=skontroluj(ccisla1ptp, n);
124    if(k)
125 	cout<<"QuickSort: trvanie "<< (c2 - c1)/CLOCKS_PER_SEC << " s"<<endl;
126    else
127        cout<<"Pole nie je utriedene!";
128         
129    c1 = clock();
130    qsort((void *)ccisla2ptp, n, sizeof(ccisla2ptp[0]), porovnajQptp);
131    c2 = clock();
132    k=skontroluj(ccisla2ptp, n); 
133    if(k)
134 	cout<<"qsort: trvanie "<< (c2 - c1)/CLOCKS_PER_SEC << " s"<<endl;
135    else
136        cout<<"Pole nie je utriedene!";
137 
138    for(i=0;i<n;i++)
139    {
140       delete ccisla1ptp[i]; //mazeme pole ukazovatelov
141       delete ccisla2ptp[i]; 
142    }
143    delete[] ccisla1ptp;  //mazeme ukazovatel na toto pole
144    delete[] ccisla2ptp;
145    
146    return 0;
147 }

Záver

Z výsledkov merania času doby behu programu sme zistili že knižničná funkcia qsort je niekoľkokrát pomalšia ako nami implementovaný algoritmus QuickSort. Táto skutočnosť je zapríčinená univerzálnou štruktúrou funkcie qsort, v ktorej môžeme triediť pole ľubovoľného dátového typu. Pri jej použití sa musia prvky triedeného typu niekoľkokrát pretypovať z ukazateľa na konkrétny dátový typ (u nás CPLX) na ukazovateľ na void. Toto zaberá väčšinu času. Naša implementácia algoritmu QuickSort je priamo naprogramované pre usporiadania dátovej štruktúry CPLX a netreba žiadne ďalšie pretypovania, čím sa ušetrí výpočtový čas