• Tidak ada hasil yang ditemukan

SDMP Lectia dan 4 5

N/A
N/A
Protected

Academic year: 2018

Membagikan "SDMP Lectia dan 4 5"

Copied!
45
0
0

Teks penuh

(1)

3. Red 1980 450.00

4. Cyan 1975 800.00

5. Black 1981 500.00

6.

7. Green 1987 350.00

8. Gray 1968 900.00

9. White 1980 600.00

10. Yellow 1988 300.00

11.

12.

13. Magenta 1983 600.00

14.

15. Orange 1984 550.00

End of Table. Press any key ...

m=10, n=15, NCOMP=16, ALS=1.60, m/n=0.67, D(m/n)=2.00

Ana

liza rezultatelor rămâne ca exerciţiu.

Exerciţii.

1. Supraîncărcaţi operatorul de inserţie în clasa

hashing_table

.

2. Supraîncărcaţi operatorul de extragere în clasa

hashing_table

.

3. Găsiţi o funcţie de repartizare mai potrivită.

Tabele de repartizare cu

înlănţuirea externă

(repartizarea deschisă

,

înlănţuire

a

separată)

În metoda examinării

liniare

înregistrările

ce produc coliziuni

se includ în poziţiile

libere acel

uiaşi

vector de reflectare.

Însă pentru aceste înregistrări se poate crea un tabe

l aparte.

În tabelul adăugător

(2)

În tabele

de repartizare cu

înlănţuirea externă

lungimea medie de căutare pentru distribuirea

uniformă şi

aleatori

e a înregistrărilor se defineşte după for

mula:

 

Pentru a demonstra lucru cu tabele de

repartizare cu înlănţuirea externă, în primul rând să declarăm

clasa

hashing_linked_elem

ca clasa derivată de la clasa

usual_elem

şi dotată cu câmpul

next

,

pentru a crea lanţuri de legături.

//

hashing_linked_elem()

(3)

hashing_linked_elem(char* init_name, int init_year, double init_salary):

usual_elem(init_name, init_year, init_salary)

{

next = -1;

}

virtual void hashing_linked_show(const char* opening=NULL,

const char* ending=NULL)

{

if(!opening)

opening="";

if(!ending)

ending="\n";

printf("%s", opening);

usual_elem::show("", "");

if(!free())

printf(" [%4d]", next);

printf("%s", ending);

}

int hf(int n) // hashing function

{

return (name[0]-'A')%n;

}

int get_next()

{

return next;

}

int set_next(int new_next)

{

return next=new_next;

}

};

Exerciţii.

1. Supraîncărcaţi operatorul de inserţie în clasa

hashing_linked_elem

.

2. Supraîncărcaţi operatorul de extragere în clasa

hashing_linked_elem

.

(4)

//

// C l a s s "e x t e r n _ h a s h i n g _ t a b l e"

// m/n

template <class el> class extern_hashing_table: public SD

{

protected:

int n;

int m;

el *t;

el *v;

public:

extern_hashing_table<el>(char* file_name, int init_n=0): SD(file_name)

{

n=init_n;

if(n<=0)

n=countn();

t=new el[n];

v=new el[n];

m=0;

el tmp;

int repeated, position;

while(!feof(SD::pf))

if(tmp.fscanf_el(SD::pf)>0)

{

int i=tmp.hf(n);

if(t[i].free())

{

t[i]=tmp;

m++;

}

else

{

repeated=-1;

if( tmp==t[i] )

{

repeated=i;

t[i].show("", " !!!\n");

}

else

{

(5)

{

int j=0;

while(!v[j].free())

j++;

t[i].set_next(j);

v[j]=tmp;

m++;

}

else

{

i=t[i].get_next();

position=-1;

while((repeated==-1) && position==-1)

{

if( tmp==v[i] )

{

repeated=i;

v[i].show("", " !!!\n");

}

else

if(v[i].get_next()==-1)

{

position=i+1;

while(!v[position].free())

position++;

v[i].set_next(position);

v[position]=tmp;

m++;

}

else

i=v[i].get_next();

}

}

}

if ( repeated!=-1 )

{

char message[60];

char repeated_str[10];

message[0]='\0';

//strcat(message, "Key coincides with the key in the position: ");

//strcat(message, itoa(repeated+1, repeated_str, 10));

(6)

strcat(message, "Key coincides !!!\n");

error(message);

}

}

}

fclose(SD::pf), SD::pf=NULL;

}

virtual void show(const char* opening=NULL, const char* ending=NULL,

int nlinepp=20)

{

clrscr();

if(!opening)

opening="";

if(!ending)

ending="\n";

printf("%s", opening);

for(int i=0; i<n; i++)

{

if(i>0 && i%nlinepp==0)

{

printf("Press any key to continue ...\n");

getch();

clrscr();

printf("%s", opening);

}

printf("%4d. ", (i+1)); t[i].show();

}

printf("%s", ending);

printf("End of Table. Press any key ...\n");

getch();

}

virtual void primary_show(const char* opening=NULL, const char* ending=NULL,

int nlinepp=20)

{

clrscr();

if(!opening)

opening="";

if(!ending)

ending="\n";

(7)

for(int i=0; i<n; i++)

{

if(i>0 && i% nlinepp==0)

{

printf("%s", "Press any key to continue ...\n");

getch();

clrscr();

printf("%s", opening);

}

printf("%4d. ", (i+1)); t[i].hashing_linked_show();

}

printf("%s", ending);

printf("End of Table. Press any key ...\n");

getch();

}

virtual void secondary_show(const char* opening=NULL,

const char* ending=NULL, int nlinepp=20)

{

//clrscr();

if(!opening)

opening="";

if(!ending)

ending="\n";

printf("%s", opening);

for(int i=0; i<n; i++)

{

if(i>0 && i%nlinepp==0)

{

printf("%s", "Press any key to continue ...\n");

getch();

clrscr();

printf("%s", opening);

}

printf("%4d. ", (i+1)); v[i].hashing_linked_show();

}

printf("%s", ending);

printf("End of Table. Press any key ...\n");

getch();

}

(8)

{

int position=-1;

int i=e.hf(n);

if(!t[i].free())

if(SD::ncomp++, e==t[i])

position=i;

else

if((i=t[i].get_next())!=-1)

do

{

if(SD::ncomp++, e==v[i])

position=i;

else

i=v[i].get_next();

}

while((position==-1) && (i!=-1));

return position;

}

int get_n()

{

return n;

}

int get_m()

{

return m;

}

protected:

int countn()

{

return 200;

}

};

În funcţia

main()

să creăm pe baza fişierului

stud.txt

un tabel de repartizare cu înlănţuirea

externă

pentru

n=15

şi să demonstrăm căutarea elementelor.

void main()

{

clrscr();

(9)

ex_hashing_gr.primary_show("Primary table:\n","");

ex_hashing_gr.secondary_show("Secondary table:\n","");

char ch='n';

char surname[21];

while(ch!='y')

{

printf("Enter a name to search: ");

scanf("%s", surname);

hashing_linked_elem e(surname, 2000, 0.0);

ex_hashing_gr.reset_ncomp();

int pos=ex_hashing_gr.search(e);

if(pos<0)

{

printf("No table! ");

printf("The number of comparisons: %d\n", ex_hashing_gr.get_ncomp());

}

else

{

printf("There are in the position %d. ", pos+1);

printf("The number of comparisons: %d\n", ex_hashing_gr.get_ncomp());

}

printf("Done ? (y/n) ");

ch = getch();

printf("\n"); }

}

O variantă de

afişare poate arăta astfel:

Primary table:

1.

2. Blue 1981 500.00 [ 2]

3. Red 1980 450.00 [ 1]

4.

5.

6.

7. Green 1987 350.00 [ 0]

8. White 1980 600.00 [ -1]

9.

10. Yellow 1988 300.00 [ -1]

11.

12.

13. Magenta 1983 600.00 [ -1]

(10)

15. Orange 1984 550.00 [ -1]

End of Table. Press any key ...

Secondary table:

1. Gray 1968 900.00 [ -1]

2. Cyan 1975 800.00 [ -1]

3. Black 1981 500.00 [ -1]

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

End of Table. Press any key ...

Enter a name to search: White

There are in the position 8. The number of comparisons: 1

Done ? (y/n)

Enter a name to search: Green

There are in the position 7. The number of comparisons: 1

Done ? (y/n)

Enter a name to search: Black

There are in the position 3. The number of comparisons: 2

Done ? (y/n)

Enter a name to search: Purple

No table! The number of comparisons: 0

Done ? (y/n)

Pentru

n=10

afişarea va fi:

Primary table:

1.

2. Blue 1981 500.00 [ 4]

3. White 1980 600.00 [ 1]

4.

5. Orange 1984 550.00 [ 2]

6.

7. Green 1987 350.00 [ 0]

8. Red 1980 450.00 [ -1]

(11)

10.

End of Table. Press any key ...

Secondary table:

1. Gray 1968 900.00 [ -1]

2. Cyan 1975 800.00 [ 3]

3. Yellow 1988 300.00 [ -1]

4. Magenta 1983 600.00 [ -1]

5. Black 1981 500.00 [ -1]

6.

7.

8.

9.

10.

End of Table. Press any key ...

Enter a name to search: Gray

There are in the position 1. The number of comparisons: 2

Done ? (y/n)

Enter a name to search: Blue

There are in the position 2. The number of comparisons: 1

Done ? (y/n)

Enter a name to search: Magenta

There are in the position 4. The number of comparisons: 3

Done ? (y/n)

Enter a name to search: Purple

No table! The number of comparisons: 0

Done ? (y/n)

Pentru a calcula lungimea medie de c

ăutare mod

ifi

căm funcţia

main()

.

void main()

{

clrscr();

extern_hashing_table<hashing_linked_elem> ex_hashing_gr("stud.txt", 15);

ex_hashing_gr.primary_show("Primary table:\n","");

ex_hashing_gr.secondary_show("Secondary table:\n","");

hashing_linked_elem sample;

long NCOMP=0;

FILE* pf=fopen("Stud.txt", "rt");

while(!feof(pf))

if(sample.fscanf_el(pf)>0)

(12)

ex_hashing_gr.reset_ncomp();

if(ex_hashing_gr.search(sample)>=0)

NCOMP+=ex_hashing_gr.get_ncomp();

}

fclose(pf);

printf("m=%d, n=%d, NCOMP=%d, ALS=%.2lf", ex_hashing_gr.get_m(),

ex_hashing_gr.get_n(), NCOMP, (double)NCOMP/ex_hashing_gr.get_m());

printf(", D(m/n)=%.2lf\n",

1.+(ex_hashing_gr.get_m()-1.)/(2.*ex_hashing_gr.get_n()));

getch();

}

Pentru

n=15

rezultatul va fi:

Primary table:

1.

2. Blue 1981 500.00 [ 2]

3. Red 1980 450.00 [ 1]

4.

5.

6.

7. Green 1987 350.00 [ 0]

8. White 1980 600.00 [ -1]

9.

10. Yellow 1988 300.00 [ -1]

11.

12.

13. Magenta 1983 600.00 [ -1]

14.

15. Orange 1984 550.00 [ -1]

End of Table. Press any key ...

Secondary table:

1. Gray 1968 900.00 [ -1]

2. Cyan 1975 800.00 [ -1]

3. Black 1981 500.00 [ -1]

4.

5.

6.

7.

8.

9.

10.

(13)

12.

13.

14.

15.

End of Table. Press any key ...

m=10, n=15, NCOMP=13, ALS=1.30, D(m/n)=1.30

Iar pentru

n=10

rezultatul va fi:

Primary table:

1.

2. Blue 1981 500.00 [ 4]

3. White 1980 600.00 [ 1]

4.

5. Orange 1984 550.00 [ 2]

6.

7. Green 1987 350.00 [ 0]

8. Red 1980 450.00 [ -1]

9.

10.

End of Table. Press any key ...

Secondary table:

1. Gray 1968 900.00 [ -1]

2. Cyan 1975 800.00 [ 3]

3. Yellow 1988 300.00 [ -1]

4. Magenta 1983 600.00 [ -1]

5. Black 1981 500.00 [ -1]

6.

7.

8.

9.

10.

End of Table. Press any key ...

m=10, n=10, NCOMP=16, ALS=1.60, D(m/n)=1.45

Exerci

ţii.

(14)

Tabele de repartizare cu înlănţuirea internă

Încărcarea poziţiilor vectorului de

reflectare

constă din două etape:

prima etapă se aseamănă cu repartizarea cu înlănţuirea externă

;

a doua etapă se îndeplineşte după terminarea creării tabelului pri

mar

şi

celui secundar.

Ea constă

în mutarea lanţurilor din tabelul

secundar

r în poziţiile libere ale tabelului p

rimar.

Astfel tabelele din exemplul precedent vor fi transformate

în următorul tabel:

Prioritatea acestei metode în comparaţie cu precedenta –

economie de memorie, dar neajunsul

flexibilitatea mică şi algoritmul de încărcare a tabelului

este mai complicat.

Lungimea medie de căutare aici se defineşte după aceiaşi

formula:

metodă se foloseşte pentru tabele permanente, şi pentru tabele temporare,

care se

încărcă

la prima

etapă, dar

se folosesc la alta.

Pentru tabele constant

e înregistrări

le cel mai des folosite

se înscriu primele, atunci

accesările

lanţuril

or

interioare sunt rare, ce micşorează lungimea medie de căutare. Este caracter, că

la

folosirea lanţu

rilor interioare

toate poziţiile

ale vectorului de reflectare

pot fi încărcate (adică

m

=

n

),

(15)

Pentru a demonstra lucru cu tabele de

repartizare cu înlănţuirea

inte

rnă

să declarăm pe baza clasei

abstracte

SD

clasa generică

extern_hashing_table

, care na va da posibilitatea de a crea tabele de

repartizare cu

înlănţuirea

in

ternă

.

//

// C l a s s "i n t e r n _ h a s h i n g _ t a b l e"

// m/n

template <class el> class intern_hashing_table: public SD

{

protected:

int n;

int m;

el *t;

el *v;

public:

intern_hashing_table<el>(char* file_name, int init_n=0): SD(file_name)

{

n=init_n;

if(n<=0)

n=countn();

t=new el[n];

v=new el[n];

m=0;

el tmp;

int i, j;

int repeated, position;

while(!feof(SD::pf))

if(tmp.fscanf_el(SD::pf)>0)

{

i=tmp.hf(n);

if(t[i].free())

{

t[i]=tmp;

m++;

}

else

{

repeated=-1;

if( tmp==t[i] )

{

repeated=i;

(16)

}

else

{

if(t[i].get_next()==-1)

{

j=0;

while(!v[j].free())

j++;

t[i].set_next(j);

v[j]=tmp;

m++;

}

else

{

i=t[i].get_next();

position=-1;

while((repeated==-1) && position==-1)

{

if( tmp==v[i] )

{

repeated=i;

v[i].show("", " !!!\n");

}

else

if(v[i].get_next()==-1)

{

position=i+1;

while(!v[position].free())

position++;

v[i].set_next(position);

v[position]=tmp;

m++;

}

else

i=v[i].get_next();

}

}

}

if ( repeated!=-1 )

{

char message[60];

(17)

message[0]='\0';

//strcat(message, "Key coincides with the key in the position: ");

//strcat(message, itoa(repeated+1, repeated_str, 10));

//strcat(message, "!\n");

strcat(message, "Key coincides !!!\n");

error(message);

}

}

}

fclose(SD::pf), SD::pf=NULL;

el empty;

int k=0;

for(j=0; j<n; j++)

if(!v[j].free())

{

i=v[j].hf(n);

int j1=j;

do

{

while(!t[k].free())

k++;

t[i].set_next(k);

i=k;

t[i]=v[j1];

t[i].set_next(-1);

int jtmp=j1;

j1=v[j1].get_next();

v[jtmp]=empty;

}

while (j1!=-1);

}

delete v;

}

virtual void show(const char* opening=NULL, const char* ending=NULL,

int nlinepp=20)

{

clrscr();

(18)

opening="";

if(!ending)

ending="\n";

printf("%s", opening);

for(int i=0; i<n; i++)

{

if(i>0 && i% nlinepp==0)

{

printf("Press any key to continue ...\n");

getch();

clrscr();

printf("%s", opening);

}

printf("%4d. ", (i+1)); t[i].show();

}

printf("%s", ending);

printf("End of Table. Press any key ...\n");

getch();

}

virtual void primary_show(const char* opening=NULL, const char* ending=NULL,

int nlinepp=20)

{

clrscr();

if(!opening)

opening="";

if(!ending)

ending="\n";

printf("%s", opening);

for(int i=0; i<n; i++)

{

if(i>0 && i%nlinepp==0)

{

printf("%s", "Press any key to continue ...\n");

getch();

clrscr();

printf("%s", opening);

}

printf("%4d. ", (i+1)); t[i].hashing_linked_show();

}

printf("%s", ending);

(19)

getch();

}

int search(el e)

{

int position=-1;

int i=e.hf(n);

if(!t[i].free())

do

{

if(SD::ncomp++, e==t[i])

position=i;

else

i=t[i].get_next();

}

while((position==-1) && (i!=-1));

return position;

}

int get_n()

{

return n;

}

int get_m()

{

return m;

}

protected:

int countn()

{

return 200;

}

};

În funcţia

main()

să creăm pe baza fişierului

stud.txt

un tabel de repartizare cu înlănţuirea

internă pentru

n=15

şi să demonstrăm căutarea elementelor.

void main()

{

clrscr();

(20)

in_hashing_gr.primary_show("Primary table:\n","");

char ch='n';

char surname[21];

while(ch!='y')

{

printf("Enter a name to search: ");

scanf("%s", surname);

hashing_linked_elem e(surname, 2000, 0.0);

in_hashing_gr.reset_ncomp();

int pos=in_hashing_gr.search(e);

if(pos<0)

{

printf("No table! ");

printf("The number of comparisons: %d\n", in_hashing_gr.get_ncomp());

}

else

{

printf("There are in the position %d. ", pos+1);

printf("The number of comparisons: %d\n", in_hashing_gr.get_ncomp());

}

printf("Done ? (y/n) ");

ch = getch();

printf("\n");

}

}

O variantă de afişare poate arăta astfel:

Primary table:

1. Gray 1968 900.00 [ -1]

2. Blue 1981 500.00 [ 4]

3. Red 1980 450.00 [ 3]

4. Cyan 1975 800.00 [ -1]

5. Black 1981 500.00 [ -1]

6.

7. Green 1987 350.00 [ 0]

8. White 1980 600.00 [ -1]

9.

10. Yellow 1988 300.00 [ -1]

11.

12.

13. Magenta 1983 600.00 [ -1]

(21)

15. Orange 1984 550.00 [ -1]

End of Table. Press any key ...

Enter a name to search: White

There are in the position 8. The number of comparisons: 1

Done ? (y/n)

Enter a name to search: Green

There are in the position 7. The number of comparisons: 1

Done ? (y/n)

Enter a name to search: Black

There are in the position 5. The number of comparisons: 2

Done ? (y/n)

Enter a name to search: Purple

No table! The number of comparisons: 1

Done ? (y/n)

Pentru

n=10

afişarea va fi:

Primary table:

1. Gray 1968 900.00 [ -1]

2. Blue 1981 500.00 [ 9]

3. White 1980 600.00 [ 3]

4. Cyan 1975 800.00 [ 5]

5. Orange 1984 550.00 [ 8]

6. Magenta 1983 600.00 [ -1]

7. Green 1987 350.00 [ 0]

8. Red 1980 450.00 [ -1]

9. Yellow 1988 300.00 [ -1]

10. Black 1981 500.00 [ -1]

End of Table. Press any key ...

Enter a name to search: Gray

There are in the position 1. The number of comparisons: 2

Done ? (y/n)

Enter a name to search: Blue

There are in the position 2. The number of comparisons: 1

Done ? (y/n)

Enter a name to search: Magenta

There are in the position 6. The number of comparisons: 3

Done ? (y/n)

Enter a name to search: Purple

No table! The number of comparisons: 1

Done ? (y/n)

(22)

clrscr();

intern_hashing_table<hashing_linked_elem> in_hashing_gr("stud.txt", 15);

// intern_hashing_table<hashing_linked_elem> in_hashing_gr("stud.txt", 10);

in_hashing_gr.primary_show("Primary table:\n","");

hashing_linked_elem sample;

long NCOMP=0;

FILE* pf=fopen("Stud.txt", "rt");

while(!feof(pf))

if(sample.fscanf_el(pf)>0)

{

in_hashing_gr.reset_ncomp();

if(in_hashing_gr.search(sample)>=0)

NCOMP+=in_hashing_gr.get_ncomp();

}

fclose(pf);

printf("m=%d, n=%d, NCOMP=%d, ALS=%.2lf", in_hashing_gr.get_m(),

in_hashing_gr.get_n(), NCOMP, (double)NCOMP/in_hashing_gr.get_m());

printf(", D(m/n)=%.2lf\n",

1.+(in_hashing_gr.get_m()-1.)/(2.*in_hashing_gr.get_n()));

getch();

}

Pentru

n=15

afişarea va fi:

Primary table:

1. Gray 1968 900.00 [ -1]

2. Blue 1981 500.00 [ 4]

3. Red 1980 450.00 [ 3]

4. Cyan 1975 800.00 [ -1]

5. Black 1981 500.00 [ -1]

6.

7. Green 1987 350.00 [ 0]

8. White 1980 600.00 [ -1]

9.

10. Yellow 1988 300.00 [ -1]

11.

12.

13. Magenta 1983 600.00 [ -1]

14.

(23)

End of Table. Press any key ...

m=10, n=15, NCOMP=13, ALS=1.30, D(m/n)=1.30

Pentru

n=10

afişarea va fi:

Primary table:

1. Gray 1968 900.00 [ -1]

2. Blue 1981 500.00 [ 9]

3. White 1980 600.00 [ 3]

4. Cyan 1975 800.00 [ 5]

5. Orange 1984 550.00 [ 8]

6. Magenta 1983 600.00 [ -1]

7. Green 1987 350.00 [ 0]

8. Red 1980 450.00 [ -1]

9. Yellow 1988 300.00 [ -1]

10. Black 1981 500.00 [ -1]

End of Table. Press any key ...

m=10, n=10, NCOMP=16, ALS=1.60, D(m/n)=1.45

Tabelele de repartizare aleatori

e se încarcă

destul de

simplu, nu necesit ordonarea înregistrărilor şi

asigură o căutare rapidă. Deaceea aceste tabele deseori se folosesc în practică

.

Exerciţii.

1. Supraîncărcaţi operatorul de inserţie în clasa

intern_hashing_table

.

2. Supraîncărcaţi operatorul de extragere în clasa

intern_hashing_table

.

Fun

cţii de repartizare

Timpul

calculării funcţi

ei de repartizare

f

(

k

) intră în timpul mediu de căutare, d

eaceiea trebuie de

ţinut cont la alegerea algoritmului, realizând funcţia de

repartizare.

O funcţie bună de

repartizare

trebuie să asigure repartizarea uniformă a înregistrări

lor

pe poziţiile

vectorului de reflectare, fiind

că distribuirea neuniformă măreşte timpul medi

u

de căutare. Însă

dacă

calcularea

valorii funcţiei de

repartizare

necesită îndeplin

irea unui

număr mare

de

operaţii

, aceasta

poate distruge toată economia în timpul căutării. Deci, algoritmul calculării

funcţiei de

repartizare

nu

trebuie să fie complicat. Să privim câteva metode de

calculare

a funcţiei de

repartizare:

1.

Una din metodele simple se

bazează pe evidenţierea unei părţi din codul numeric al chei

i. De

exemplu, fie dimensiunea maximă aşteptată a tabelului de nume

simbolice

nu întrece 256.

Atunci funcţia de

repartizare

poate avea în calitate de valoare 8

biţi, fiindcă 256=2

8

. Se poate

pur şi simplu

de a

evidenţia primii 8 biţi din

codul binar al identificatorului sau de a lua careva 8

(24)

2.

Pentru asigurarea

distribuirii uniforme se foloseşte “sumare

a

” codul

ui identificatorului: prima

jumătate a codului se sumează cu a doua şi din rezultat se evidenţiază 8 biţi

. Se poate de

asemenea de

împărţi

t codul cheii

în

bucăţi câte 8 biţi

, de sumat

bucăţile şi

de pus suma pe

modulul 2

8

. Ultima modificare are careva probabilitate teoretică: la presupunerea a statisticei

independente

de sumare a bucăţilor se primeşte repartizarea aproape

de uniform

ă

.

3.

O

altă metodă de

calculare

a funcţiei de

repartizare este

împărţire

a. Pentru vectorul de reflectare

de lungimea n, cheia

se priveşte ca un număr întreg

şi se împarte la mărimea

n. Experimentele

arată, că restul de la împărţire este repartizat aproape uniform în intervalul

[0, n-1]

şi poate fi

folosit ca va

loarea funcţiei de

repartizare.

Verificarea experimental

ă

a metodelor descrise pentru tabelele

de repartizare cu examinarea lineară

a arătat că evidenţierea

simplă a

bucăţii

din codul identificatorului

măreşte lungimea medie de

căutare în comparaţie cu

cea

teoretică, definită după formula:

2

2

2

)

(

D

,

în 4

-5 sau

şi mai

multe ori. L

ungimea medie de căutare pentru metoda

de sumare

a bucăţilor după modulul 2

k

aproape de două ori mai mare ca teoretică

, dar pentru

împărţirea

, lungimea

medie de căuta

re practic

(25)

3. TEHNICI DE SORTARE

3.1.

Noţiuni generale

Sortarea este operaţia de aranjare a elementelor unui vector după valoarea

cheilor, pentru care este definită relaţia de ordine.

Tradiţional diferă sortarea internă de sortarea externă. Sortarea internă prelucrează datele păstrate în

memoria operativă a calculatorului, dar sortarea externă, operează cu datele care sunt păstrate în

fişier

e.

În cazul sortării interne se tinde la minimizarea numărului de comparaţii

şi permutări ale

elementelor.

În cazul sortării externe factorul hotărâtor este numărul de operaţii de intrare şi ieşire. În acest caz

numărul de comparaţii

trece pe planul doi, totuşi şi el se i

-

a în consideraţie.

Cazul sortării interne

Presupunem, că datele supuse sortării se păstrează în memoria operativă

într

-un vector

t

. Fiecare

element

t[i]

al acetui vector este obiect al clasei parametrizate

el

în care sunt supraîncărcaţi

operatorii de compara

ţie

. Deci, sunt admise expresii:

t[i]<t[j]

,

t[i]<=t[j]

, etc.

Presupunem, că funcţia

swap(i,j)

schimbă cu locurile elementele

t[i]

şi

t[j]

a vectorului.

Funcţia

cmp(i,j)

întoarce un număr întreg, care este egal cu

-

1, dacă

t[i]<t[j]

;

0, dacă

t[i]==t[j]

;

1, dacă

t[i]>t[j]

.

Definiţia 1

. Perechea de indici

(i,j)

, astfel ca

i<j

, dar

t[i]>t[j]

se numeşte

inversie.

Definiţia 2.

Vectorul

t

se numeşte sortat în ordine crescătoare, dacă el nu

conţine nici o inversie.

Definiţia 3.

Vectorul

t

se numeşte sortat în ordine descrescătoare, dacă pentru

(26)

Definiţia 4.

Algoritmul sortării se numeşte stabil, dacă el niciodată nu schimbă

ordinea relativă în vector la oricare 2 elemente cu cheile egale. Adică pentru

orice pereche

i

,

j

:

i<j

;

t[i]=t[j]

,

i—>i'

,

j—>j' => i'<j'

.

Stabilitatea poate fi o cerinţă definitoare, de exemplu când are loc sortarea după o anumită cheie a

elementelor deja sortate după o altă cheie.

Exemplu

: lista studenţilor care este deja sortată după numele se cere de aranjat după an

ul de studii,

dar pentru fiecare an de studii numele trebuie să rămână în ordinea alfabetică.

Despre complixitatea algoritmilor de sortare

La caracterizarea algoritmilor de sortare se foloseşte noţiunea de mărime (de exemplu

numărul de

comparaţii de chei) care

depinde de un număr natural

n

(de exemplu numărul de elemente) relativ la

o altă mărime cunoscută care tot depinde de numărul natural acesta. Se spune că mărimea

k

are

ordinul

comparabil cu mărimea

m

,

se notează

k

=

O

(

m

),

se citeşte “

k

are ordinul o de

m

”, dacă

:

const

m

k

n

 

lim

Din formula aceasta rezultă că mărimile

n

-1,

n

, 2

n

,

n

+3 au unul şi acelaşi ordin O(

n

). Iar mărimea

n

2

are un ordin mai mare.

Algoritmul de sortare a

n

elementelor bazat pe compararea cheilor

are complexitatea minimă

O

(

n

)

sau mai mare.

Demonstrăm prin inducţia metematică:

Pentru

k

= 2

o compara

ţie. Presupunem că pentru

k

=

n

-1 trebuiesc

n

-2 compar

aţii. Se adaugă încă

un element, deci mai trebuie de făcut cel puţin încă o comparaţie

,

obţinem

n

-2+1 =

n

-1 compar

aţii

.

Deci, c

omplexitatea minimă

este

O

(

n

).

Teoretic este demonstrat [vezi 1]

că c

omplexitatea medie

a oricărui algoritm de sortare care

operează cu comparaţiile

nu poate fi mai mică

de

O

(

n

log2

n

).

Algoritmii triviali de sortare

bazaţi pe compararea cheilor a

u complexitatea

atât medie cât şi cea

maximă

O

(

n

2

).

Complexitatea

medie a oricărui algoritm

bun de sortare bazat pe compararea cheilor este

O

(

n

log

2

n

),

totodată

complexitatea maximă a lui poate fi ori

O

(

n

log

2

n

) ori

O

(

n

2

).

3.2.

Clase pentru exemple de sortare

Vom folosi

clasa abstractă

elem

şi clasa concretizată

usual_elem

.

Declarăm clasa generică

vector

parametrizată cu clasa

el

.

//

// C l a s s "v e c t o r"

(27)

template <class el> class vector: public SD

{

protected:

int n; // numărul de elemente ale vectorului

el* t; // pointer la vectorul de elemente

public:

vector<el>(int NMAX=200)

{

n=NMAX;

t=new el[n];

}

vector<el>(char* file_name, int NMAX=200): SD(file_name)

{

t=new el[NMAX];

n=0;

while(!feof(SD::pf))

if(t[n].fscanf_el(SD::pf)>0)

n++;

fclose(SD::pf), SD::pf=NULL;

}

virtual void show(const char* opening, const char* ending, int nlinepp=20)

{

clrscr();

if(!opening)

opening="";

printf("%s", opening);

if(!ending)

ending="\n";

for(int i=0; i<n; i++)

{

if(i>0 && i%nlinepp==0)

{

printf("Press any key to continue...\n");

getch();

clrscr();

printf("%s", opening);

}

printf("%4d. ", (i+1));

(28)

}

printf("%s", ending);

printf("End of vector. Press any key ...\n");

getch();

}

int search(el e)

{

int position = -1;

for(int i=0; (position==-1) && i<n ; i++)

if(SD::ncomp++, e==this->t[i])

position=i;

return position;

}

int get_n()

{

return n;

}

long get_ncomp()

{

return SD::ncomp;

}

void reset_ncomp()

{

SD::ncomp=0;

}

protected:

void swap(int i, int j)

{

el tempor=t[i];

t[i]=t[j];

t[j]=tempor;

}

};

(29)

1. Supraîncărcaţi operatorul de inserţie în clasa

vector

.

2. Supraîncărcaţi operatorul de extragere în clasa

vector

.

3.3.

Sortarea prin interschimbare

Metoda de sortare prin interschimbare (engl. Exchange sort)

constă în parcurgerea elementelor a

le

vectorului, în aşa mod ca fiecare parcurgere micşorează numărul de inversii, până atunci când nu

rămâne nici o inversie. Problema constă în căutarea următoarei

inversiei (

i

,

j

).

Schematic:

while (este_inversie (i, j))

swap(i, j);

Sortarea prin intersc

himbare constă în modificări succesive de

tip

t[i]t[j]

, până când

elementele vectorului nu vor deveni

în ordine crescătoare.

Din această categorie fac parte metoda bulelor

(

bubblesort

)

unul din cei mai slabi algoritmi de

sortare

şi sortarea rapidă

(

quicksort

)

unul din cei mai buni algoritmi de sortare.

Sortarea prin metoda bulelor

Metoda bulelor constă în compararea

t[i]

cu

t[i+1]

, d

acă ordinea este bună se compară

t[i+1]

cu

t[i+2]

,

dacă ordinea nu este bună se interschimbă

t[i]

cu

t[i+1]

şi apoi se compară

t[i+1]

cu

t[i+2]

. După prima parcurgere a vectorului, pe ultima poziţie ajunge elementul având valoarea

cea mai mare, după a doua parcurgere ajunge următorul element

pe penultima poziţie,

etc.

Algoritmul are complexitatea

O

(

n

2

).

void bubble_sort()

{

BOOLEAN inversion;

do

{

inversion = FALSE;

for(int i=0; i<n-1; i++)

if(ncomp++, t[i]>t[i+1])

{

swap(i,i+1);

inversion = TRUE;

}

}

while (inversion);

}

(30)

Complexitatea maximă

este

O

((

n

-1)

2

)=

O

(

n

2

). Dacă elementul minimal are indicele iniţial

n

-1,

atunci va fi nevoie de

n

-

1 executări a ciclului exterior, ca să

-

i dăm indicele

0

.

Pentru fiecare executare a ciclului exterior cu

n

-1 compar

aţii

: (

n

-1)*(

n

-1)=(

n

-1)

2

compar

aţii

.

Complexitatea medie de

asemenea este egală cu

O

(

n

2

), dacă elementul minimal căutat se află

aleator

printre indicii 0, 1,…,

n

-1.

Exerciţiu

:

Este posibilă îmbunătăţirea

acestui algoritm, c

e nu schimbă totuşi esenţial complexitatea.

Îmbunătăţiţi algoritmul de mai sus, prescurtând cu un element parcurgerea de rând faţă de

precedentă.

Metoda bulelor este unul din cei

mai răi algoritmi de sortar

e.

Neajunsul constă în aceea că

la fiecare

pas elementul

următor se compară numai cu vecinul său următor.

3.4.

Sortarea rapidă

Sortarea rapidă (

quicksort

) a fost propusă de C.A.R. Hoare şi foloseşte

principiile

Divide Et

Impera”

şi “

Echilibru

.

Ideea metodei e

ste următo

area

: se selectează un element

arbitrar din vector numit principal (sau

pivot)

şi se rearanjează vectorul în doi subvectori, astfel încât cel din stânga are toate elementele

mai mici sau egale decât pivotul, iar cel din dreapta mai mari sau egale

ca pivotul. Procedeul se reia

în subvectorul din stânga şi apoi în cel din dreapta

, etc

. Procedeul se termină când se ajunge

la

subvectori dintr-un singur element.

În baza clasei generice

vector

declarăm clasa derivată

vector_quicksort

, dotată cu algoritm

ul de

sortare rapidă.

//

// C l a s s " v e c t o r q u i c k s o r t"

//

template <class el> class vector_quicksort: public vector<el>

{

public:

vector_quicksort<el>(char* file_name, int NMAX=200):

vector<el>(file_name, NMAX)

{

}

void quicksort(int i=0, int j=-1)

{

if(j>=n || j==-1)

j=n-1;

if(i<0 || i>j)

i=0;

quicksort_intern(i, j);

(31)

protected:

void quicksort_intern(int i, int j);

int divide(int i, int j);

};

Presupune

m că există funcţia

numită

divide()

, care într

-un anumit fel alege elementul principal cu

cheia

K

şi rea

ran

jează

vectorul astfel, ca

elementul principal primeşte un indice

imain

,

iar toate

elementele cu cheile ≤

K

se aranjează de la stânga (adică au indici

i <

imain

), dar toate elementele cu

cheile

K

se aranjează de la dreapta

(adică au indicii >

imain

):

Avem aici un caz tipic recursiv:

parametrizarea: se precaută pentru

subvectorul

t[i]÷t[j]

; pentru vectorul

iniţial

i=0

,

j=n-1

;

cazul trivial:

i=j

(nu avem ce sorta);

t

recerea de la cazul general la un caz mai simplu, care are loc datorită funcţiei

divide()

.

Dacă există

o astfel de

funcţie, atunci sortarea rapidă imediat se obţine

în formă recursivă

:

template <class el>

void vector_quicksort<el>::quicksort_intern(int i, int j)

{

if (j>i)

{

int imain=divide(i,j);

quicksort_intern(i,imain-1);

quicksort_intern(imain+1,j);

}

}

Algoritmul are loc pentru ambele ordine de apeluri recursive.

Schema metodei de divizare

în timpul

O

(

n

):

elementul cu cheia, ce întrece cheia elementului principal, dar din dreapta –

elementul cu cheia mai

mică ca cheia elementului principal.

După aceasta se poate de s

chimbat cu locurile aceste

două

elemente ≤

t[imain]

e

lemente ≥

t[imain]

(32)

continuă cu poziţiile deja găsite. Vectorul se

socoate

împărţit, când poziţi

ile

din stânga şi din

dreapta se

întâlnesc.

Valo

area comună a lor notăm prin

imain

.

Evident, că complexitatea

divi

rii

nu întrece

O

(

n

), sau mai bine spus

O

(

j-i

) când

divizarea se

aplică

la subvectorul

t[i]÷t[j]

.

Sfaturi practice la alegerea elementului principal

Alegerea elementului principal trebuie

fie

în aşa fel ca să se micşoreze probabilitatea

cazului

când

după divizarea

subvectorii (segmentele)

să difer

e

mult după lungime.

Prima strategie

: la fiecare divizare alegerea aleatorie (

folosind funcţia

-generator de numere

aleatoare) a valorii indicelui elementului principal dintre

i

,

i+1

, …,

j

. Neajunsul acestei metode -

cheltuieli suplimentare de timp necesare

pentru această operaţie.

A două strategie

:

în calitate de elementul principal

se alege elementul cu valoarea medie dintr-un

set nu mare de elemente. C

el mai simplu şi mai uşor

de examinat setul

ce conţine trei elemente cu

indicii respectiv

i

,

j

şi

(i+j)/2

.

Ambele metode micşorează probabilitatea cazului

catastrofal

O

(

n

2

), doar totu

şi

aşa situaţie

nu este

exclusă. Sortarea rapidă întotdeauna poate să se degenereze. Paradoxal, că sortarea rapidă este unul

din cei mai buni algoritmi de sortare internă, dar suntem nevoiţi să ne refuzăm de e

a

în probleme

unde limitele superioare de timp (de tip

kn

log

2

n

) necesare pentru sortarea, sunt critice.

Algori

tmul divizării

Există mai multe variante a

le algoritmului de divizare.

Toate din ele urmăresc cel puţin două

scopuri:

a accelera ciclurile interioare;

a prevedea caracterul

aleator

” a vectorului. Adică de a exclude introducerea întâmplătoare a

ordinei

î

n

segmentele de divizare din punct de vedere al productivităţii generale a algoritmului.

(33)

Sedgewick R. E. a propus următoare

a

metodă

de divizare:

a)

punem elementul principal

în poziţia

i

(

îl schimbăm dacă este necesar cu elementul

t[i]

).

b)

divizăm sub

vectorul

t[i+1]

,

t[i+2]

,…

t[j]

, cu ajutorul valoarei elementului principal

t[i]

lăsând

pe

t[i]

la locul său

. S

e primeşte

divizarea cu

poziţi

a intermediar

ă

imain

, de exemplu:

c)

schim

băm

cu locurile elementul

t[i]

cu elementul

t[imain]

şi dăm valoarea

imain

ca

rezultatul

întors de către funcţia

divide

.

template <class el> int vector_quicksort<el>::divide(int i, int j)

{

int imain, jmain, imed;

imed =(i+j)/2;

imain = (SD::ncomp++, t[i] < t[imed]) ?

((SD::ncomp++, t[imed] < t[j]) ?

imed

:

(SD::ncomp++, t[i] < t[j]) ? j : i)

:

((SD::ncomp++, t[imed] > t[j]) ?

imed

:

(SD::ncomp++, t[i] > t[j]) ? j : i);

i i+1 j

i imain j

imain+1 imain-1

elemente ≤

t[imain]

elementul

elemente ≥

t[imain]

principal

i imain j

imain+1 imain-1

(34)

if(imain > i)

swap(i, imain);

imain = i+1, jmain = j;

while(imain < jmain)

{

while((imain < jmain)&&(SD::ncomp++, t[imain] <= t[i]))

imain++;

while((jmain > imain)&&(SD::ncomp++, t[jmain] >= t[i]))

jmain--;

if(imain < jmain)

swap(imain, jmain);

}

if(SD::ncomp++, t[imain] > t[i])

imain--;

if(imain > i)

swap(i, imain);

return imain;

}

Este clar că funcţia

divide()

are complexitatea

O

(

n

). Ciclul exterior

while(imain < jmain)

{

}

verifică fiecare element al vectorului

t[0]

,

t[i]

,…

t[n-1]

cel mai mult de două ori, dar restul

operaţiilor cere un timp fix.

În funcţia

main()

creăm un vector

si-il sortat

ăm

prin metoda

quicksort:

void main()

{

clrscr();

vector_quicksort<usual_elem> gr("Stud.txt");

gr.show("Unsorted group:\n","");

gr.quicksort();

gr.show("Group sorted by name:\n","");

printf("n=%d, ncomp=%d, n*log2(n)=%.2lf, n*n=%.0lf\n",

gr.get_n(),gr.get_ncomp(),

gr.get_n()*log((double)gr.get_n())/log(2.0),

(double)gr.get_n()*gr.get_n());

getch();

}

A

fişare

a va

arăta astfel:

(35)

1. Green 1987 350.00

2. Red 1980 450.00

3. Blue 1981 500.00

4. Gray 1968 900.00

5. Orange 1984 550.00

6. White 1980 600.00

7. Cyan 1975 800.00

8. Yellow 1988 300.00

9. Magenta 1983 600.00

10. Black 1981 500.00

End of vector. Press any key ...

Group sorted by name:

1. Black 1981 500.00

2. Blue 1981 500.00

3. Cyan 1975 800.00

4. Gray 1968 900.00

5. Green 1987 350.00

6. Magenta 1983 600.00

7. Orange 1984 550.00

8. Red 1980 450.00

9. White 1980 600.00

10. Yellow 1988 300.00

End of vector. Press any key ...

n=10, ncomp=39, n*log2(n)=33.22, n*n=100

Analiza rezultatelor rămâne ca exerciţiu.

Dacă vrem să sortăm după anul de naştere, atunci declarăm în baza clasei

usual_elem

clasa

year_elem

la care suprascriem

funcţia

cmp()

.

//

// C l a s s "y e a r _ e l e m"

//

class year_elem : public usual_elem

{

public:

year_elem()

{

}

year_elem(char* init_name, int init_year, double init_salary):

usual_elem(init_name, init_year, init_salary)

{

(36)

virtual int cmp(elem& e2)

{

int result;

if(this->year < ((year_elem&)e2).year)

result=-1;

else

if(this->year > ((year_elem&)e2).year)

result=1;

else

result=0;

return result;

}

};

În funcţia

main()

instanţiem clasa generică

vector_quicksort

cu clasa-argument

year_elem

.

void main()

{

clrscr();

vector_quicksort<year_elem> gr("Stud.txt");

gr.show("Unsorted group:\n","");

gr.quicksort();

gr.show("Group sorted by year:\n","");

printf("n=%d, ncomp=%d, n*log2(n)=%.2lf, n*n=%.0lf\n",

gr.get_n(),gr.get_ncomp(),

gr.get_n()*log((double)gr.get_n())/log(2.0),

(double)gr.get_n()*gr.get_n());

getch();

}

Afişarea va arăta astfel:

Unsorted group:

1. Green 1987 350.00

2. Red 1980 450.00

3. Blue 1981 500.00

4. Gray 1968 900.00

5. Orange 1984 550.00

6. White 1980 600.00

7. Cyan 1975 800.00

8. Yellow 1988 300.00

9. Magenta 1983 600.00

10. Black 1981 500.00

(37)

1. Gray 1968 900.00

2. Cyan 1975 800.00

3. Red 1980 450.00

4. White 1980 600.00

5. Black 1981 500.00

6. Blue 1981 500.00

7. Magenta 1983 600.00

8. Orange 1984 550.00

9. Green 1987 350.00

10. Yellow 1988 300.00

End of vector. Press any key ...

n=10, ncomp=42, n*log2(n)=33.22, n*n=100

Analiza rezultatelor rămâne ca exerciţiu.

Exerciţii.

1. Supraîncărcaţi operatorul de inserţie în clasa

year_elem

.

2. Supraîncărcaţi operatorul de extragere în clasa

year_elem

.

În sfârşit declarăm în baza clasei

usual_elem

clasa derivată

salary_elem

care c

ompară

obiecte

după salariu.

//

// C l a s s "s a l a r y _ e l e m"

//

class salary_elem : public usual_elem

{

public:

salary_elem()

{

}

salary_elem(char* init_name, int init_year, double init_salary):

usual_elem(init_name, init_year, init_salary)

{

}

virtual int cmp(elem& e2)

{

int result;

if(this->salary < ((salary_elem&)e2).salary)

result=-1;

(38)

if(this->salary > ((salary_elem&)e2).salary)

result=1;

else

result=0;

return result;

}

};

În funcţia

main()

instanţiem clasa generică

vector_quicksort

cu clasa-argument

salary_elem

.

void main()

{

clrscr();

vector_quicksort<salary_elem> gr("Stud.txt");

gr.show("Unsorted group:\n","");

gr.quicksort();

gr.show("Group sorted by salary:\n","");

printf("n=%d, ncomp=%d, n*log2(n)=%.2lf, n*n=%.0lf\n",

gr.get_n(),gr.get_ncomp(),

gr.get_n()*log((double)gr.get_n())/log(2.0),

(double)gr.get_n()*gr.get_n());

getch();

}

Afişarea de data aceasta va arăta astfel:

Unsorted group:

1. Green 1987 350.00

2. Red 1980 450.00

3. Blue 1981 500.00

4. Gray 1968 900.00

5. Orange 1984 550.00

6. White 1980 600.00

7. Cyan 1975 800.00

8. Yellow 1988 300.00

9. Magenta 1983 600.00

10. Black 1981 500.00

End of vector. Press any key ...

Group sorted by salary:

1. Yellow 1988 300.00

2. Green 1987 350.00

3. Red 1980 450.00

4. Blue 1981 500.00

(39)

7. White 1980 600.00

1. Supraîncărcaţi operatorul de inserţie în clasa

salary_elem

.

2. Supraîncărcaţi operatorul de extragere în clasa

salary_elem

.

Construirea sortării rapide efective

Apelurile recursive neterminate vor fi

înscrise în stivă. În cel mai nefavorabil caz

divizarea

secvenţială poate da sistematic

imain=0

(

imain=i

)

sau

imain=n-1

(

imain=j

).

În acest caz

divizarea

vectorului în doi sub vectori

va da permanent unul de lungime

0

iar altul va avea lungimea

n-1

(

j-i

).

Adâncimea în apelurile recursive poate atinge valoarea

n

lungimea vectorului

iniţial.

Trebuie

prevedem

stiva de adâncime

n

(complexitatea spaţială

O

(

n

), ce nu este accesibil).

accesibil). Deci mod

ificăm funcţia

quicksort_intern()

ca ea să se

arate astfel:

template <class el>

void vector_quicksort<el>::quicksort_intern(int i, int j)

(40)

{//începem cu intervalul drept

Deoarece

modificarea introdusă nu va schimba afişările

obţinute pr

in exemplele de sortare

precedente, nu le mai repetăm.

fi divizat de către funcţia

divide()

.

O situaţie ideală care ar putea fi

o

bţinută prin aplicarea principiu

lui

de echilibru, constă în

pe lângă

n

la complexitatea împărţirii.

Aşa

dar,

metoda obţine

O

(

n

log2

n

) ce este limita de jos pentru complexitatea algoritmilor de sortare

bazaţi

pe compararea cheilor.

Dacă

divizarea sistematic se

obţine

lângă primul sau lângă ultimul element

e ale subvectorilor

cercetaţi (adică

permanent

imain=i

, sau

imain=j

), atunci

fiecare dată rămâne de sortat o parte a

subvector

ului, în care numărul de elemente este cu o unitate mai mic

decât subvectorul precedent,

şi

rezultă că complexitatea

va fi

T

(

n

)

O

(

n

)

O

(

1

)

T

(

n

1

)

O

(

n

)

O

(

n

1

)

...

O

(

1

)

O

(

n

2

)

.

În acest caz sortarea rapidă are complexitatea teoretică asemănătoare cu complexitatea c

elor mai

răi

algoritmi de sortare, de exemplu sortarea prin metoda

bulelor. Dar complexitatea practică probabil

va fi

şi mai mare

,

din cauza timpului necesar pentru realizarea recursiei dirijate de stivă.

Pentru

sortarea rapidă este arătat teoretic că complexitatea medie apreciată pentru probabilităţile egale a

tuturor permutărilor este egală cu

O

(

n

log

2

n

) cu aproximativ 2

n

log

2

n

compar

aţii

a cheilor, de

asemenea este arătat că probabilitatea celui mai nefavorabil caz cu complexitatea

O

(

n

2

) este destul

de mică.

Posibilitatea celui mai nefavorabil caz

nu este exclusă când datele sunt deja sortate

sau parţial

sortate (

poate fi şi în ordine inversă).

Paradoxul sortării rapide în contrastul cu sortarea prin inserţie sau chiar

prin metoda bulelor,

constă

(41)

incomod,

fiindcă necesitate

a de a sorta datele

“aproape sortate” destul de des se întâlneşte în

practică.

Îmbunătăţirea sortării rapide

Ca

sortarea rapidă să devină

real un algoritm efectiv, ea mai

cere încă o îmbunătăţire.

Este evident,

că î

n versiunile precedente

recursia şi alegerea elementului principal devin destul de

grele pentru

subvectori mici

. Sortarea rapidă nu poate fi aplicată

la vectori mici. De

aceea recursia trebuie oprită

c

ând dimensiunea subvectorului devine mai mică decât careva constantă, numită prag. După aceasta

se foloseşte metoda, eficacitatea căreia poate să se îmbunătăţească la datele parţial sortate, de

exemplu sortarea prin inserţie simplă.

D.

Knuth a obţinut că

v

aloarea optimală teoretică a pragului

este

egală cu 9.

În

practică rezultatele bune ne dau valorile pragului de la 8 până la 20, iar valoarea optimă

se

conţine între 14 şi 16.

//

// C l a s s " v e c t o r o p t i m q u i c k s o r t "

//

template <class el> class vector_optim_quicksort:

public vector_quicksort<el>

{

public:

vector_optim_quicksort<el>(char* file_name, int threshold_init=15,

int NMAX=200):

vector_quicksort<el>(file_name, NMAX)

{

threshold=threshold_init;

if(threshold<1)

threshold=1;

}

void quicksort(int i=0, int j=-1)

{

if(j>=n || j==-1)

j=n-1;

if(i<0 || i>j)

i=0;

quicksort_intern(i, j);

}

protected:

(42)

void quicksort_intern(int i, int j);

void insertsort(int i, int j);

};

template <class el>

void vector_optim_quicksort<el>::quicksort_intern(int i, int j)

{

if(j-i+1>threshold)

{

int imain=divide(i,j);

if(imain-i > j-imain)

{

quicksort_intern(i,imain-1);

quicksort_intern(imain+1,j);

}

else

{

quicksort_intern(imain+1,j);

quicksort_intern(i,imain-1);

}

}

else

insertsort(i, j);

}

template <class el>

void vector_optim_quicksort<el>::insertsort(int i, int j)

{

for(int k = i+1; k<=j; k++)

for(int l=k; (l>i)&&(ncomp++, t[l]<t[l-1]); l--)

swap(l-1, l);

}

Exemple de funcţia

main()

luăm din trei exemple precedente, înlocuind în ele denumirea clasei

vector_quicksort

cu

vector_optim_quicksort

şi adăugând la parametrii

constructorilor

valoarea pentru prag, de exemplu 4.

Afişările, analiza cărora rămâne ca exerciţiu,

vor

arăta astfel:

Pentru vectorul concretizat cu clasa

usual_elem

Unsorted group:

1. Green 1987 350.00

2. Red 1980 450.00

(43)

4. Gray 1968 900.00

5. Orange 1984 550.00

6. White 1980 600.00

7. Cyan 1975 800.00

8. Yellow 1988 300.00

9. Magenta 1983 600.00

10. Black 1981 500.00

End of vector. Press any key ...

Group sorted by name:

1. Black 1981 500.00

2. Blue 1981 500.00

3. Cyan 1975 800.00

4. Gray 1968 900.00

5. Green 1987 350.00

6. Magenta 1983 600.00

7. Orange 1984 550.00

8. Red 1980 450.00

9. White 1980 600.00

10. Yellow 1988 300.00

End of vector. Press any key ...

n=10, ncomp=32, n*log2(n)=33.22, n*n=100

Pentru vectorul concretizat cu clasa

year_elem

Unsorted group:

1. Green 1987 350.00

2. Red 1980 450.00

3. Blue 1981 500.00

4. Gray 1968 900.00

5. Orange 1984 550.00

6. White 1980 600.00

7. Cyan 1975 800.00

8. Yellow 1988 300.00

9. Magenta 1983 600.00

10. Black 1981 500.00

End of vector. Press any key ...

Group sorted by year:

1. Gray 1968 900.00

2. Cyan 1975 800.00

3. White 1980 600.00

4. Red 1980 450.00

5. Black 1981 500.00

6. Blue 1981 500.00

(44)

8. Orange 1984 550.00

9. Green 1987 350.00

10. Yellow 1988 300.00

End of vector. Press any key ...

n=10, ncomp=37, n*log2(n)=33.22, n*n=100

Pentru vectorul concretizat cu clasa

salary_elem

Unsorted group:

1. Green 1987 350.00

2. Red 1980 450.00

3. Blue 1981 500.00

4. Gray 1968 900.00

5. Orange 1984 550.00

6. White 1980 600.00

7. Cyan 1975 800.00

8. Yellow 1988 300.00

9. Magenta 1983 600.00

10. Black 1981 500.00

End of vector. Press any key ...

Group sorted by salary:

1. Yellow 1988 300.00

2. Green 1987 350.00

3. Red 1980 450.00

4. Blue 1981 500.00

5. Black 1981 500.00

6. Orange 1984 550.00

7. White 1980 600.00

8. Magenta 1983 600.00

9. Cyan 1975 800.00

10. Gray 1968 900.00

End of vector. Press any key ...

n=10, ncomp=33, n*log2(n)=33.22, n*n=100

În următorul tabel

este prezentat num

ărul

de comparări

efectuate la sortarea obiectelor

vector_optim_quicksort

create pe

baza fişierului

”stud.txt”

pentru diferite valori ale pragului

şi clase de concretizare.

Prag

Câmpul de sortare

name year salary

0

39

42

43

1

39

42

43

2

37

37

41

3

35

37

38

Referensi

Garis besar

Dokumen terkait

(g) „costul total al creditului pentru consumatori” înseamnă toate costurile, inclusiv dobânda, comisioanele, taxele şi orice alt tip de costuri pe care trebuie să le

loc de joaca pentru copii, spatiu depozitare echipament de schi, baza wellness noua cu bio sauna, baie cu aburi,camera de relaxare, terasa, parcare. Dotari: camera cu baie

– O unitate de activitate α-glucozidazică = cantitatea de enzimă care eliberează un µmol de pNP pe minut... Metode de analiză a hidrolizatelor de amidon.. A) Compuşi cu

Skripsi ini membahas tentang “efektivitas penggunaan model pembelajaranteams games tournament (TGT) menggunakan media gambar cetak dalam meningkatkan hasil belajar

Pada T2 embrio mencit yang dikultur dalam HECM-6 mampu tumbuh setara dengan embrio mencit yang dikultur dalam medium KSOMaa (T1) sampai tahap blastosis akhir,

Dari hasil observasi yang penulis lakukan, AMINEF / Fulbright Indonesia berkeinginan untuk memiliki database yang terpadu sehingga para karyawan dapat menggunakan

Desa Palar Legen sebagai salah satu desa yang menianjikan kehidupan yang harmoni antara manusia dengan lingkungan hidup di sekitarnya. Misalnya, di desa Palar Legen

Puji dan syukur penulis panjatkan kepada Allah SWT atas segala rahmad dan karunia- Nya yang telah dilimpahkan sejak penulis mencari ide, mengajukan, menyusun,