Sunteți pe pagina 1din 33

CAPITOLUL I Introducere in C++

C++ (pronunat "si plus plus") este un limbaj de programare general. Este un limbaj multi-paradigm cu verificarea static a tipului variabilelor ce suport programare procedural, abstractizare a datelor, programare orientat pe obiect. n anii 90, C++ a devenit unul din cele mai populare limbaje de programare comerciale. Nimeni nu deine C++ ns, acesta fiind gratuit. Bjarne Stroustrup de la Bell Labs a dezvoltat C++ (iniial denumit C cu Clase) n anii 1980 ca o serie de mbuntiri ale limbajului C. Acestea au nceput cu adugarea de clase apoi de funcii virtuale, suprancrcarea operatorilor, motenire multipl, template-uri i excepii. Limbajul de programare C++ a fost standardizat n 1998 ca i ISO 14882:1998, versiunea curenta fiind din 2003, ISO 14882:2003. Istoricul C++ Stroustrup a nceput s lucreze la C cu Clase n 1979. Ideea crerii unui nou limbaj a venit din experiena de programare pentru pregtirea tezei sale de doctorat. Stroustrup a descoperit c Simula avea faciliti foarte utile pentru proiecte mari, ns era prea lent, n timp ce BCPL era rapid, ns nu era de nivel nalt i era nepotrivit pentru proiecte mari. Cnd Stroustrup a nceput s lucreze pentru Bell Labs, avea sarcina de a analiza nucleul UNIX referitor la calcul distribuit. Amintindu-i de experiena sa din perioada lucrrii de doctorat, Stroustrup a nceput s mbunteasc C cu faciliti asemntoare Simula. C a fost ales deoarece era rapid i portabil. La nceput facilitile adaugate C-ului au fost clase, clase derivate, verificare a tipului, inline i argumente cu valori implicite. n timp ce Stroustrup a proiectat C cu Clase (mai apoi C++), a
1

scris de asemenea i Cfront, un compilator care genera cod surs C din cod C cu Clase. Prima lansare comercial a fost n 1985. n 1982, numele limbajului a fost schimbat de la C cu Clase la C+ +. Au fost adaugate noi faciliti, inclusiv funcii virtuale, suprancrcarea operatorilor i a functiilor, referine, constante, alocare dinamic, un control al tipului mai puternic i noua variant de comentariu pe un singur rnd (liniile care ncep cu caracterele '//'). n 1985 a fost lansat prima ediie a crii "The C++ Programming Language" (Limbajul de programare C++), oferind informaii importante despre limbaj, care nc nu era un standard oficial. n 1989 a fost lansat versiunea 2.0 a C++. Au aprut acum motenirea multipl, clase abstracte, funcii statice, funcii constante i membri protected. n 1990 o alt carte a fost lansat, oferind suport pentru standarde viitoare. Ultimele adugri includeau template-uri, excepii, spaii de nume (namespace-uri) i tipul Boolean. O dat cu evoluia limbajului C++, a evoluat i o bibliotec standard. Prima adugire a fost biblioteca de intrri/ieiri (I/O stream), care oferea faciliti pentru a nlocui funciile tradiionale C cum ar fi printf i scanf. Mai trziu, printre cele mai semnificative adugari la biblioteca standard a fost STL (Standard Template Library) (Biblioteca de formate standard). Dup ani de lucru, un comitet ANSI-ISO a standardizat C++ n 1998 (ISO/IEC 14882:1998). Instructiuni elementare in limbajul C++ 1 Instructiuni conditionale Conditii In limbajul C++ intalnim doua instructiuni conditionale (if si switch), precum si o variant imediata a instructiunii if. Le vom descrie pe rand insistand asupra modalitatii de folosire si a greselilor frecvent intalnite. Insa, inainte de aceasta vom detalia notiunea de conditie in acceptiunea limbajului C++ si vom prezenta operatorii logici acceptati. In limbajul C++ nu exista un tip logic de sine statator cum exista in Pascal tipul boolean. Pentru un plus de flexibilitate si o mai mare putere de exprimare s-a convenit ca orice tip de variabila/expresie ce
2

poate fi evaluata la un tip diferit de tipul void sa poata fi considerata o expresie logica. Conventia este ca orice expresie/variabila/constanta care este evaluata la o valoare nenula (diferita de 0) sa fie considerata ca avand valoarea de adevar adevarat si orice expresie/variabila/constanta care este nula sa fie considerate ca avand valoare de adevar fals. Pentru a clarifica acest concept vom prezenta cateva exemple de conditii. La inceput vom declara cateva variabile: int x = 3; char ch = A; char chn = 0x0; float r = 0.0; unsigned long ul = 0; Acum sa vedem cateva expresii logice formate cu variabilele de mai sus si valoarea lor de adevar: x - este o expresie care fiind diferita de 0 (3 != 0), se va evalua la adevarat chn - este o expresie care fiind 0 (caracterul avand codul ASCII 0) este considerata falsa x + ul - este o expresie a carei valoare este 3 + 0 = 3, adica adevarata ch * 0 - este o expresie a carei valoare este 0 (A are codul ASCII 65, iar 65 * 0 = 0), deci falsa x / ch - este o expresie a carei valoare este 0 (3 / 65 in numere intregi are valoarea 0), deci falsa sqrt(x) - este o expresie a carei valoare este nenula, radical din 3 este aprox. 1.732 De obicei nu vom testa pur si simplu o variabila, sau o expresie simpla, ci vom folosi operatori pentru a defini o conditie. Deoarece in acceptiunea C++ nu exista un tip logic ci orice expresie poate fi o conditie, se pot folosi absolut toti operatorii in cadrul expresiei (aritmetici, relationali, logici, pe biti, de egalitate, etc). Operatorii logici (a nu se confunda cu operatorii aritmetici, relationali, de egalitate sau pe biti care pot fi folositi in expresiile care se evalueaza pentru conditie) suportati de C++ sunt: && SI logic. Returneaza adevarat daca ambele expresii se evalueaza la valori nenule, altfel returneaza fals. Daca prima expresie este evaluata la 0 (fals), a doua expresie nu se mai evalueaza. Acest lucru poate cauza erori de programare greu de detectat in cazul in care
3

cea de a doua expresie contine un apel de functie. Apelul va avea loc numai daca prima conditie se evalueaza la adevarat (valoare nenula). Atentie: un singur simbol & inseamna SI pe biti si este diferit de &&. || SAU logic. Returneaza adevarat daca cel putin una din expresii se evalueaza la o valoare nenula. In cazul in care prima expresie este adevarata, cea de a doua nu se mai evalueaza. Remarca este absolut similara cu cea de la SI logic. Atentie: un singur simbol | insemna SAU pe biti si este diferit de ||. ! negatie logica; Returneaza adevarat daca expresia este nula, fals in caz contrar. (!E) este echivalent cu (E == 0). Alti operatori mult folositi in conditii sunt operatorii de egalitatate: == egal. Returneaza adevarat (valoare nenula) daca cele doua expresii sunt egale, fals (0) daca cele doua expresii sunt diferite. Din punct de vedere logic (E1 == E2) este echivalent cu (!(E1 - E2)) != diferit. Semnificatia este opusa operatorului ==. In consecinta, din punct de vedere logic (E1!= E2) si (E1 - E2) sunt echivalente. Ultimii operatori amintiti sunt cei relationali: < mai mic. Returneaza adevarat daca valoarea expresiei din stanga este mai mica decat a celei din dreapta. Normal ca ambele expresii trebuie sa fie de tip scalar/numeric (integer/real/caracter/etc) pentru ca sa se poate face comparatia. <= mai mic sau egal. Returneaza adevarat daca valoarea expresiei din stanga este mai mica sau egala cu cea din dreapta. > mai mare. Returneaza adevarat daca valoarea expresiei din stanga este mai mare ca si cea din dreapta. >= mai mare sau egal. Returneaza adevarat daca valoarea expresiei din stanga este mai mare sau egala cu cea din dreapta. Acum, dupa ce am prezentat cele mai importante lucruri despre ceea ce inseamna o conditie si care sunt operatorii care pot fi folositi in definirea conditiilor, vom trece in revista instructiunile conditionale. - if Sintaxa instructiunii este: if ( <conditie> ) <instructiune1>; Sau in varianta cu executie alternativa (executie pentru cazul in care conditia este falsa) if ( <conditie> ) <instructiune1>; else <instructiune2>;
4

In ambele cazuri prin instructiune se intelege fie o instructiune simpla, fie una compusa (grup de instructiuni incadrat de { } ). Conditia poate sa contina dupa cum am mentionat mai sus orice expresie. Este bine insa sa nu se exagereze la folosirea expresiilor complexe deoarece aceasta afecteaza claritatea codului. Un exemplu in care se poate vedea aceasta este urmatorul: int x = 3; int y = 4; if(x = y) { printf(Conditie adevarata); } else printf(Conditie falsa); In mod oarecum surprinzator pentru un neinitiat in C++ bucata de program la executie va tipari Conditie adevarata. Aceasta deoarece conditia este x = y, adica o atribuire si nu o comparative (comparatia ar fi fost x == y), iar atribuirea primeste ca valoare, valoarea atribuita (4 in cazul nostru). Recomandam o folosire mai putin tipica C++, dar mult mai usor de inteles: int x = 3; int y = 4; x = y; if(x != 0) //mai normal pentru C++ ar fi fost sintaxa echivalenta if(x) { printf(Conditie adevarata); } Mai facem doua remarci pentru instructiunea if. Este obligatoriu simbolul de terminare instructiune ; inainte de else. In cazul in care avem mai multe instructiuni if/else imbricate se recomanda folosirea parantezelor acoladice pentru a ne asigura ca ramura de else se va interpreta pentru ramura de if la care ne asteptam. De exemplu: if(conditie_a) { if(conditie_b) //executie pt conditie_b adevarata else //executie pt conditie_b falsa
5

} else { //executie pt conditia_a falsa } - operatorul conditional (if imediat) ? : In multe cazuri avem de atribuit o anumita valoare unei variabile in functie de o anumita conditie. Sa presupunem urmatorul scenariu. Daca conditia_a este adevarata atunci vrem sa atribuim variabilei x valoarea 1, daca nu valoarea 3. Evident acest lucru se poate scrie folosind instructiunea if: if(conditia_a) x = 1; else x = 3; O alta posibilitate mult mai compacta si mai usor de scris este folosind operatorul if imediat: x = (conditia_a) ? 1 : 3; - switch Formatul instructiunii este: switch ( <variabila> ) { case <constanta1> : <instructiune1>; [break;] case <constanta2> : <instructiune2>; [break;] ... default : <instructiune_def>; } Este o instructiune folosita cand se doreste ca in functie de valoarea unei anumite expresii (<variabila>) numerice sa se execute un grup de instructiuni. De obicei se foloseste in cazul in care avem mai mult de trei variante (pentru 2 variante este mai comoda varianta cu if). Ca si interpretare in cazul in care break este prezent pe toate ramurile de case, este echivalenta cu: if(<variabila> == <constanta1>) <instructiune1>; else if(<variabila> == <constanta2>) <instructiune2>; else <instructiune_def>; In cazul in care break nu este prezent pe una din ramuri se va executa in continuare instructiunea de pe ramura urmatoare. O greseala comuna este uitarea instructiunii break in totalitate, in acest
6

caz se vor executa toate instructiunile situate mai jos de ramura de case pe care este selectata executia. ex. unsigned int uTest = 3; //evident in acest caz se va intra doar pe ramura pentru valoarea 3, //daca aceasta exista, altfel pe ramura default daca exista unsigned uVal = 0; switch(uTest) { case 1 : uVal = 3; break; case 3 : uVal = 78; break; default : uVal = 0; } Daca nu ar fi existat instructiunile break s-ar fi executat uVal = 78 si apoi uVal = 0, deoarece neexistand instructiunea break de iesire din switch executia se continua liniar ignorand selectiile din case. Desi aceasta pare un deficit, in fond este un avantaj, permitand un cod mai compact in cazul in care conditiile din case sunt cumulative (adica pentru prima ramura se executa si codul de pe cea de a doua ramura, etc.). 2 Instructiuni de ciclare In C++ instructiunile care pot fi folosite pentru a cicla (executa un grup de mai multe instructiuni -numit si bucla de ciclare- in mod repetat) sunt: - for - while - do while Toate aceste instructiuni folosesc conditii pentru a iesi din bucla de ciclare. Reamintim ca in C o conditie este formata din orice expresie, nu exista un tip specific "logic" pentru conditii ca si in alte limbaje de programare. Expresia care formeaza conditia este analizata si daca este diferita de zero se considera valoare "adevarat", iar daca este egala cu zero se considera "fals". Vom analiza pe rand instructiunile de ciclare si vom specifica pentru fiecare cateva modalitati tipice de folosire. Inainte insa vom reaminti cateva notiuni despre sirurile de caractere, deoarece o utilizare tipica a tuturor instructiunilor de ciclare este parcurgerea sirurilor de caractere si executarea unor anumite operatiuni pe acestea. Cele mai multe din exemple vor lucra cu siruri de caractere sau de intregi.
7

Sirurile de caractere in C/C++ sunt stocate ca si secvente de caractere in memorie terminate prin caracterul 0. Nu este stocata lungimea in mod explicit. De exemplu cuvantul ABC este stocat in memorie ca un sir de caractere A B C \0x0. Numeric in memorie se vor gasi codurile ASCII: 65 66 67 0, sau in hexazecimal 0x41 0x42 0x43 0x0. Sirurile sunt referite in general de pointeri la caracter. Deoarece in C++ un nume de matrice/vector este un pointer constant la sirul -primul element din sir de fapt, dar acesta este urmat in memorie de restul elementelor din sir- de caractere/elemente din matrice/vector, acesta se poate folosi pentru a cicla peste vectorul respectiv. De exemplu urmatorul cod declara o variabila de tip sir de caractere, apoi copiaza in sirul respectiv un alt sir, iar in final tipareste sirul.. unsigned char strBuffer[256]; strcpy(strBuffer, Un alt sir); printf(strBuffer); - for Sintaxa instructiunii este prezentata mai jos. for ( [<instructiuni_initializare>] ; [<conditie_ciclare>] ; [<instructiuni_incrementare>] ) <instructiune_bucla>; Este probabil cea mai folosita instructiune de ciclare, prin ea se pot emula toate celelalte instructiuni. In pozitia instructiuni_initializare se pot include mai multe instructiuni separate prin virgula ,. Acestea pot fi declaratii de variabile, initializari, etc. Se vor executa la inceputul buclei, inainte de orice instructiune din bucla. In cadrul instructiuni_incrementare se respecta aceleasi reguli, doar ca aceste instructiuni se vor executa la fiecare ciclare. In mod normal aici se vor afla instructiuni care modifica contoarele folosite in ciclu si in baza carora se va testa iesirea din bucla. Testul se va face in conditie_ciclare. Conditia se testeaza pentru fiecare executie a buclei, inclusiv prima executie. Sa vedem de exemplu cum putem parcurge un sir de caractere, modificand caracterele la majuscule (in engleza UpCase): unsigned char strSir[256]; sscanf(Introduceti un sir de caractere (cuvant/propozitie): %s, strSir); //citim for(int iIndex = 0; (strSir[iIndex]) && (iIndex < 256); iIndex++) strSir[iIndex] = UpCase(strSir[iIndex]); printf(\nSirul convertit la majuscule: %s\n, strSir);
8

Ne vom concentra asupra instructiunii for. La initializare pornim un contor care va creste de la 0 pana la valoarea pe care se afla pozitia ultima in sir (caracterul 0). Variabila declarata in cadrul instructiunilor de initializare este local buclei for si nu poate fi folosita in afara ei.In conditie testam doua lucruri: caracterul din sir de pe pozitia indicata de iIndex sa fie diferit de 0 si sa nu depasim dimensiunea sirului. De obicei al doilea test nu se mai face, in cazul in care sirul este folosit corect, nu se va ajunge niciodata sa se depaseasca dimensiunea maxima. Daca in schimb se incerca a se copia in spatiul destinat unui sir un sir care este mai mare decat spatiu respectiv se poate ajunge in situatia in care nu mai exista caracterul 0 de terminare si atunci se va depasi zona de date alocata sirului, algoritmul mergand in continuare pana intalneste primul 0 in memoria de dupa sir (aceasta generand un memory overwrite si o comportare aleatoare a programului). Sa vedem de exemplu un mic program care determina lungimea unui sir de caractere (echivalent cu strlen): #include <stdio.h> void main(void) { unsigned char strSir[256]; sscanf(Introduceti un sir de caractere (cuvant/propozitie): %s, strSir); //citim de la //tastatura int iIndex; for(iIndex = 0; (strSir[iIndex]) && (iIndex < 256); iIndex++); printf(\nSirul are lungimea: %d\n, iIndex); } Ca si in cazul instructiunii switch este posibil sa folosim break pentru a intrerupe in mod fortat bucla. O alta instructiune de control al executiei in cadrul buclei este continue care trece controlul la inceputul buclei. De exemplu daca vrem sa parcurgem un sir de 20 de elemente si in loc de fiecare element sa setam inversul acestuia (1/x, evident doar daca acesta este nenul). float array[20]; void main () { array[16] = 5.0; for (i = 0; i < 20; i++) { if (array[i] == 0) //se poate scrie pur si simplu if(array[i])
9

continue; array[i] = 1/array[i]; } } - while Sintaxa instructiunii este urmatoarea: while ( <conditie> ) <instructiune>; Este folosita pentru ciclurile in care conditia nu depinde de un contor. De exemplu unul din cazurile cele mai folosite sunt la citirea dintr-un fisier. Exemplul de mai jos prezinta un ciclu in care se citesc dintr-un fisier (c:\\test.txt) blocuri de 1MB si se afiseaza de fiecare data nr. de bytes (octeti) cititi (cu unele modificari s-ar putea folosi ca si un program de cautare in fisiere a unui text, sau de copiere). const int BufferSize = 1024L * 1024L; int *Buffer = (int *)malloc(BufferSize); //1Mb = 1024 * 1024 bytes int fin = open(c:\\test.txt, O_RDONLY | O_BINARY); int iBytesRead; while((fin != -1) && (!eof(fin))) { iBytesRead = read(fin, Buffer, BufferSize); printf(blocul de date citit are dimensiunea de: %d\n, iBytesRead); } close(fin); Se remarca folosirea unor altor instructiuni de lucrul cu fisiere fata de articolul trecut. In mod normal acestea sunt preferate -mai noi si mai eficiente-, desi in cele mai multe cazuri pentru convenienta data de fprintf si fscanf se folosesc instructiunile mai vechi fopen, fclose. Echivalentul in Pascal al instructiunii este while ... do. Mentionam aceasta pentru a se evita confuzia dintre while si do ... while -ambele instructiuni C++-. Prima testeaza conditia la inceput de bucla, a doua executa bucla si abia apoi testeaza conditia (la sfarsit de bucla). In cazul while se poate intampla ca sa nu se execute bucla nici macar o data daca la evaluarea conditiei de ciclare aceasta este mereu falsa. Ca si in cazul instructiunii for se pot folosi break si continue pentru a intrerupe executia sau a relua executia de la inceputul buclei (cu reevaluarea conditiei!). - do ... while
10

In cazurile in care este necesara cel putin o parcurgere a buclei inainte de testare se foloseste instructiunea do...while. Sintaxa ei este redata mai jos: do <instructiune> while ( <conditie> ); Echivalentul ei in Pascal este repeat ... until. Ca si la while se folosesc instructiunile break si continue pentru a controla executia in interiorul buclei. Diferenta vine din faptul ca se executa in mod sigur cel putin o data bucla, pe cand la while bucla poate sa nu se execute deloc daca se evalueaza la fals conditia in momentul atingerii instructiunii while. Mai jos dam un exemplu de program care foloseste bucla do ... while pentru a testa introducerea unei parole: #include <stdio.h> #include <string.h> int main () { char checkword[80] = "password"; char password[80] = ""; do { printf ("Enter password: "); scanf("%s", password); } while (strcmp(password, checkword)); return 0; } In cazul in care parola se va potrivi cu cea stocata in variabila checkword, functia strcmp (compara doua stringuri) va intoarce 0 si conditia de iesire din bucla se va evalua la fals, parasindu-se bucla. In cazul instructiunii do ... while la intanirea instructiunii continue nu se testeaza conditia (normal daca ne gandim ca executia se reia de la inceputul buclei si conditia se testeaza la sfarsitul buclei). In numarul urmator vom trece la prezentarea unor functii de prelucrare a sirurilor de caractere, detaliind modul in care sunt stocate acestea in memorie, apoi vom prezenta functiile disponibile pentru gestionarea de fisiere si directoare.

11

CAPITOLUL II Preliminarii

Relatia binar reprezint un instrument puternic, folosit n teoria limbajelor formale. De aceea mi propun s prezint n continuare principalele rezultate legate de aceast notiune, rezultate folosite n lingvistica matematic. Definitie: Fie A si B dou multimi pentru care se defineste produsul cartezian A x B. Se numeste relatie binar de la A la B o submultime R a produsului cartezian. n strns legtur cu aceast notiune se pot defini urmtoa-rele: Definitie: Se numeste domeniul relatiei R submultimea de-finit astfel: Dom(R)={ x A|( ) (x,y) R } Definitie: Se numeste imaginea relatiei R submultimea lui B definit astfel: Im(R)={ y B|( ) (x,y) R } Reprezentarea relatiilor se poate face sub forma unui tabel unde pe prima linie a tabelului se scriu elementele primei multimi iar sub ele elementele din a doua multime cu care sunt n relatie. n continuare este prezentat un exemplu de relatie si reprezentarea sa. Exemplu: Fie A={a1, a2, a3, a4} si B={b1, b2, b3} dou multimi. Produsul lor cartezian este AxB={(a1,b1); (a1,b2); (a1,b3); (a2,b1); (a2,b2); (a2,b3); (a3,b1); (a3,b2); (a3,b3); (a4,b1); (a4,b2); (a4,b3)}. n continuare se consider relatia: R={(a1,b2); (a1,b3); (a2,b3), (a3,b1); (a3,b2)}. Domeniul acestei relatii este Dom(R)={a1, a2, a3}, iar imaginea sa este Im(R)={b1, b2, b3}. a1 a2 a3 b1 a4
12

b2 b3

b2 b3

CAPITOLUL III Operatii cu relatii

Modurile de reprezentare prezentate anterior au dezavantajul de a nu se putea folosi n momentul n care se doreste demonstrarea unei teoreme sau reprezentarea relatiilor ntr-un limbaj de programare. Definitie: Fie A={a1, ..., an} si B={b1, ..., bm} dou multimi fini-te. Unei relatii R A x B i se poate asocia o matrice M cu n linii si m coloane, numit matricea boolean atasat relatiei. Elementul Mi,j aflat pe linia i si coloana j, al acestei matrici este dat 1 dac (ai,bj) R si 0 dac (ai,bj) R. Exemplu: Matricea boolean atasat relatiei de la exemplul anterior este urmtoarea:
0 0 1 0 1 0 1 0 1 1 0 0

Reprezentarea relatiilor n limbajele de programare uzuale se poate face prin urmtoarele modalitti: (1) Se dau N si M care sunt cardinalele celor dou multimi A si B si matricea boolean atasat relatiei, matrice care se reprezint prin intermediul unui tablou bidimensional. (2) Se dau N si M care sunt cardinalele celor dou multimi A respectiv B si n plus pentru fiecare element din A se d lista elementelor din B cu care se afl n relatie. Implementarea acestei liste se face astfel: n tabloul tab care are N intrri, se memoreaz pentru fiecare element i din A adresa (adr) unde ncepe lista de elemente si numrul (nr) elementelor j din B cu care i se afl n relatie. Aceste elemente se memoreaz n tabela sir ntre adresele adr si adr+nr-1. Reprezentarea relatiei se face deci prin intermediul unor structuri care au forma de mai jos:
13

Exemplu: Structurile oar au forma: tab 1 1 2 2 3 1 3 4 2 2 4 6 0 1

folosite pentru a reprezenta relatia anteri-

sir 3 3 1 2 2 3 4 5

(3) Reprezentarea este analoag cu cea prezentat la punctul (2), cu deosebirea c locul elementelor multimii A este luat de ele-mentele multimii B si reciproc. Exemplu: Structurile de date folosite n acest caz pentru a reprezenta relatia anterioar au forma: tab 1 1 1 2 2 2 3 4 2 sir 3 1 3 1 2 1 2 3 4 5

(4) Se dau N si M care sunt cardinalele celor dou multimi, P care reprezint numrul de perechi aflate n relatie, precum si o list a perechilor din relatie. Exemplu: Structurile de date care reprezint relatia anterioar au forma: 1 2 3 4 5 1 1 2 3 3 2 3 3 1 2

Cea mai simpl problem care trebuie rezolvat n acest mo-ment este dac se poate decide asupra existentei relatiei ntre dou elemente i si j si dac da, care este algoritmul care rezolv aceast problem. n ceea ce urmeaz ne vom referi la relatii definite pe multimi finite, ca atare prima parte a problemei este rezolvat. n continuare se prezint pe rnd algoritmul care solutioneaz cea de a doua parte a problemei, pentru cele patru modalitti de reprezentare.
14

Algoritm: Se decide dac dou elemente se afl n relatia dat sau nu. Intrare. O relatie reprezentat sub forma 1 si dou elemente x A si y B. Iesire. Iesirea este TRUE n cazul n care x R y, FALSE n caz contrar. Metoda. Algoritmul este prezentat mai jos n pseudocod. Cea mai important structur de date este matricea boolean. functia APARTINE(R,x,y) este + dac R(x,y)=1 atunci | APARTINE<-TRUE | altfel | APARTINE<-FALSE + sfrsit Algoritm: Se decide dac dou elemente se afl n relatia dat sau nu. Intrare. O relatie reprezentat sub forma 2 si dou elemente x A si y B. Iesire. Iesirea este TRUE n cazul n care x R y, FALSE n caz contrar. Metoda. Algoritmul este prezentat mai jos n pseudocod. Cele mai importante structuri de date sunt tab si sir. functia APARTINE(tab,sir,x,y) este k<-tab(x).adr gsit<-FALSE adrf<-k+tab(x).nr-1 + ct_timp k<=adrf si gsit=FALSE repet | + dac sir(k)=y atunci | | gsit<-TRUE | | altfel | | k<-k+1 | + + APARTINE<-gsit sfrsit
15

Algoritm: Se decide dac dou elemente se afl n relatia dat sau nu. Intrare. O relatie reprezentat sub forma 3 si dou elemente x A si y B. Iesire. Iesirea este TRUE n cazul n care x R y, FALSE n caz contrar. Metoda. Algoritmul este prezentat mai jos n pseudocod. Cele mai importante structuri de date sunt tab si sir. functia APARTINE(tab,sir,x,y) este k<-tab(y).adr gsit<-FALSE adrf<-k+tab(y).nr-1 + ct_timp k<=adrf si gsit=FALSE repet | + dac sir(k)=x atunci | | gsit<-TRUE | | altfel | | k<-k+1 | + + APARTINE<-gsit sfrsit Algoritm: Se decide dac dou elemente se afl n relatia dat sau nu. Intrare. O relatie reprezentat sub forma 4 si dou elemente x A si y B. Iesire. Iesirea este TRUE n cazul n care x R y, FALSE n caz contrar. Metoda. Algoritmul este prezentat mai jos n pseudocod. Cea mai important structur de date este lista perechilor R. functia APARTINE(R,P,x,y) este gsit <-FALSE k<-1 + ct_timp k<=P si gsit=FALSE repet | + dac R(k)=(x,y) atunci | | gsit<-TRUE | | altfel
16

| | k<-k+1 | + + APARTINE<-gsit sfrsit Observatie: Atunci cnd au fost proiectati algoritmii s-a considerat c datele folosite de functia APARTINE sunt corecte. S considerm trei submultimi X,Y,Z ale lui A. n aceste condi-tii se pot defini urmtoarele notiuni. Definitie: Fie R1 A x B si R2 B x C dou relatii binare. Se defineste compusul lui R1 si R2 astfel: R1R2={ (x,z) | (x,y) R1 si (y,z) R2 pentru y B} Observatii: 1. Avem deci R1,R2 B x C; 2. n particular dac R X x X se poate defini R0={ (x,x) | x X } si Rn= Rn-1R. Teorem: Fie A={a1, ..., am}, B={b1, ..., bn} si C={c1, ..., cp} trei multimi finite de cardinal m, n respectiv p, R1 A x B si R2 B x C dou relatii binare si M1 si M2 matricele booleene atasate lor. n aceste conditii matricea boolean atasat relatiei R1xR2 este M=M1xM2 unde M(i,j)=M1(i,1) M2(1,j) ...M1(i,n) M2(n,j) cu i {1, ..., m} si j {1, ..., p} unde operatiile sunt definite mai jos: 0 1 0 0 1 1 1 1 0 1 0 0 0 1 0 1

Demonstratie. Pentru a demonstra teorema trebuie s artm c: 1) pentru orice (ai,cj) R1R2 avem M(i,j)=1 si 2) pentru orice (i,j) pentru care M(i,j)=1 avem (ai,cj) R1R2. 1) Deoarece (ai,cj) R1R2, rezult din definitie c exist un k {1, ..., n} astfel nct (ai,bk) R1 si (bk,cj) R2. Aceasta nseamn c M1(i,k)=1 si M2(k,j)=1 deci si M1(i,k) M2(k,j)=1, deci si M(i,j)=1.

17

2) Reciproc, dac M(i,j)=1, nseamn c exist cel putin un k {1, ..., n} pentru care avem M1(i,k)=1 si M2(k,j)=1. Deci avem (ai,bk) R1 si (bk,cj) R2. Ca atare avem (ai,cj) R1R2. Propozitie: Fie A o multime finit si R o relatie pe ea. n aceste conditii (x,y) Ri dac si numai dac exist z0, ..., zi astfel nct x=z0, (zj, zj+1) R pentru 0 j< i si zi=y. Demonstratie: Vom proceda prin inductie dup i. Pentru i=0 propozitia este adevrat. Considerm propozitia adevrat pentru i si o demonstrm pentru i+1. Dar, avem (x,y) Ri+1 dac si numai dac exist z din A astfel nct (x,z) Ri si (z,y) R. Deoarece propozitia este adevrat pentru i exist z0, ..., zi astfel nct x=z0, (zj, zj+1) R pentru 0 j< i si zi=z. Dac se consider zi+1=y lema este demonstrat.

18

CAPITOLUL IV Reflexivitate
Vom examina n cele ce urmeaz anumite proprietti ale unei relatii, proprietti care reflect relatia unui element cu el nsusi. Situatiile care pot aprea sunt sintetizate n definitiile ce urmeaz. Definitie: Fie A o multime si R o relatie definit pe aceast multime R A x A. Aceast relatie este reflexiv dac este adevrat relatia: ( ( ) x A) ( (x,x) R). Algoritm: Se decide dac o relatie este reflexiv. Intrare. O relatie binar R definit pe multimea A. Iesire. Iesirea este TRUE dac relatia este reflexiv, FALSE n caz contrar. Descriere. n continuare se prezint algoritmul n pseudocod. functia REFLEXIVA(R) este REFLEXIVA<-FALSE + pentru ( ) x A repet | + dac (x,x) R atunci | | REFLEXIVA<-TRUE | + + sfrsit Algoritm: Se decide dac o relatie este ireflexiv. Intrare. O relatie binar R definit pe multimea A. Iesire. Iesirea este TRUE dac relatia este ireflexiv, FALSE n caz contrar. Descriere. n continuare se prezint algoritmul n pseudocod. functia IREFLEXIVA(R) este IREFLEXIVA<-TRUE + pentru ( ) x A repet | + dac (x,x) R atunci
19

| | IREFLEXIVA<-FALSE | + + sfrsit

CAPITOLUL V Simetrie
Un alt set de proprietti ale unei relatii se refer la prezenta unor perechi de forma (x,y) si (y,x) n relatia R. Situatiile care pot aprea sunt sintetizate n urmtoarele definitii. Definitie: Fie A o multime si R o relatie definit pe aceast multime, R A x A. Aceast relatie este simetric dac este adevrat relatia: ( ( ) (x,y) R, x y) ( (y,x) R). Algoritm: Se decide dac o relatie este simetric. Intrare. O relatie binar R definit pe multimea A. Iesire. Iesirea este TRUE dac relatia este simetric, FALSE n caz contrar. Descriere. n continuare se prezint algoritmul n pseudocod. functia SIMETRICA(R) este SIMETRICA<-TRUE + pentru ( ) x A repet | + pentru ( ) y A repet | | + dac (x,y) R si x y atunci | | | + dac (y,x) R atunci | | | | SIMETRICA<-FALSE | | | + | | + | + + sfrsit Definitie: O relatie R definit pe o multime A este antisimetric n sens larg dac este adevrat relatia: ( ( ) (x,y) R si (y,x) R) (x=y). Algoritm: Se decide dac o relatie este antisimetric n sens larg Intrare. O relatie binar R definit pe multimea A.
20

Iesire. Iesirea este TRUE dac relatia este antisimetric n sens larg, FALSE n caz contrar. Descriere. n continuare se prezint algoritmul n pseudocod. functia ANTISIMETRICA_LARG(R) este ANTISIMETRICA_LARG<-TRUE + pentru ( ) x A repet | + pentru ( ) y A repet | | + dac (x,y) R si (y,x) R atunci | | | + dac x y atunci | | | | ANTISIMETRICA_LARG<-FALSE | | | + | | + | + + sfrsit Definitie: Fie A o multime si R o relatie definit pe aceast multime R A x A. Aceast relatie este antisimetric n sens strict dac este adevrat relatia: ( ( ) (x,y) R, x y) ( (y,x) R). Algoritm: Se decide dac o relatie este antisimetric n sens strict. Intrare. O relatie binar R definit pe multimea A. Iesire. Iesirea este TRUE dac relatia este antisimetric n sens strict, FALSE n caz contrar. Descriere. n continuare se prezint algoritmul n pseudocod. functia ANTISIMETRICA_STRICT(R) este ANTISIMETRICA_STRICT<-TRUE + pentru ( ) x A repet | + pentru ( ) y A repet | | + dac (x,y) R si x=y atunci | | | ANTISIMETRICA_STRICT<-FALSE | | + | + + sfrsit

21

CAPITOLUL VI Tranzitivitate

O alt clas de proprietti importante care se refer la relatiile binare, este cea legat de tranzitivitate. Situatiile posibile sunt sintetizate mai jos. Definitie: O relatie R definit pe multimea A este tranzitiv dac relatia urmtoare este adevrat: ( ( ) (x,y) R si (y,z) R cu x y z) ( (x,z) R). Algoritm: Se stabileste dac o relatie de ordine este tranzitiv. Intrare. O relatie binar R definit pe multimea A. Iesire. Iesirea este TRUE dac relatia este tranzitiv, FALSE n caz contrar. Descriere. Algoritmul este descris mai jos n pseudocod. functia TRANZITIVA este TRANZITIVA<-TRUE + pentru ( ) x A repet | + pentru ( ) y A si y x repet | | + dac (x,y) R atunci | | | + pentru ( ) z A cu z x si z y repet | | | | + dac (y,z) R atunci | | | | | + dac (x,z) R atunci | | | | | | TRANZITIVA<-FALSE | | | | | + | | | | + | | | + | | + | + + sfrsit
22

Definitie: Fiind dat o relatie R definit pe o multime A, aceasta este netranzitiv dac este ndeplinit proprietatea: ( ( ) (x,y) R si (y,z) R) ( (x,z) R)

CAPITOLUL VII Relatii de ordine

Vom examina n cele ce urmeaz un exemplu de relatie binar numit relatie de ordine, relatie care genereaz notiunea de prioritate, de important relativ, de anterioritate, de superioritate, de clasament. Notiunile principale sunt prezentate mai jos: Definitie: Se spune c o relatie binar definit pe A x A este o relatie de preordine pe A dac este reflexiv si tranzitiv. Definitie: Se spune despre o relatie binar definit pe A x A c este o relatie de ordine nestrict pe A dac ea posed urmtoarele proprietti: - reflexivitate; - tranzitivitate; - antisimetrie n sens larg. Algoritm: Se stabileste dac o relatie este de ordin nestrict sau nu. Intrare. O relatie binar R definit pe multimea A. Iesire. Iesirea este TRUE dac relatia este de ordine nestrict, FALSE n caz contrar. Descriere. Algoritmul este descris mai jos n pseudocod. functia ORDINE_NESTRICTA(R) este ORDINE_NESTRICTA<-REFLEXIVA(R) si SIMETRICA(R) si ANTISIMETRICA_LARG(R) sfrsit Definitie: O relatie binar definit pe A x A este o relatie de ordine strict pe A dac este tranzitiv si strict antisimetric.

23

Ca si n cazul antisimetriei de multe ori nu se face distinctie ntre relatiile de ordine nestrict si relatia de ordine. De aceea n continuare, cnd va fi vorba de relatie de ordine se va face referire de fapt la relatia de ordine nestrict. Algoritm: Se stabileste dac o relatie este de ordine strict sau nu. Intrare. O relatie binar R definit pe multimea A. Iesire. Iesirea este TRUE dac relatia este de ordine strict, FALSE n caz contrar. Descriere. Algoritmul este descris mai jos n pseudocod. functia ORDINE_STRICTA(R) este ORDINE_STRICTA<-TRANZITIVA(R) si ANTISIMETRICA_STRICT(R) sfrsit Definitie: Se spune despre o multime A c este ordonat atunci cnd pe A s-a definit o relatie de ordine. Definitie: Se spune c o relatie de ordine pe A este o relatie de ordine total, dac pe lng propriettile relatiei de ordine, mai este verificat si urmtoarea proprietate: ( ( ) (x,y) A x A) ( (x,y) R sau (y,x) R). Algoritm: Se stabileste dac o relatie este de ordine total sau nu. Intrare. O relatie binar R definit pe multimea A. Iesire. Iesirea este TRUE dac relatia este de ordine total, FALSE n caz contrar. Descriere. Algoritmul este descris mai jos n pseudocod. functia ORDINE_TOTALA(R) este ORDINE_TOTALA<-TRUE + pentru ( ) x A repet | + pentru ( ) y A repet | | + dac ( (x,y) R) si ( (y,x) R) atunci | | | ORDINE_TOTALA<-FALSE | | + | + + ORDINE_TOTALA<-ORDINE_TOTALA si
24

ORDINE_NESTRICTA(R) sfrsit Definitie: Se spune c o relatie de ordine pe A este o relatie de ordine partial pe A, dac ea nu este o relatie de ordine total, deci este valabil relatia: ( ( ) (x,y) A x A) ( (x,y) R si (y,x) R). Algoritm: Se stabileste dac o relatie este de ordine partial sau nu. Intrare. O relatie binar R definit pe multimea A. Iesire. Iesirea este TRUE dac relatia este de ordine partial, FALSE n caz contrar. Descriere. Algoritmul este descris mai jos n pseudocod. functia ORDINE_PARTIALA(R) este ORDINE_PARTIALA<-FALSE + pentru ( ) x A repet | + pentru ( ) y A repet | | + dac ( (x,y) R) si ( (y,x) R) atunci | | | ORDINE_PARTIALA<-TRUE | | + | + + ORDINE_PARTIALA<-ORDINE_PARTIALA si ORDINE_NESTRICTA(R) sfrsit

25

CAPITOLUL VIII Echivalent

n continuare dorim s prezentm cteva lucruri legate de partitiile definite pe o multime precum si despre legtura lor cu relatiile de echivalent. Definitie: Fie A o multime finit. O partitie a sa se defineste ca fiind un set de submultimi A1, ..., Ak pentru care sunt adevrate propriettile: (1) A1 ... Ak= A (2) Ai Aj= ( ) (1 i, j k si i j) Definitie: O relatie binar definit pe A x A este de echivalent dac are urmtoarele proprietti: - reflexiv; - simetric; - tranzitiv. Teorema: Orice relatie de echivalent definit pe o multime determin pe aceast multime o partitie si reciproc, orice partitie determin o relatie de echivalent. Demonstratie: Fie A o multime finit si R o relatie de echivalent definit pe A. Se consider multimile Bi={x A | (a,x) R} pentru care demonstrm propriettile urmtoare: 1) pentru orice dou multimi B1 si B2 care nu sunt identice avem B1 B2= 3) UBi=A Pentru a demonstra prima proprietate procedm prin reducere la absurd. Presupunem c propozitia B1 B2= nu este adevrat, deci presupunem c propozitia B1 B2 este adevrat, ceea ce nseamn c cele dou multimi au un element comun, adic z B1 si z B2. Deci (a1,z) R si (a2,z) R. Deoarece relatia este de echiva-lent
26

rezult c a2 B1, deci cele dou multimi sunt identice, ceea ce contrazice ipoteza. Cea de a doua proprietate este evident deoarece a1 B1, fapt ce rezult din reflexivitatea relatiei. n concluzie, dac se consider doar acele clase de echivalent care sunt distincte, acestea formeaz o partitie a lui A. Reciproc, s considerm o partitie oarecare a multimii A determinat de setul de submultimi A1, ..., Ak pentru care avem: 1) A1 ... Ak=A 2) Ai Aj= ( ) 1 i, j k cu i j. n aceste conditii se defineste relatia R astfel ( (x,y) R) ( (( )j, 1 i, j k) (x,y) Aj) ). Este evident c relatia este de echivalent, deci teorema a fost demonstrat. n continuare este prezentat algoritmul care construieste partitia determinat de o relatie de echivalent. Algoritm: Construirea claselor de echivalent determinate de o relatie. Intrare. O relatie de ordine R reprezentat sub una din formele prezentate anterior. Iesire. Partitia determinat de relatia de echivalent. Metoda. Algoritmul foloseste pentru reprezentarea partitiei determinat de relatia de echivalent un tablou part care contine pe pozitia i cel mai mic element care se afl n relatia R cu elementul i. Algoritmul este descris n pseudocod mai jos: procedura PART(R,part) este: + pentru i=2,n,1 repet | + pentru j=i-1,1,-1 repet | | + dac (i,j) R atunci | | | part(j)<-i | | + | + + sfrsit

27

CAPITOLUL IX Problem rezolvat

n continuare este prezentat programul C care verific dac o relatie este reflexiv sau ireflexiv. #include<stdlib.h> #include<stdio.h> #include<conio.h> #define DIM_A 20 #define DIM_B 20 #define DIM_SIR DIM_A*DIM_B struct {int adr,nr;} rel[DIM_A]; int sir[DIM_SIR]; int nr_a; void CITIRE(void) {int i; int j; int k; int nr_j; int adr=0; printf(Numarul de elemente din multimea A: ); scanf(%d,&nr_a); if (nr_a>DIM_A) {printf(Multimea A are prea multe elemente\n); exit(1); } for (i=1,i<=nr_a;i++) {printf(Numarul de elemente cu care %d se afla in relatie: ,i); scanf(%d,&nr_j); rel[i-1].adr=adr;
28

rel[i-1].nr=nr_j; printf(Elementele sunt: ); for (j=1;j<=nr_j;j++) {scanf(%d,&k); if (adr>DIM_SIR) {printf(Relatia are prea multe elemente\n); exit(1); } sir[adr++]=k; } } } void SCRIERE(void) {int i; int j; int adr=0; int nr_j; printf(\n); printf(Relatia este:\n); for (i=1;i<=nr_a;i++) {nr_j=rel[i-1].nr; for (j=1;j<=nr_j;j++) printf((%d,%d)\n, i, sir[adr++]); } } int APARTINE(int i, int j) {int adr; int nr; int k; int gasit; adr=rel[i-1].adr; nr=rel[i-1].nr; gasit=0; for (k=0;k<nr;k++) if (sir[adr++]==j) {gasit=1; break; } return gasit; }
29

int REFLEXIVA(void) {int i; for (i=1;i<=nr_a;i++) if (!APARTINE(i,i)) return 0; return 1; } int IREFLEXIVA(void) {int i; for (i=1;i<=nr_a;i++) if(APARTINE(i,i)) return 0; return 1; } void main(void) {clrscr(); CITIRE(); SCRIERE(); if (REFLEXIVA()) printf(\nRelatia este reflexiva); else printf(\nRelatia este nereflexiva); if (IREFLEXIVA()) printf(\nRelatia este ireflexiva); else printf(\nRelatia este neireflexiva); getch(); }

30

CAPITOLUL X Probleme propuse

1. Fiind date dou relatii P si Q s se determine relatia obtinut prin compunerea lor. 2. S se scrie programul care determin relatia Rn, pornind de la R. 3. Fiind dat o relatie binar R pe o multime A, s se verifice dac este o relatie de preordine. 4. Fie R o relatie binar si A o multime finit. S se stabileasc dac relatia este de ordine partial. 5. S se scrie programul care verific dac o relatie este de ordine total sau nu. 6. Fiind dat o relatie R, s se stabileasc prin program dac relatia este de echivalent sau nu. 7. Fiind dat o relatie de echivalent R pe o multime A, s se determine partitia indus de ea pe aceast multime. 8. Fiind dat o partitie a unei multimi A, s se determine relatia de echivalent care poate genera aceast partitie.

31

32

33