Sunteți pe pagina 1din 50

Denumirea temei Subprograme. Funcii. Proceduri. Sintaxa declaraiilor i apelurilor de subprograme. Subprograme. Funcii. Proceduri. Domeniul de vizibilitate.

Comunicarea prin variabile globale. Efecte colaterale. Proceduri recursive. Variabile dinamice. Tipul referin. Structuri de date. Liste unidirecionale. Prelucrarea lor. Stiva. Arbori binari. Parcurgerea arborilor binari. Analiza algoritmilor. Iterativitate sau recursivitate. Metoda trierii (tehnica Greedy). Metoda relurii (tehnica backtracking). Metoda desparte i stpnete (tehnica divide et impera). Programarea modular. Testarea i depanarea programelor. Metodele de realizare a programelor mari. 1. 2.

lit 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 4 3 1 1 1

pag 144 144 158 161 163 167 176 176 180 181 197 206 213 129 54 139 123 97 238 245 249

Gremalschi A., Mocanu Iu., Spinei Ion. Informatica. Limbajul de programare PASCAL. Manual pentru clasele IX-XI., tiina, Chiinu, 2000 Cerchez Emanuela, erban Marinel. Informatica. Manual pentru clasa a X-a. Filiera teoretic, profilul matematic-informatic. Iai: Editura POLIROM, 2000. 199 p. Sorin T. Tehnici de programare. Bucureti Editura Teora. 1996.

3.

ANALIZA TIMPULUI DE CALCUL NECESAR ALGORITMILOR Noiuni generale


1

Ne propunem s lmurim modul n care se estimeaz timpul de calcul necesar unui program pentru a fumiza rezultatul. S considerm una din cele mai simple probleme. Se d un vector cu n componente. Se cere s se calculeze maximul dintre componentele sale. Reamintim, pe scurt, algoritmul: o variabil (numit MAX) va lua valoarea primei componente; fiecare din cele n-1 componente rmase, se compar pe rnd cu valoarea variabilei MAX, iar n cazul n care coninutul este mai mare dect al variabilei MAX, valabilei MAX i se atribuie valoarea acelei componente. Timpul de calcul depinde, n primul rnd, de lungimea (mrimea) datelor de intrare, pe care o vom nota w n. Exemple: => pentru aflarea maximului (minimului) n reprezint numrul de componente al vectorului; => pentru sortare, n reprezint numrul valorilor care se sorteaz; => pentru generarea permutrilor, n reprezint numrul de elemente ale mulimii pentru care se genereaz permutrile; => pentru testul unui numr natural dac este prim (sau nu) n este chiar numrul; => pentru problema colorrii hrilor, n este numrul arilor. Pentru a calcula timpul de calcul ar trebui sa inventariem toate instruciunile programului i s tim de cte ori se execut fiecare din ele (n funcie de n). Mai mult, ar trebui s cunoatem ct dureaz execuia fiecrui tip de instruciune. Observaie 1. => nu ntotdeauna putem ti de cte ori se execut o instruciune. chiar dac cunoatem valoarea lui n. Exemplu. Pentru calculul maximului, nu putem ti de cte ori se execut atribuirea max:=v[i] . Cu toate acestea, putem considera ca exist o proporionalitate ntre valoarea n i numrul de execuii. Observaie 2. => Viteza de execuie a unei instruciuni este dependent de calculatorul utilizat. Datorit acestor considerente vom proceda altfel. Se alege o operaie numit operaie de baz, i se vede de cte ori se execut aceasta. Cerina pentru operaia de baz este ca aceasta s se execute de un numr de ori, numr care se poate calcula de la nceput pornind de la n. Exemple. => Pentru problema aflrii maximului, operaia de baza va fi comparaia (fiind dat n se fac n-1 comparaii pentru calculul maximului). => Pentru generarea permutrilor, alegerea operaiei de baz este greu de fcut. De exemplu, dac aceasta este comparaia, este greu de calculat numrul comparaiilor efectuate pentru a genera toate permutrile. Din acest motiv putem considera ca operaie de baz generarea unei permutrii. Vom avea astfel n! operaii de baz (un numr foarte mare). Pentru generarea permutrilor exist foarte muli algoritmi. Alegerea ca operaie de baz a comparaiei permite ca acetia sa fie comparai ntre ei, dar alegerea ca operaie de baz a generrii unei permutri nu permite aceasta. n astfel de cazuri (cnd avem de ales ntre mai multe operaii de baz), vom alege acea operaie care corespunde ct mai bine scopului propus. Astfel, dac analizm un algoritm de generare a permutrilor, comparativ cu altele de aceeai natur vom alege ca operaie de baz comparaia. n cazul n care analizm un algoritm oarecare, n care soluia este sub form de permutare, putem considera ca operaie de baz permutarea. Problema se pune n felul urmtor: daca cutm soluia printre toate permutrile pe care se pot genera vom avea n! cutri (un numr imens), oare nu se poate altfel? Exemple de probleme la care se folosete un astfel de raionament: problema comis voiajorului, problema celor n dame. Timpul estimat de calcul se trece sub form aproximativ astfel: O (numr estimat de execuii ale operaiei de baz). Exemplu de exprimare a timpului. 2

Dac un algoritm efectueaz 3n2+7n+5 operaii de baz, vom spune c acesta este un algoritm cu O(n2). Exemple. => Pentru aflarea maximului dintre n valori timpul estimat de calcul este O(n). n cazul de fa, operaia aleas este cea de comparare. Pentru un vector cu n componente se fac n-1 comparaii. Aceasta nseamn c dac vectorul are 100 de componente se fac 99 de comparaii .a.m.d.. Se poate demonstra faptul c un algoritm mai bun nu exist. => Pentru generarea permutrilor (n cazul n care se consider ca operaie de baz generarea unei permutri) timpul estimat de calcul este O(n!). n anumite cazuri nu putem preciza nici mcar numrul de execuii ale operaiei da baza. Exemplu. Sortarea prin interschimbare (vezi 6.2.1). n cazul n care vectorul este sortat se exercit n-1 comparaii (se parcurge vectorul o singur dat). Dac vectorul este sortat invers se execut n(n-1)/2 operaii de baz (se parcurge vectorul de n ori). n astfel de cazuri se poate considera: timp minim de calcul: timp mediu de calcul; timp maxim de calcul. De fiecare dat cnd se trece timpul estimat se precizeaz despre care este vorba (minim mediu, maxim). 1n practic, prezint interes timpul mediu i maxim. Dac timpul estimat este sub forma O(nk), kN, spunem c algoritmul este n timp polinomial. Exemplu. Sortarea prin interschimbare are timpul (mediu, maxim) polinomial si anume O(n2). Un algoritm n O(n) se numete algoritm liniar. Dac un algoritm are un timp estimat O(2n), O(3n) spunem c algoritmul este n timp exponenial.

Un algoritm n timp O(n!) este asimilat unui algoritm n timp exponenial. n!=1x2x...n<2x2x..x2=2n-1. n practic nu sunt admii dect algoritmii n timp polinomial, dei nu este deloc indiferent gradul polinomului. i totui pentru ce tot acest efort de estimare a timpului de calcul? Oare viteza de lucru a. unui calculator nu este att de mare, nct abstracie fcnd de cteva minute de calcul, n plus sau n minus, sa renunm la estimarea lui? Rspunsul la aceasta ntrebare este categoric negativ. Mai mult, timpul de calcul este esenial, dac este prea mare algoritmul nu are nici un fel de valoare practic. S ne imaginm un algoritm care are un timp de calcul O(2 n). Dac n este 10, calculatorul va efectua aproximativ 1024 operaii elementare. Dac n este 100 acesta va trebui s efectueze 1024*1024*..-*1024 operaii elementare, adic un numr mai mare dect 1000*1000*...*1000 adic 1000000...0000 (de treizeci de ori). Acesta este un numr imens. Dar dac n este 1000? Pentru un n nu foarte mare, nici cel mai modern calculator din lume nu scoate rezultatul dect n sute sau mii de ani). De altfel ce nseamn n asemenea cazuri un calculator modern? Sa presupunem c un sistem PENTIUM are p vitez de circa 25 de ori mai mare dect a unul sistem XT. S presupunem c avem un program care are un timp de calcul de ordinul O(2 n). Se cere s rezolvm o problem pentru n=30. Fie t timpul necesar rulrii acestei probleme pe un XT. Aceeai problem va fi rulat pe PENTIUM n timpul t/25. Dorim s rezolvm problema pe PENTIUM pentru n=35 (n a crescut numai cu 5). Dar 2 35=230*25=32*230. Deci dac n a crescut cu 5, vom avea nevoie de mai mult timp de lucru sa rezolvm problema pe PENTIUM dect timpul pentru rezolvarea ei pentru n=30 pe XT. V dai seama ce nseamn un timp de calcul de ordinul O(2 n)? Sporul lui n cu o singur unitate duce la dublarea timpului de calcul. Ce concluzie tragem de aici? De cte ori avem de rezolvat o problem cutm pentru aceasta un algoritm n timp polinomial (polinomul va avea gradul minim). Mai mult, cutm un algoritm care s rezolve problema n timp polinomial de grad minim. 3

Exemple Sortarea prin interschimbare


Se consider un vector cu n componente. Se cere s se estimeze timpul de calcul al algoritmului de sortare prin interclasare. Pe scurt, algoritmul este urmtorul: se face o parcurgere a vectorului, n care se inverseaz elementele alturate care nu ndeplinesc relaia de ordine considerat; dac n parcurgerea efectuat se face cel puin o inversare, algoritmul se reia de la punctul anterior, altfel s-a obinut rezultatul dorit. n cazul n care dorim s estimm timpul de calcul necesar acestui algoritm i considerm ca operaie de baza comparaia, ajungem la o dilem: dac vectorul este gata sortat facem o singur parcurgere a acestuia i n-1 comparaii ( chiar dac au fost inutile); n cazul cel mai defavorabil, corespunztor situaiei n care vectorul este iniial sortat descresctor, se fac n-1 parcurgeri ale sale n care se fac inversri i una fr inversri, deci se fac n parcurgeri, iar pentru fiecare parcurgere se fac n-1 comparaii. n concluzie se fac n(n-1) comparaii. Iat c i datele de intrare influeneaz n multe cazuri timpul de calcul i aceasta n mod semnificativ. Vom avea : timp minim O(n), timp mediu sau maxim O(n 2). Datorit aproximailor se consider c timpul mediu este egal cu cel maxim.

Problema sumei divizibile cu n


Vom reveni la o problem prezentat n manualul de clasa a noua. Se consider un vector cu n numere naturale. Se cere s se listeze cteva din acestea, a crui sum se divide cu n (dup cum va rezulta din demonstraie. acestea exist ntotdeauna!). O analiz superficial a problemei va conduce la un astfel de algoritm: considerm toate submulimile mulimii celor n numere naturale; pentru fiecare din ele testm dac suma elementelor se divide la n, iar cnd am gsit o astfel de submulime, algoritmul se oprete. Categoric, aceasta este o rezolvare. Mai mult, este o rezolvare corect. Cineva care analizeaz ns timpul de rezolvare al problemei va observa c avem 2n submulimi ale unei mulimi considerate. Pentru toate acestea se face testul dac suma elementelor se divide prin n. Timpul de calcul este de ordinul O(2 n). Apare ca fireasc urmtoarea ntrebare: dar daca gsim o astfel de submulime printre primele generate? ntr-adevr exist i aceast posibilitate, dar exist i ansa ca o astfel de submulime s fie printre ultimele generate i atunci nu obinem un rezultat n timp util. Aa cum a fost artat, o abordare serioas a acestei probleme const n a forma pe rnd sumele: S1=x1; S2=x1+x2; Sn=x1+x2++xn. Dac suma Si se mparte la n, atunci numerele cutate sunt x 1, x2, .... xi. Dac nici una nu se mparte la n, innd cont de faptul c restul acestor sume prin mprirea la n aparine mulimii {1,..,n}, (avem n-1 posibiliti de rest), rezult c exist dou sume care dau acelai rest prin mprirea la n. Diferena acestor doua sume se va divide prin n. Fie S i i Sj aceste sume (i). Si= x1+x2++xi; Sj= x1+x2++xj; Si-Sj=xi+1++xj. 4

Cum estimm timpul de calcul n astfel de situaii? Considerm ca operaie de baz comparaia (a dou resturi). Revenim la problem. Dispunem de n resturi. Cutm un rest nul. Pentru aceasta se fac cel mult n comparaii. Presupunem c nici un rest nu este nul. Urmeaz s identificm dou resturi egale. Pentru aceasta se compar primul rest cu resturile 2 ... n (n-1 comparaii), al doilea cu resturile 3 ... n (n-2 comparaii), ... penultimul rest cu ultimul (o singura comparaie). n concluzie, se efectueaz cel mult n(n-1)/2 comparaii, n total s-au efectuat n+n(n+1)/2 comparaii. Avem un algoritm cu O(n 2) timp maxim de calcul. Este recomandabil s realizai acest program n ambele variante i s comparai timpul de calcul pentru n=30 ( o valoare mic pentru n).

Probleme pentru care nu se cunosc algoritmi polinomiali de rezolvare


Fiind dat o anumit problem, se pune ntrebarea: exist un algoritm de rezolvare al ei polinomial? La aceast ntrebare se poate rspunde n felul urmtor exist probleme pentru care nu se cunosc algoritmi de rezolvare n timp polinomial. Matematicienii, oamenii foarte serioi, nu au avut curajul s fac o afirmaie de genul nu exist algoritmi polinomiali de rezolvare a acestei probleme pentru c o astfel de afirmaie presupune o demonstraie, care nu s-a fcut. Faptul c nu s-a gsit un algoritm polinomial nu nseamn c el nu exist. S analizm o astfel de problem. Problema satisfacerii (problema problemelor). Se consider o funcie boolean cu n variabile (x 1, x2,xn), dat n form canonic conjunctiv. Se cere un sistem de valori x1, x2,xn astfel nct, pentru acest sistem funcia s ia valoarea TRUE. Orict ne strduim s procedam altfel, trebuie s ncercm toate sistemele de valori pentru a vedea ce valoarea ia funcia, dar avem 2n astfel de sisteme. n concluzie, pentru aceast problem avem timpul de calcul O(2 n). Mai mult, se demonstreaz c alte probleme se reduc la aceasta (exist algoritmi n timp polinomiali care transform o alt problem n PROBLEMA SATISFACERII). Dac am fi capabili s rezolvm aceasta problem n timp polinomial, am rezolva n timp polinomial o ntreag clas de probleme care se reduc la aceasta. Exemple de probleme care se reduc la problema satisfacerii: problema colorrii hrilor; problema comis voiajorului; problema celor n dame; problema rucsacului cazul discret (o vom studia). O mulime de probleme cerute de practic se reduc la problema satisfacerii (de aici i numele de problem a problemelor). V dai seama ce semnificaie au cele prezentate? nseamn c nu se poate (pe moment cel puin) rezolva orice cu ajutorul calculatorului electronic. Mai mult, perfecionrile tehnologice aduse acestor maini, orict de spectaculoase vor fi, nu vor rezolva aceast problem. Ce se poate face, n absena unei rezolvri n timp polinomial, pentru aceast categorie de probleme? n multe cazuri se renun la optimalitatea unei soluii (obin o soluie bun, dar nu cea mai bun) cu condiia ca acestea s fie obinute n timp polinomial. Astfel de metode se numesc EURISTICE i vor fi prezentate n cadrul acestui manual. Nu este deloc uor s se imagineze astfel de metode. Mai ales, este foarte dificil de artat ct de departe de soluia optim este soluia gsit. Au aprut metode noi de cercetare, pentru obinerea unor soluii ct mai bune, chiar dac nu optime: Greedy euristic, algoritmi genetici, algoritmi neuronali, algoritmi de tip clire etc. Astfel de probleme sunt de cea mai mare actualitate n informatica teoretic pe plan mondial. O parte dintre ele le vom aborda chiar n aceasta lucrare.

Timpul de calcul necesar tehnicii Backtracking.


Tehnica BACKTRACKING pleac de la o premiz de bun sim si anume: dac la un moment dat, nu mai am anse s ajung la soluia cutat, renun sa continui cutarea cu o valoarea pentru care tiu c nu ajung la rezultat.

S presupunem c fiecare nivel al stivei ia valori ntre 1 si n. S presupunem, de asemenea, c stiva are n nivele. O explorare sistematic presupune a investiga n n posibiliti. Mecanismul tehnicii evit (ct poate) investigarea tuturor soluiilor. Dar de aici nu se poate trage concluzia ca timpul de calcul mediu nu este exponenial. Dei este dificil de demonstrat timpul de calcul necesar tehnicii Backtracking este asimilat timpului exponenial. n cazul n care problema nu are soluie, se exploreaz toate posibilitile, caz n care se ajunge la un timp de calcul exponenial. Nu este exclus ca prin aceasta tehnic s se ajung imediat la soluie, dar pentru aceasta trebuie s avem "ans. Oricum, timpul maxim cerut de aceasta tehnic este exponenial. Din acest motiv, se va evita folosirea ei n rezolvarea problemelor. n situaia n care se cere soluia optim la o problem pentru care nu se cunosc algoritmi polinomiali, i nu se renun sub o nici o form la optimalitate (n favoarea unei soluii suficient de bune) putem folosi tehnica Backtracking, altfel aceasta nu se va folosi.

Timpul de calcul necesar tehnicii Divide et impera


Tehnica DIVIDE ET IMPERA conduce, n general la un timp de calcul polinomial. Se tie din studiul acestei tehnici ca, la fiecare descompunere se obin probleme cu un grad de complexitate mai mic (n scade cu o unitate, n cazul cel mai nefavorabil). ntruct multe din aceste probleme (vezi problema cutrii binare, problema sortrii prin interclasare) se mpart n doua probleme de aceeai lungime (n/2), vom studia acest din urm caz. Dac notam cu T(n) timpul necesar rezolvrii unei astfel de probleme cu n cunoscute, acesta se poate calcula astfel:

a, daca n = 1; T (n) = 2T (n / 2) + bn , daca n > 1.


Ce semnificaie are aceast relaie? n cazul n care n=1 problema necesita un timp de calcul a. n caz contrar, avem de rezolvat dou probleme de "lungime" n/2. De asemenea, timpul necesar combinrii soluiilor celor dou probleme l vom nota cu b n (n definitiv, atunci cnd se combin rezultatele avem rezolvat o problem de "lungime" n). S aplicm acest rezultat sortrii prin interclasare. Estimm timpul necesar interclasrii. S presupunem c cei doi vectori care trebuiesc interclasai cu m si n componente. Conform algoritmului de interclasare, la fiecare pas n urma unei comparaii, coninutul uneia din componente era trecut n vectorul rezultat C. Se proceda n acest fel, pn cnd unul din vectori este trecut n ntregime n C. Rezult de aici faptul c n cazul interclasrii se fac cel mult m+n+1 comparaii. Dac vectorul are p componente, rezult c putem estima timpul de calcul prin O(p). Iat c algoritmul de interclasare este unul liniar (extrem de performant). Cum va fi atunci sortarea prin interclasare? Pentru simplificarea calcului, s presupunem c n este de forma 2 k (n caz contrar mrim pe n pn ajungem la o valoare de aceasta form). n cazul n care nu se obin descompunerile (descompunem fiecare problem pn la n=1):

Acest arbore are k niveluri (k=log2(n)). Timpul de calcul va fi: T(8)=2T(4)+b8=2(2T(2)+b4)+b8=4T(2)+2b4+b8=4(2T(1)+b2)+2b4+b8 =8T(1)+4b2+2b4+b8=8a+4b2+2b4+b8. Dup cum am vzut, bk reprezint timpul unei interclasri n care vectorul rezultat are k componente. n acest caz, aproximnd superior, putem consider bk=k. Rezult: T(8)=8a+8+8+8=8a+8*3=8a+8log2(8). Generaliznd problema (propunem demonstraie ca exerciiu) vom avea: T(n)=na+nxlog2(n). Rezult c algoritmul de sortare prin interclasare necesit un timp de calcul O(n xlog2(n)). La algebr am nvat c log2(n)<n, aceast inegalitate se demonstreaz prin inducie. n cadrul acestui capitol s-a artat faptul c, sortarea prin interschimbare (metoda bulelor) necesit un timp de calcul O(n2). innd cont de ultima inegalitate, rezult ca algoritmul de sortare prin interclasare este mai bun. Mai mult, se demonstreaz i faptul c timpul cerut de acest algoritm este cel mai mic timp necesar unei operaii de sortare care au acelai timp estimat de calcul. Pn acum am prezentat modul n care se estimeaz timpul de calcul al unui algoritm elaborat cu ajutorul tehnicii ntr-un caz simplu. De multe ori, problemele nu se descompun n alte dou probleme cu aceeai "lungime" ca aici, ci n trei, patru .a.m.d. n acest caz nu dispunem de un mecanism general de estimare a timpului, se studiaz fiecare caz n parte.
DIVIDE ET IMPERA,

Concluzii
Estimarea timpului necesar unui algoritm nu este simplu de realizat. S ne gndim la faptul ca i datele de intrare pot aprea cu o anumit probabilitate. Cum pentru un set de date se poate obine, un anumit timp (de multe ori inferior timpului estimat), rezult c timpul estimat trebuie judecat si prin prisma TEORIEI PROBABILITILOR i STATISTICII MATEMATICE. Toate acestea depesc cu mult nivelul de cunotine cerut n licee sau n majoritatea facultilor. Tehnicile care urmeaz s le nvm (PROGRAMARE DINAMIC i GREEDY) conduc, n general la un timp polinomial, iar tehnica Branch and Bound la unul exponenial.

Probleme propuse
1. Estimai timpul de calcul necesar aflrii valorii maxime i a celei minime dintr-un vector cu n componente numere reale (numerele nu sunt sortate). 2. Estimai timpul de calcul necesar problemei cutrii binare unui anumit numr natural ntr-un vector cu n componente, ce conin numere naturale ordonate. Comparai timpul de calcul cu metoda clasic (parcurgerea sistematic a vectorului). 3. Estimai timpul de calcul necesar generrii produsului cartezian a n mulimi finite. 4. Estimai timpul de calcul necesar pentru obinerea recursiv a lui fib(n) unde: fib(n)=fib(n-1)+fib(n-2) fib(0)=a; fib(1)=b. Comparai acest timp cu cel estimat pentru aceeai problem, n cazul rezolvrii iterative.

METODA BACKTRACKING Descrierea general a metodei


Deseori n practic apar probleme care implic alegerea unor soluii optime dintr-un spaiu extrem de vast de soluii posibile. Un teoretician pur" ar rspunde : nici o problem! construim toate soluiile posibile si le alegem apoi pe cele optime ! ". Dar este realist o astfel de abordare ? S analizm un caz foarte simplu: s presupunem c problema noastr implic selectarea unei mulimi de n elemente care trebuie s ndeplineasc anumite condiii. Pentru fiecare element i presupunem c exist pi posibiliti de alegere. A genera toate soluiile posibile nseamn de fapt a genera toate elementele produsului cartezian {1,2, . . . , p1} x {l, 2 , . . . , p2} x . . . x {l, 2 , . . . , pn}. Acest produs cartezian are p1xp2x . . . xpn elemente. Chiar dac vom considera c pi=2, 7

pentru orice i = l, n tot obinem 2 n soluii posibile. E mult? S evalum cu aproximaie timpul de execuie a unui program bazat pe un astfel de algoritm, presupunnd c dispunem de un calculator care execut un miliard de operaii pe secund (109 operaii). n 40 50 100 Timp de execuie 109 secunde 31 ore 4.1013 ani

E puin probabil s putem atepta att! Prin urmare trebuie s abordm n alt mod astfel de probleme ! 0 idee ar fi s procedm ca n multe situaii din viaa de zi cu zi. S ne gndim la modul n care un copil rezolv un puzzle. Copilul nu face toate combinaiile posibile de piese pentru ca apoi s le compare cu modelul pe care vrea s l obin. El va lua mai nti o pies. Va cuta apoi n mulimea de piese rmase una care s se potriveasc la cea pe care o are, apoi nc una .a.m.d. Dac la un moment dat se blocheaz" (nu mai gsete nici o pies n cele rmase care s se potriveasc), el nu distruge tot ce a construit ca s o ia de la nceput. Va ndeprta mai nti ultima pies pe care a pus-o i va cuta o alternativ" (o alt pies care s-ar potrivi n locul ei). Dac gsete, continu construcia cu noua pies aleas. Dac nu gsete, se mai ntoarce un pas i ndeprteaz i penultima pies pe care a pus-o, cutnd apoi o alt variant. Procedeul continu pn cnd obine modelul dorit. Observai c pe parcursul construciei, copilul face anumite verificri (se potrivete piesa aici? m poate conduce la modelul dorit?), eliminnd astfel foarte multe dintre soluiile posibile. Cu alte cuvinte, cutarea nu este exhaustiv. Un alt aspect semnificativ este faptul c se fac reveniri. Dac la un moment dat nu mai poate continua construcia, copilul revine la pasul precedent, ndeprteaz piesa utilizat i ncearc s o nlocuiasc, dac este posibil. Dac nu este posibil, face reveniri succesive, pn cnd gsete o pies pe care o poate nlocui i apoi continu construcia. Acest mod de abordare se bazeaz pe principiul ncerc aa, dac nu merge m ntorc i ncerc o alt variant ! " Fr s tie, copilul din exemplu a aplicat metoda backtracking. Numele metodei este semnificativ i s-ar putea traduce prin a o lua napoi pe urme" sau, cu aproximaie, prin cutare cu revenire" S descriem acum ntr-un mod mai general metoda backtracking. n variant elementar, considerm c soluia problemei pe care trebuie s o rezolvm se poate reprezenta ca un vector x = (x1,x2,...xn) . Fiecare component xi a vectorului poate lua valori ntr-o anumit mulime Si (i=l , 2 ,..., n). Produsul cartezian S1xS2x . . . xSn se numete spaiul soluiilor posibile. Problemele care se rezolv prin backtracking nu impun generarea tuturor soluiilor posibile (generare exhaustiv), ci doar generarea acelor soluii care ndeplinesc anumite condiii, specifice problemei, denumite condiii interne. Soluiile posibile care respect condiiile interne sunt denumite soluii rezultat. Unele probleme impun obinerea unei singure soluii rezultat, i anume cea care ndeplinete o anumit condiie de optim. Aceasta este denumit soluie optim. Pentru a evita generarea tuturor soluiilor posibile, metoda backtracking atribuie pe rnd valori elementelor vectorului x. Mai exact, componenta xk primete o valoare numai n cazul n care componentele x1,x2, . . . xk-1 au primit deja valori. n acest caz, componentei xk i se atribuie pe rnd acele valori posibile (valori din mulimea Sk) care ndeplinesc condiiile de continuare. Condiiile de continuare sunt condiii derivate din condiiile interne, care stabilesc dac pentru o anumit valoare pentru xk are sau nu sens s continum construcia soluiei. Spunem c o anumit valoare pentru xk nu ndeplinete condiiile interne dac oricum am atribui valori componentelor xk+1 , . . . , xn nu obinem o soluie rezultat (soluie care s respecte condiiile interne). Dup atribuirea unei valori posibile care respect condiiile interne componentei xi se poate continua construcia soluiei n mod recursiv (de data aceasta sunt fixate k poziii). Pentru a descrie formatul general al procedurii backtracking vom utiliza o procedur BKT. Considerm c n, numrul de componente ale vectorului, i vectorul x sunt variabile globale. procedure BKT (k: integer); {cnd apelam procedura BKT cu parametrul k presupunem ca) {poziiile 1,2,...,k-l din vectorul x sunt fixate} var MC: Mulime Elemente; a: TipElement; {tipul elementelor vectorului depinde de problema concret} begin if k-1 = n then {soluia este completa} Prelucrare_Solutie else begin 8

Candidat(MC, k); {procedura Candidat memoreaz in mulimea MC elementele} {din mulimea Sk care respecta condiiile de continuare) while MC nu este vid do begin {nu am epuizat candidaii pentru poziia k) x[k]:= Extrage(MC); {extrag un candidat din MC} BKT(k + 1) {apel recursiv} end; end end; Din descrierea general a metodei backtracking nu reiese explicit unde intervine revenirea. Pentru aceasta trebuie s ne gndim la modul de realizare a recursivitii. La fiecare apel recursiv se memoreaz pe stiv valorile variabilelor locale i valoarea parametrului. La ncheierea unui apel recursiv, se elibereaz zona de memorie alocat pe stiv i se revine la apelul precedent. Pentru a nelege mai bine modul de funcionare a acestei metode s analizm urmtoarea problem.

Problema reginelor
S se plaseze n regine pe o tabl de ah de dimensiune nxn astfel nct oricare dou regine s nu se atace. Reprezentarea informaiilor Pentru ca dou regine s nu se atace, ele nu trebuie s fie situate pe aceeai linie, pe aceeai coloan sau pe aceeai diagonal. Cum numrul de regine este egal cu dimensiunea tablei de ah, deducem c pe fiecare linie trebuie plasat o regin. Deci, pentru ca poziia reginelor s fie complet determinat, este suficient s reinem pentru fiecare linie coloana n care este plasat regina. Pentru aceasta vom utiliza un vector C, cu n componente avnd urmtoarea semnificaie: C [ i ] reprezint coloana n care este plasat regina de pe linia i. Condiii interne

1. C[i] aparine {l,2,...,n}, pentru I aparine {l,2...,n} (elementele vectorului C sunt indici de linii) 2.C[i] diferit C[j], i diferit j, i, j aparine {l,2,...,n} (dou regine nu pot fi plasate pe aceeai coloan) 3. |C[i]-C[j] | diferit | i-j | , Vi diferit j, i, j aparine {l,2,...,n} (dou regine nu pot fi plasate pe aceeai diagonal).

program Regine; const NrMaxRegine = 30; type Indice = 0 .. NrMaxRegine; Solutie = array[Indice] of 0..NrMaxRegine; var C: Solutie; n: Indice; NrSol: word;

procedure Afisare; var i, j: Indice; 9

begin inc(NrSol); writeln('Solutia nr. ', NrSol); for i := 1 to n do begin for j := 1 to n do if j = C[i] then write(' * ') else write(' o '); writeln end; writeln; readln; end;

procedure Plaseaza_Regina(k: Indice); {cand apelam procedura Plaseaza__Regina cu parametrul k am plasat deja regine pe liniile 1, 2, ...,k-l} var i, j: Indice; ok: boolean; begin if k-1 = n then Afisare else {trebuie sa mai plasam regine pe liniile k,k+l,...,n} for i := 1 to n do begin ok := true; for j := 1 to k-1 do if (C[j]=i) or (abs(C[j]-i)=(k-j)) then ok := false; {regina s-ar gasi pe aceeasi coloana sau aceeasi diagonala cu o regina deja plasata} if ok then {valoarea i respecta conditiile interne} begin {determin multimea MC a candidatilor pentru pozitia k} {verific daca pot plasa regina de pe linia k in coloana i} {am obtinut o solutie}

{prelucrarea solutiei consta in afisare}

C[k] := i;{i este un candidat, il extrag imediat} Plaseaza_Regina(k+1) ; end; end; end; 10

begin write('n= '); readln(n); Plaseaza_Regina(1); end.

{program principal}

Observai c am marcat poziiile libere cu ' o', iar poziiile pe care sunt plasate regine cu ' * '.

S analizm modul n care programul genereaz aceste soluii. Pentru aceasta vom urmri execuia programului pas cu pas.

11

12

13

14

Partiiile unei mulimi


Fie n aparine N*. Scriei un program recursiv care s genereze toate partiiile mulimii {1,2.....n}. Definiie Fie M o mulime nevid. S= {S 1, S2,..., Sn} constituie o partiie a mulimii M dac si numai dac sunt ndeplinite urmtoarele condiii:

(clasele partiiei sunt nevide) (clasele partiiei sunt disjuncte) (reuniunea claselor este egal cu ntreaga mulime). De exemplu, pentru n=3 programul va afia:

15

Reprezentarea informaiilor Vom reprezenta o partiie printr-un vector P, de dimensiune n, n care pentru fiecare element din mulimea {1,2,...,n} rein indicele clasei din care face parte. Aceast reprezentare asigur respectarea tuturor condiiilor din definiia partiiei. program Partitii; const NMax = 20; type Element = 1..NMax; Clasa = 1..NMax; Partitie = array[Element] of Clasa; var N: Element; {numarul de elemente din multime} NC: Clasa; {numarul de clase} P: Partitie; NrP: word; {numarul de partitii} procedure Afisare; var i: Element; j: Clasa; begin inc(NrP); write('Partitia ', NrP, ': '); for j := 1 to NC do begin {afisez clasa nr. j} write(' {'); for i := 1 to N do if P[i]=j then write(i, ' '); write(#8'} '); end; writeln; end; procedure GenPartitie(k: Element); {cand apelam GenPartitie(k), in vectorul P pe primele k-1 pozitii se afla o partitie a multimii 1,2,...,k-l formata din NC clase} var j: Clasa; begin if k=N+1 then Afisare {partitia este complet construita} else begin {plasam elementul k in una din clasele existente} for j := 1 to NC do begin P[k] := j; GenPartitie(k + 1); end; {sau elementul k reprezinta o clasa separata} inc(NC) ; {maresc numarul de clase} P[k] := NC; GenPartitie(k+1); dec(NC); {restaurez numarul de clase} end; end; begin {program principal} write('n= '); readln(n); GenPartitie(1); end.

Partiile unui numr natural Fie n aparine N*. Scriei un program care s afieze n fiierul de ieire ' partnr. out' toate partiiile numrului natural n. Definiie Numim partiie a unui numr natural nenul n un sistem de numere naturale nenule {p1,p2, . . . ,pk} astfel nct p1+p2+. . . +pk=n De exemplu, pentru n=5 programul va afia: 5=1 +1+1+1+1 5=1 +1+1+2 5=1 +1+3 5=1 +2+2 5=1 + 4 5=2 + 3 5=5 Reprezentarea informaiilor 16

Vom reprezenta o partiie printr-un vector p cu maxim n componente, n care vom reine elementele partiiei. Pentru a nu genera de dou ori o aceeai partiie (de exemplu, 5=1+4 i 5=4+1), convenim s memoram n vectorul p elementele partiiei n ordine cresctoare. Condiii interne (elementele partiiei sunt numere naturale nenule, cel mult egale cu n) (elementele partiiei sunt n ordine cresctoare); (suma elementelor partiiei trebuie s fie egala cu n). Vom utiliza o variabil global ValP n care vom reine permanent suma valorilor elementelor fixate n partiie. La adugarea unui element n partiie, valoarea acestuia va fi adugat la ValP, respectiv la eliminarea unui element din partiie, valoarea acestuia va fi sczut din ValP. Conform condiiei interne 2, candidaii pentru o poziie k din partiie sunt numerele naturale cel puin egale cu ultimul element plasat n partiie (p [ k-1 ]). Pentru ca aceast relaie s fie valabil pentru orice k (inclusiv pentru k=l), vom utiliza o poziie suplimentar n vectorul p, poziia 0, pe care vom plasa iniial valoarea 1 (p [ 0 ] =1). Conform condiiei interne 3, candidaii pentru o poziie k trebuie s fie numere naturale care, adugate la suma valorilor fixate deja n partiie, s nu depeasc pe n, deci trebuie s fie cel mult egale cu n-ValP. Utiliznd aceste dou observaii, deducem c mulimea candidailor posibili pentru poziia k este {p [k-1 ],..., n-ValP}. program Partitii_Numar_Natural; const NMax = 100; type Element = 0 .. NMax; Partitie = array[Element] of Element; var n, ValP: Element; {ValP - reprezinta suma valorilor elementelor partitiei} p: Partitie; fout: text; procedure Afisare(lg: Element) ; var i: Element; begin write({fout,} n, '= '); for i :=1 to lg-1 do write({fout,} p[i], ' + '); writeln({fout,} p[lg]); end; procedure ConstrPart(k: Element); var i: Element; begin if ValP=n then Afisare(k-1) inc(ValP,i); ConstrPart (k+1) ; dec(ValP, i); end; begin write('n= '); readln(n); assign(fout, 'partnr.out'); rewrite(fout); p[0] := 1; 17 {am obtinut o solutie, o afisez} {maresc ValP cu valoarea noului element} {apel recursiv} {restaurez valoarea variabilei ValP} end; else for i := p[k-1] to n-ValP do begin p[k] := i; {cand apelam ConstrPart(k) am fixat p[1],p[2],...,p[k-1]}

ConstrPart(1); close(fout); end.

Aplicaii
Plata unei sume cu monede de valori date. Avnd la dispoziie n sculee cu monede, fiecare scule coninnd monede de aceeai valoare, s se afieze toate modalitile de a plti o sum dat S folosind numai monedele din sculee. De exemplu, s presupunem c trebuie s achitm suma S=100 i avem n=3 sculee de monede. n primul scule se gsesc 6 monede cu valoarea 3, n al doilea scule se gsesc 4 monede cu valoarea 7, iar n ultimul scule sunt 14 monede cu valoarea 5. Programul va afia n fiierul de ieire (denumit ' suma. out') cele dou soluii posibile de plat astfel: Soluia nr. 1 3 monede cu valoarea 3 3 monede cu valoarea 7 14 monede cu valoarea 5 Soluia nr. 2 4 monede cu valoarea 3 4 monede cu valoarea 7 12 monede cu valoarea 5 Reprezentarea informaiilor Vom reine ntr-un vector V cu n componente valorile monedelor din cei n sculee, iar ntr-un alt vector M vom reine multiplicitile, adic numrul de monede din fiecare scule n parte.

program Plata_Suma; const NrMaxMonede = 20; type Moneda = 0..NrMaxMonede; Valori = array[Moneda] of word; Multiplicitati = array[Moneda] of byte; var n: Moneda; V: Valori; M, P: Multiplicitati; S, Sum, NrSol: word; fout: text; procedure Citire; {citesc datele de intrare din fisierul suma.in} var fin: text; i: Moneda; begin {assign(fin,'c:\tp\bin\suma.in'); reset(fin);} readln({fin,} n); readln({fin,} S); for i := 1 to n do readln({fin,} V[i], M[i]); { close(fin);} end; procedure Afisare; {afisez o solutie in fisierul de iesire} 18

var i: Moneda; begin inc(NrSol); writeln({fout,} 'Solutia nr. ', NrSol); for i := 1 to n do if P[i]<>0 then writeln({fout,} P[i], ' monede cu valoarea ', V[i]); writeln{(fout)}; end; procedure Plata(k: Moneda); {cand apelam procedura Plata cu parametrul k am selectat deja monede din saculetii 1,2,...,k-l} var j: Moneda; begin if k=n+1 then {am selectat monede de toate tipurile} if Sum = S then Afisare {Sum - valoarea monedelor selectate este egala cu S} else else {mai selectam monede din saculetii k,k+l,...,n} for j := 0 to M[k] do begin P[k] := j; {selectez j monede din saculetul k} if Sum+j*V[k]<=S then {valoarea monedelor selectate din saculetul k adaugata la valoarea monedelor deja selectate} {nu depaseste S, suma care trebuie platita} begin inc(Sum, j*V[k]); {adaug valoarea monedelor selectate din saculetul k} {la valoarea totala a monedelor selectate memorata in Sum} Plata(k+1); {apel recursiv} dec(Sum, j * V[k]); {restaurez dupa apel valoarea variabilei globale Sum} end else break {valoarea curenta a monedelor selectate depaseste suma S} {iesire fortata din for, deoarece nu are sens sa selectez mai multe monede din saculetul k, oricum am depasit deja S} end; end; begin {program principal} Citire; assign(fout, 'suma.out'); rewrite(fout); Plata(1); close(fout); end.

Observai c, i n acest program am utilizat tehnica variabilei globale: pentru a nu calcula la fiecare pas valoarea total a monedelor deja selectate, am reinut aceast valoare n variabila global Sum. De fiecare dat cnd selectm monede dintr-un scule, adugm valoarea monedelor selectate la Sum, apoi apelm recursiv procedura Plata care selecteaz n continuare monede din ceilali sculee pentru plata sumei. Cnd revenim din apelul recursiv, trebuie s restaurm valoarea variabilei globale Sum (deci trebuie s scdem din Sum valoarea monedelor selectate la pasul precedent din sculeul k, pentru a putea aduga ulterior, dac este cazul, valoarea corespunztoare monedelor selectate din sculeul k la pasul urmtor). Observai, de asemenea, c am optimizat programul: dac la un moment dat valoarea monedelor deja selectate depete suma S care trebuie pltit, nu continum generarea.

Paranteze
Fie N aparine N*, N numr par. S se scrie un program care s genereze toate irurile de n paranteze rotunde care se nchid corect. De exemplu, pentru N=4, programul afieaz: (()) ()() Reprezentarea informaiilor Vom memora un ir de paranteze ntr-un vector s cu N componente care pot avea doar valorile ' ( ' sau ' ) '. La fiecare moment vom reine numrul total de paranteze deschise (n variabila global ND) i numrul total de paranteze nchise (n variabila global NI). 19

Condiii interne n final: ND=NI Numrul total de paranteze nchise trebuie s fie egal cu numrul total de paranteze deschise i egal cu N div 2. Aceast condiie nu este suficient. Dac am considera numai aceast condiie, irul ) ) ( ( ar trebui s fie corect i nu e! Pentru ca parantezele s se nchid corect, trebuie ca la fiecare moment numrul de paranteze deschise s fie cel puin egal cu numrul de paranteze nchise. Pe parcurs : ND>=NI. program Paranteze; const NMax = 20; type Indice = 1..NMax; Sir = array[Indice] of char; var s: Sir; N, ND, NI: Indice; {ND - numarul total de paranteze deschise} {NI - numarul total de paranteze inchise} fout: text; procedure Afisare; var i: Indice; begin for i := 1 to N do write({fout,} s[i]); writeln{(fout)} end; procedure Constructie(k: Indice); {cand apelam Constructie(k), am fixat in sir k-l paranteze} begin if k-1 = N then {sirul de paranteze este complet} Afisare else begin if ND < N div 2 then {se mai pot deschide paranteze} begin s[k]:='('; inc(ND); Constructie(k+1); dec(ND); end; if NI < ND then {se poate inchide o paranteza} begin s[k] := ')' ; inc(NI); Constructie(k+1); dec(NI); end end end; begin {program principal} write('N= '); readln(N); assign(fout, 'sir.out'); rewrite(fout); Constructie(1); close(fout); end.

Comis-voiajor
Un comis-voiajor plec din oraul n care locuiete (s-1 notm 1) s prezinte produsele unei firme n toate cele n orae din regiunea sa. El are la dispoziie harta regiunii, pe care sunt marcate legturile directe dintre orae i distanele dintre acestea. Scriei un program care s determine un traseu ct mai scurt, astfel nct comis-voiajorul s viziteze toate oraele din regiune, s nu treac de mai multe ori prin acelai ora i s se ntoarc n oraul n care locuiete! De exemplu, s presupunem c exist n=5 orae, numerotate de la 1 la 5. S presupunem c harta regiunii indic urmtoarele legturi directe :

Pentru acest exemplu, programul va afia: Traseul cel mai scurt are lungimea 815.00 Traseul este 1,3,4,2,5,1 Reprezentarea informaiilor Vom reprezenta harta regiunii printr-o matrice ptratic A, cu nxn componente avnd semnificaia: A [ i, j ] = 0, dac ntre oraele i i j nu exista legtur direct, respectiv distana dintre oraele i i j, dac ntre i i j exist legtur direct. 20

Traseul, mai exact ordinea n care comis-voiajorul viziteaz cele n orae, l vom reine ntr-un vector T, cu n componente. Pentru a nu trece de mai multe ori prin acelai ora, vom mai utiliza un vector v, cu n componente, n care pentru fiecare ora vom reine dac a fost sau nu vizitat: v [ i ] = 1 dac oraul i a fost vizitat i 0 n caz contrar. Cnd obinem un traseu soluie nu l afim, ci l comparm cu traseul de lungime minim obinut pn la momentul respectiv. Prin urmare, vom utiliza TMin, un vector cu n componente, n care reinem un traseu de lungime minim i o variabil global LgMin n care reinem lungimea acestui traseu.

Pentru a optimiza procedura de generare a traseelor, vom utiliza o variabil global Lg m care reinem lungimea traseului curent. Astfel, nu va mai fi nevoie s calculm la fiecare pas lungimea traseului curent: cnd ne deplasm n alt ora adunm distana pn la oraul respectiv la Lg. Cnd eliminm un ora de pe traseu, scdem distana pn la acesta din Lg. Dac la un moment dat

program Comis_Voiajor; const NMaxOrase = 20; type Oras = 1 .. NMaxOrase; Harta = array[oras, Oras] of real; Traseu = array[oras] of Oras; var A: Harta; n: Oras; T, TMin: Traseu; Lg, LgMin: real; v: array[oras] of 0..1; procedure Citire; var i, j: Oras; f: text; d: real; begin assign(f, 'comis.in'); reset(f); readln(f, n); while not seekeof(f) do begin {de pe fiecare linie citesc doua orase intre care exista legatura directa, precum si distanta dintre ele} readln(f, i, j, d) ; A[i,j] := d; A[j,i] := d; end; close(f) end; function Infinit: real; {intoarce un numar suficient de mare, pentru a putea fi utilizat ca valoare de initializare pentru LgMin} var s: real; i, j: Oras; begin s := 0; for i := 1 to n do end; procedure Afisare; var i: Oras; begin if LgMin = Infinit then writeln('Nu exista solutii') else begin 21 for j := 1 to n do s:=s+A[i,j]; Infinit := s+1;

writeln('Traseul cel mai scurt are lungimea ',LgMin:10:2);writeln; write('Traseul este '); for i := 1 to n do write (TMin[i] , ','); writeln(TMin[1]);writeln; end; readln; end; procedure ConstrTraseu(k: Oras); var i: Oras; begin if k-1 = n then if A[1, T[n]] <> 0 then if Lg+A[1,T[n]]<LgMin then begin TMin := T; LgMin := Lg+A[1,T[n]]; end else else else for i := 2 to n do {construiesc in continuare traseul} {verific daca se poate deplasa in orasul i} {traseul este complet} {poate reveni in orasul 1} {am obtinut un traseu mai scurt}

if (A[i, T[k-1]] <> 0) and (v[i] = 0) then begin T[k] := i; v[i] := 1; Lg := Lg+A[i,T[k-1] ] ; if Lg <= LgMin then ConstrTraseu(k+1); v[i] := 0; Lg := Lg-A[i,T[k-1]]; end end; begin {program principal} Citire; T[1] := 1; v[1] := 1; LgMin := Infinit; ConstrTraseu (2); Afisare; end.

Backtracking in plan
n variant elementar, aplicm metoda backtracking pentru rezolvarea problemelor n care soluia era reprezentat ca vector. Putem generaliza ideea cutrii cu revenire i pentru probleme n care cutarea se face n plan". Pentru noi, planul va fi reprezentat ca un tablou bidimensional. Pentru a intui modul de funcionare a metodei backtracking n plan, s ne imaginm explorarea unei peteri. Speologul pornete de la intrarea n peter i trebuie s exploreze n mod sistematic toate culoarele peterii. Ce nseamn n mod sistematic" ? n primul rnd, i stabilete o ordine pentru toate direciile posibile de micare (de exemplu, N, NE, E, SE, S, SV, V, NV) i ntotdeauna cnd se gsete ntr-un punct din care are mai multe culoare de explorat, alege direciile de deplasare n ordinea prestabilit. n al doilea rnd, speologul va plasa marcaje pe culoarele pe care le-a explorat, pentru ca nu cumva s se rtceasc i s parcurg de mai multe ori acelai culoar (ceea ce ar conduce la determinarea eronat a lungimii peterii). n ce const explorarea ? Speologul exploreaz un culoar pn cnd ntlnete o intersecie sau pn cnd culoarul se nfund. Dac a ajuns la o intersecie, exploreaz succesiv toate culoarele care pornesc din intersecia respectiv, n ordinea prestabilit a direciilor. Cnd un culoar se nfund, revine la intersecia precedent i alege un alt culoar, de pe urmtoarea direcie (dac exist; dac nu exist, revine la intersecia precedent .a.m.d.). S descriem ntr-o form mai general aceast metod. Vom nota prin NrDirectii o constant care reprezint numrul de direcii de deplasare, iar dx, respectiv dy sunt doi vectori constani care reprezint deplasrile relative pe direcia Ox, respectiv pe direcia Oy, urmnd n ordine cele NrDirectii de deplasare. procedure Bkt_Plan(x,y: integer); (x, y reprezinta coordonatele pozitiei curente} begin Explorare(x,y); {exploram pozitia curenta} if EPinal(x,y) then Prelucrare_Solutie {pozitia x,y este un punct final} else {continuam cautarea} for i : = 1 to NrDirectii do {ma deplasez succesiv pe directiile posibile de miscare} if Nevizitat(x+dx[i], y+dy[i]) then {nu am mai trecut prin aceasta pozitie} Bkt_Plan(x+dx[i], y+dy[i]); end; 22

Labirint
ntr-un labirint, reprezentat ca o matrice L, cu n linii i m coloane, cu componente 0 sau 1 (1 semnificnd perete, 0 culoar), se gsete o bucat de brnz pe poziia (xb, yb) i un oricel pe poziia (xs, ys). Afiai toate posibilitile oricelului de a ajunge la brnz, tiind c el se poate deplasa numai pe culoar, iar direciile posibile de micare sunt N, NE, E, SE, S, SV, V, NV. De exemplu, pentru un labirint cu 4 linii i 4 coloane de forma urmtoare, n care oricelul se gsete pe poziia 1 1, iar brnza pe poziia 4 4 0 0 0 1 1 1 1 0 1 1 0 1 1 1 0 0

programul va afia: Soluia nr. *111 *111 *1** 1*1* Soluia nr. *111 *111 *1*0 1*1* 1

Reprezentarea informaiilor

Labirintul este reprezentat ca o matrice L, cu nxm elemente. Elementele labirintului sunt iniial 0 (semnificnd culoar) i 1 (semnificnd perete). Pentru ca oricelul s nu treac de mai multe ori prin aceeai poziie, existnd riscul de a intra n bucl, vom marca n labirint poziiile prin care trece oricelul cu 2. Pentru a determina poziiile n care se poate deplasa oricelul, vom utiliza dou constante cu tip Dx i Dy, pe care le iniializm cu deplasrile pe linie, respectiv pe coloan pentru toate cele 8 direcii posibile de micare.

Pentru a nu verifica permanent daca oricelul nu a ajuns cumva la marginea labirintului, bordm labirintul cu perete (dou linii i dou coloane cu valoarea 1). Condiii interne Din poziia (x, y) oricelul se poate deplasa pe direcia dir, deci n poziia (x+Dx[dir] , y+Dy[dir] ) dac i numai dac L[x+Dx[dir],y+Dx[dir]]=0 (aceast poziie reprezint un culoar prin care oricelul nu a mai trecut).

program Cautare_in_Labirint; const DimMax = 20; {dimensiunea maxima a labirintului} Dx: array[1..8] of integer = (-1,-1,0,1,1,1,0,-1 ); 23

Dy: array[1..8] of integer = (0,1, 1,1,0,-1,-1,-1); type Indice = 0 .. DimMax +1; var L: Labirint; Labirint = array[Indice, Indice] of 0 .. 2;

n, m, xs, ys, xb, yb: Indice; fout: text; NrSol: word;

procedure Citire; var f: text; i, j: Indice; begin assign (f, 'labirint.in'); reset(f); readln(f, n, m); readln(f, xs, ys, xb, yb);

for i := 1 to n do begin for j := 1 to m do read (f, L [ i, j ] ) ; end; procedure Bordare; {bordam labirintul cu cate un perete} var i: Indice; {perete la stanga si la dreapta} readln(f) end; close(f);

begin for i := 0 to n+1 do

begin L[i, 0] := 1; L[i, m+1] := 1 end; for i := 0 to m +1 do {perete sus si jos}

begin L[0, i] := 1; L[n+1, i] := 1 end end; function Final(x, y: Indice): boolean; {intoarce true daca in pozitia x y se afla branza} begin Final := false; if (x = xb) and (y = yb) then Final := true end; procedure Afisare; var i, j: Indice; begin inc(NrSol); writeln(fout, 'Solutia nr. ', NrSol); for i := 1 to n do begin for j := 1 to m do

if L[i, j] = 2 then write(fout, '*') else write(fout, L[i, j]); writeln(fout); end; end; procedure Cauta(x, y: Indice); var dir: 1..8; begin L[x,y] := 2; {marchez pozitia x y}

if Final(x, y) then Afisare else for dir := 1 to 8 do if L[x+Dx[dir], y+Dy[dir]]=0 then {culoar nevizitat} Cauta(x+Dx[dir], y+Dy[dir]); L[x, y] := 0; {la intoarcere sterg marcajul, pentru a putea explora} 24

{acest culoar si in alta varianta} end;

begin Citire; Bordare;

{program principal}

assign(fout, 'labirint.out'); rewrite(fout); Cauta(xs, ys); if NrSol = 0 then writeln(fout, 'Nu exista solutii!'); close(fout);end.

Fotografie
Fotografia alb-negru a unui obiect este reprezentat sub forma unei matrice cu n linii i m coloane, ale crei elemente sunt 0 sau 1. Elementele egale cu 1 reprezint punctele ce aparin unui obiect. Dou elemente de valoare 1 fac parte din acelai obiect dac sunt adiacente pe linie sau pe coloan. Se cere s se determine numrul obiectelor din fotografie. De exemplu, pentru matricea:

Soluie Pentru a numra obiectele din fotografie, vom parcurge matricea care reprezint fotografia, cutnd un element cu valoarea 1, deci care aparine unui obiect. Vom numra noul obiect depistat, apoi vom terge" obiectul din fotografie, colorndu-1 n culoarea de fond (valoarea 0 n matrice). Pentru a terge" un obiect vom folosi procedura recursiv Sterge_0biect (x, y), care n cazul n care punctul de coordonate (x, y) aparine obiectului (a [x, yj =1), l terge (a [x,y] =0), apoi se apeleaz recursiv procedura pentru toate punctele adiacente cu (x,y) (pe linie sau pe coloan).

Pentru a nu testa permanent dac n timpul cutrii am depit marginile fotografiei, am bordat matricea care reprezint fotografia cu cte o linie i o coloan sus, jos, stnga, dreapta iniializate cu valoarea 0 (am nrmat" fotografia). Observaie Acest tip de algoritm, prin care plecnd de la un element sunt atinse" succesiv toate elementele care au o legtur (direct sau indirect) cu elementul respectiv, poart numele de algoritm defll (umplere). program Fotografie; const DimMax = 50; Dx: array[l .. 4] of integer=(-1,0,1,0); Dy: array[l .. 4] of integer=(0,1,0,-1); type Indice = 0 .. DimMax+1; var a: array[Indice, Indice] of 0 .. 1; m, n, i, j, NrObiecte: Indice; procedure Citire; var fin: text; begin assign(fin, 'foto.in'); reset(fin); readln(fin, n, m) ; for i : = 1 to n do begin for j := 1 to m do read(fin, a[i, j]); readln(fin); close(fin); end;

end;

25

procedure Sterge_0biect(x ,y: Indice); var dir: 1 .. 4; begin if a[x, y] = 1 then begin a[x, y] := 0;

{sterg acest element de imagine} {cautarea continua in cele 4 directii posibile} for dir:= 1 to 4 do Sterge_0biect(x+Dx[dir], y+Dy[dir]); end; end; begin Citire; for i : = 1 to n do for j : = 1 to m do if a[i, j] = 1 then begin inc(NrObiecte); Sterge_0biect(i, j); end; writeln('Nr. obiecte = ', NrObiecte); readln end.

{am depistat un obiect}

Consideraii finale asupra metodei backtracking


A existat o perioad n evoluia gndirii algoritmice cnd metoda backtracking era considerat un panaceu. Nu tim s rezolvm o problem? Nu-i nimic! Aplicm un backtracking! Sigur c aceast metod are avantaje indiscutabile. Dup cum spuneam la nceputul capitolului, metoda evit generarea tuturor soluiilor posibile, urmat de verificarea condiiilor problemei pentru fiecare soluie posibil (adic se poate i mai ru). n plus, rezolvarea unei probleme prin backtracking garanteaz obinerea soluiei. Numai c timpul de execuie este foarte mare, datorit revenirilor specifice metodei. Uneori timpul de execuie este att de mare, nct pentru dimensiuni mari ale datelor de intrare obinerea unei soluii este practic imposibil. n concluzie, atunci cnd trebuie s rezolvm o problem ncercm n primul rnd s elaborm un algoritm care nu se bazeaz pe backtracking. Dac nu reuim s elaborm un astfel de algoritm sau un astfel de algoritm nu exist, analizm datele de intrare. Dac datele de intrare au dimensiuni rezonabil de mici, astfel nct un algoritm backtracking s furnizeze soluii n timp util, abordm problema n aceast manier.

Dac ns datele de intrare au dimensiuni mari, ceea ce n practic este inevitabil, o abordare backtracking este inacceptabil. Principiul de baz poate fi rezumat astfel: dect un algoritm teoretic perfect, dar care s nu furnizeze soluii n timpul disponibil, mai bine un algoritm bun, care s ofere soluii aproape" optime n timp scurt. Un astfel de algoritm este denumit algoritm euristic. Studiul algoritmilor euristici este o problem fierbinte" n informatic la ora actual.

Generarea elementelor combinatorice


Analiza combinatorica este o ramur a matematicii care studiaz diferite posibiliti de ordonare sau de combinare a unor elemente. De exemplu, n cte moduri putem aranja 4 litere diferite ? " sau cte posibiliti exist de a construi o echip format din 5 fete si 3 biei dintr-o clas cu 20 de fete i 11 biei ? ". La matematic am studiat cteva elemente de combinatoric (permutri, combinri, aranjamente). n acest capitol vom descrie algoritmi recursivi de generare a acestor elemente combinatorice. Generarea permutrilor. Fie ne N*. Scriei un program recursiv de generare a permutrilor de ordin n. De exemplu, pentru n=3 programul va genera: 1 1 2 2 3 3 2 3 1 3 1 2 3 2 3 1 2 1

Soluie Reprezentarea informaiilor Permutrile de ordin n reprezint toate posibilitile de a aranja elementele unei mulimi de n elemente. Definite riguros, permutrile de ordin n sunt funcii bijective de forma: p:{1,2,...,n}->{1,2,...,n}. 26

Reprezentm o astfel de funcie printr-un vector p, cu n componente, avnd semnificaia: p [ i ] este elementul asociat prin intermediul funciei p elementului i (i aparine {1, 2 ,..., n}). Condiii interne 1. p[i] aparine {1,2,...,n} pentru i aparine {1,2,...,n} (domeniul de valori este {1,2,...,n}) 2. p [ i ] diferit p [ j ] , pentru i diferit j , i, j aparine {1, 2 ,..., n}

(funcia este injectiv).

program Permutari; const NMax = 20; type Indice = 1 .. NMax; Permutare = array[Indice] of Indice; var n: Indice; p: Permutare; ImF: set of Indice; {imaginea functiei} fout: text; {fisierul de iesire} procedure Afisare; var i: Indice; begin for i := 1 to n do write(fout, p[i], ' '); writeln(fout); end; procedure GenPermutari(k: Indice); {cand apelam procedura GenPermutari cu parametrul k) {pozitiile 1,2,...,k-1 din vectorul p sunt fixate} var i: Indice; begin if k-1 = n then {solutia este completa} Afisare {prelucrarea solutiei consta in afisare} else {continuam generarea} for i:=1 to n do {determin candidatii pentru pozitia k} if not (i in Imf) then begin {i este un candidat, deoarece nu este imaginea nici unui alt element fixat} p[k] := i; Imf := Imf + [ i]; {i este imaginea lui k, deci il retin in Imf} GenPermutari (k + 1); {apel recursiv} Imf := Imf-[i]; {restaurez valoarea variabilei Imf dinaintea apelului} end; end; begin write(' n = '); readln(n); assign(fout, 'perm.out'); rewrite(fout); GenPermutari(1); close(fout); end. Observatie Pentru generarea permutrilor am utilizat o tehnic interesant. n loc sa verificam pentru fiecare element i daca este sau nu un candidat pentru pozitia k} procedure GenAranjamente(k: Indice); var i: Indice; begin if k-l=m then {numarul de elemente din vector este m} Afisare else for i : = 1 to n do if not (i in Imf) then begin Imf := Imf + [i]; f[k] := i; GenAranjamente(k+1) ; Imf := Imf - [i]; end; end;

Generarea combinrilor. Fie nN* i mN, m<n. Scriei un program recursiv care s genereze combinrile de n elemente luate cte m. De exemplu, pentru n=4 i m=2, programul va genera: l 2 l 3 l 4 2 3 2 4 3 4 Soluie 27

Combinrile de n elemente luate cte m reprezint toate submulimile de m elemente ale unei mulimi cu n elemente. Observai c, spre deosebire de aranjamente, ordinea elementelor din submulime nu conteaz. Din acest motiv, pentru a nu genera de mai multe ori aceeai submulime (de exemplu, 1 2 i 2 1) vom stabili o ordine convenabil pentru elementele submulimii - ordinea cresctoare. Reprezentarea informaiilor Reprezentm o submulime printr-un vector C, care conine cele m elemente ale submulimii, ordonate cresctor. Condiii interne (elementele aparin mulimii {1,2, ... ,n}) (elementele sunt ordonate cresctor).

Conform celei de a doua condiii interne pe poziia k putem plasa doar elemente mai mari dect elementul plasat pe poziia k-1 (C[k-l]). Deci, valoarea minim care poate fi plasat pe poziia k este C [k-1] +1. Pentru ca aceast formul s fie valabil pentru orice poziie k (inclusiv pentru k=l) vom declara indicii vectorului care reprezint submulimea ncepnd de la 0 i vom considera C [ 0 ] = 0. Deoarece dup C[k] trebuie plasate nc m-k elemente mai mari dect C [k], s determinm valoarea maxim care poate fi plasat pe poziia k.

Valoarea maxim care poate fi plasat pe poziia m este n, pe poziia m-1 este n-1 .a.m.d. Observm c, daca poziia scade cu 1, i valoarea maxim care poate fi plasat pe poziia respectiv scade cu 1. Prin urmare, diferena dintre poziie i valoarea maxim care poate fi plasat pe poziia respectiv este constant (m-n). Deducem c valoarea maxim care poate fi plasat pe poziia k este n-m+k. Prin urmare, mulimea candidailor pentru poziia k este {C[k-l]+l,...,n-m+k}. program Combinari; const NMax = 30; type Indice = 0..NMax; Submultime = array[Indice] of Indice; var C : Submultime; m, n: Indice; procedure Afisare; var i : Indice; begin for i := 1 to m do write(C[i],' '); writeln; end; procedure GenCombinari(k : Indice); var i : Indice; begin if k-1=m then Afisare else for i := C[k-1]+1 to n-m+k do begin C[k] := i; GenCombinari(k+1); end; end; begin write('n = '); readln(n); write('m = '); readln(m); GenCombinari(1); end.

Divide et Impera Prezentare generala


Divide et Impera este o tehnic special prin care se pot rezolva anumite probleme. Ca i backtracking, divide et impera se bazeaz pe un principiu extrem de simplu: descompunem problema n dou sau mai multe subprobleme (mai 28

uoare), care se rezolv, iar soluia pentru problema iniial se obine combinnd soluiile problemelor n care a fost descompus. Se presupune c fiecare din problemele n care a fost descompus problema iniial, se poate descompune n alte subprobleme, la fel cum a fost descompus problema iniial. Procedeul se reia pn cnd (n urma descompunerilor repetate) se ajunge la probleme care admit rezolvare imediat. Evident, nu toate problemele pot fi rezolvate prin utilizarea acestei tehnici. Fr teama de a grei, putem afirma c numrul lor este relativ mic, tocmai datorit cerinei ca problema s admit o descompunere repetat. Divide et impera este o tehnic ce admite o implementare recursiv. Am nvat principiul general prin care se elaboreaz algoritmii recursivi: ce se ntmpl la un nivel, se ntmpl la orice nivel (avnd grij s asigurm condiiile de terminare). Tot aa, se elaboreaz un algoritm prin divide et impera: la un anumit nivel avem dou posibiliti: 1) am ajuns la o problem care admite o rezolvare imediat, caz n care se rezolv i se revine din apel (condiia de terminare); 2) nu am ajuns n situaia de la punctul 1, caz n care descompunem problema n dou sau mai multe subprobleme, pentru fiecare din ele reapelm procedura (funcia), combinm rezultatele si revenim din apel.

Maxim dintr-un vector


Se citete un vector cu n componente numere naturale. Se cere s se tipreasc valoarea maxim. Problema de mai sus este binecunoscut. Cum o rezolvm utiliznd divide et impera? Trebuie tiprita valoarea maxima dintre numerele reinute n vector de la i la j (iniial i=1, j=n), Pentru aceasta procedm astfel: dac i=j, valoarea maxim va fi v [ i ]; contrar vom mpri vectorul n doi vectori (primul vector va conine componentele de la i la (i+j) div 2, al doilea va conine componentele de la (i+j) div 2+1 la j, rezolvm subproblemele (aflm maximul pentru fiecare din ele) iar soluia problemei va fi data de valoarea maxima dintre rezultatele celor dou subprobleme. program maxim; var v:array[1..10] of integer; n,i:integer; function max (i,j :integer): integer; var a,b:integer; begin if i=j then max:=v[i] else begin a:=max(i, (i+j)div 2); b:=max((i+j) div 2+1,j); if a>b then max:=a else max:=b; end; end; begin write('n=');readln(n); for i:=1 to n do begin write ('v[',i, ']='); readln (v[i] ) end; writeln ('max=', max (1,n)) end. Algoritmul prezentat este exclusiv didactic, n practic se prefer algoritmul clasic.

Cutare binar
Se citete un vector cu n componente numere ntregi, unde numerele se presupun ordonate cresctor i o valoare ntreag (nr). S se decid dac nr se gsete sau nu printre numerele citite, iar n caz afirmativ s se tipreasc indicele componentei care conine acea valoare. 29

O rezolvare n care nr se compar pe rnd cu cele n valori, este lipsit de valoare (nu exploateaz faptul c cele n valori sunt n secven cresctoare). Algoritmul care va fi propus este mult mai performant i face parte din algoritmii clasici. program ackermann; type stiva=array [1..100,1..2] of integer; var st: stiva; begin write ('m=') ; readln (m); write ('n='); readln (n); k:=1; st[k,1]:=m; st[k,2]:=n; while k>0 do if (st[k,1]<>0) and (st[k,2]<>0) then begin m,n,k:integer;

k:=k+1; st[k,1]:= st[k-1, 1]; st[k,2]:=st[k-1,2]-1 end else if st[k,2]=0 then begin st[k,1]:=st[k,1]-1; st[k,2]:=1 end else begin k:=k-1; if k>0 then begin st[k,1]:=st[k,1]-1; st[k,2]:=st[k+1,2]+1 end end; writeln('ac(',m, ', ',n,')=',st[1,2]+1) end. { m=2, n=3}

Ca i la problema anterioar, este interesant de urmrit modul n care a fost ridicat recursivitatea din definiie. Cele dou variante sunt echivalente din punct de vedere al timpului de calcul, dar cea recursiv este de preferat pentru simplitatea cu care a fost scris programul. write('nr='); readln(nr); caut(1,n); end.

Turnurile din Hanoi


Se dau 3 tije simbolice prin a, b, c. Pe tija a se gsesc discuri de diametre diferite, aezate n ordine descresctoare a diametrelor privite de jos n sus. Se cere s se mute discurile respectnd urmtoarele reguli: la fiecare pas se mut un singur disc: nu este permis s se aeze un disc cu diametrul mai mare peste un disc cu diametrul mai mic. Rezolvare: Dac n=1 se face mutarea ab, adic se mut discul de pe tija a pe tija b. Dac n=2 se fac mutrile ac, ab, cb. n cazul n care n>2 problema se complic. Notm cu H(n,a,b,c) irul mutrilor celor n discuri de pe tija a pe tija b, utiliznd ca tija intermediar, tija c. Conform strategiei DIVIDE ET IMPERA ncercm s descompunem problema n alte dou subprobleme de acelai tip, urmnd apoi combinarea soluiilor. n acest sens, observm c mutarea celor n discuri de pe tija a pe tija b, utiliznd ca tij intermediar tija c, este echivalent cu: 30

mutarea a n-1 discuri de pe tija a pe tija c, utiliznd ca tij intermediar tija b; mutarea discului rmas pe tija b; mutarea a n-1 discuri de pe tija c pe tija b, utiliznd ca tij intermediar tija a. Parcurgerea celor trei etape permite definirea recursiv a irului H(n,a,b,c) astfel:

daca n = 1 ab, H (n, a, b, c) = H (n 1, a, c, b), ab, H (n 1, c, b, a) daca n > 1


Exemple: Pentru n=2 avem: H(2,a,b,c)=H(1,a,c,b),ab,H(1,c,b,a)=ac,ab,cb. Pentru n=3 avem: H(3,a,b,c)=H(2,a,c,b),ab,H(2,c,b,a)= H(1,a,b,c),ac,H(1,b,c,a),ab,H(1,c,a,b),cb,H(1,a,b,c)=ab,ac,bc,ab,ca,ab. program hanoi; var a,b,c:char; n:integer; procedure han (n:integer;a,b,c:char); begin if n=l then writeln (a,b) else begin han(n-l,a,c,b); writeln(a,b); han(n-l,c,b,a) end ; end; begin write('N=');readln(n); a:='a'; b:='b';c:='c'; han(n,a,b,c) end.

Sortare rapid
Fie vectorul a cu n componente numere ntregi (sau reale). Se cere ca vectorul s fie sortat cresctor. Pentru rezolvare este necesar o procedur POZ care trateaz o poriune din vector cuprins ntre indicii dai de li (limita inferioar) i ls (limita superioar). Rolul acestei proceduri este de a poziiona prima component a[li] pe o poziie k cuprinsa ntre li i ls, astfel nct toate componentele vectorului cuprinse ntre li i k-1 s fie mai mic sau egale dect a[k] si toate componentele vectorului cuprinse ntre h+1 i ls s fie mai mari sau egale dect a[k]. n aceast procedur exist dou moduri de lucru: a) i rmne constant, j scade cu 1; b) i creste cu 1, j rmne constant. Procedura este conceput astfel: iniial, i va lua valoarea li, iar j va lua valoarea ls (elementul care iniial se afla pe poziia li se va gsi mereu pe o poziie dat de i sau de j); se trece n modul de lucru a); att timp ct i<j se execut: dac a[J] este strict mai mare dect a[j], atunci se inverseaz cele dou numere si se schimb modul de lucru; i i j se modific corespunztor modului de lucru n care se afl programul: k ia valoarea comun a lui i i j. 31

Exemplu: a=(6,9,3,1,2), li=1, ls=5; modul de lucru a); i=1,j=5; a[1]>a[5], deci se inverseaz elementele aflate pe poziiile 1 si 5, deci a=(2,9,3,1,6) i programul trece la modul de lucru b); i=2, j=5; a[2]>a[5], deci a=(2,6,3,1,9) i se revine la modul de lucru a); 1=2, j=4; a[2]>a[4], deci a=(2,1,3,6,9); se trece la modul de lucru b); i=3, j=4: procedura se ncheie, elementul aflat iniial pe poziia 1 se gsete acum pe poziia 4, toate elementele din stnga lui fiind mai mici dect el, totodat toate elementele din dreapta lui fiind mai mari dect el (k=4). Alternana modurilor de lucru se explica prin faptul c elementul care trebuie poziional se compar cu un element aflat n dreapta sau n stnga lui, ceea ce impune o modificare corespunztoare a indicilor i i j. Observaie. Dup aplicarea procedurii POZ, este evident c elementul care se afl iniial n poziia li va ajunge pe o poziie k i va rmne pe acea poziie n cadrul vectorului deja sortat, fapt care reprezint esena algoritmului.
DIVIDE ET IMPERA,

Procedura QUICK are parametrii li i ls (limita inferioar i limita superioar). n cadrul ei se utilizeaz metoda dup cum urmeaz: se apeleaz POZ; se apeleaz QUICK pentru li i k-1; se apeleaz QUICK pentru k+1 i ls. program quickl; type vector=array[1..100] of integer; var i,n,k:integer; a:vector; procedure poz (li,ls:integer;var k:integer;var a:vector); var i,j,c,i1,j1:integer; begin i1:=0;j1:=-1; i:=li;j:=ls; while i<j do begin if a[i]>a[j] then begin c:=a[j]; a[j]:=a[i]; a[i]:=c; c:=i1; i1:=-j1; j1:=-c; end; i:=i+i1; j:=j+j1; end; k:=i ; end; procedure quick (li,ls:integer); begin if li<ls then begin poz(li,ls,k,a); quick(li,k-1); quick(k+1,ls) end; end; begin write('n=');readln(n); for i:=1 to n do begin write('a[',i,']='); readln(a[i]); end; quick(1,n); for i:=1 to n do writeln (a[i]) end.

Sortare prin interclasare


Se consider vectorul a cu n componente numere ntregi (sau reale). S sorteze cresctor, utiliznd sortarea prin interclasare. Prezentam pe scurt problema interclasrii a doi vectori. Se dau doi vectori a si b cu m i n componente numere reale, sortai cresctor (descresctor). Pentru rezolvare, simbolizm cu i indicele elementului la care s-a ajuns n primul vector, cu j indicele la care s-a ajuns n al doilea vector i cu k indicele elementului care urmeaz a fi scris n cel de-al treilea vector. Att timp ct i este mai mic sau egal cu m i j este mai mic sau egal cu n, comparm a[i] cu b[j] i n funcie de rezultat procedm astfel: 32

dac a[i]>b[j], atunci c[k] va lua valoarea lui a[i], iar valorile variabilelor i i k vor crete cu 1; altfel, c[k] va lua valoarea lui b[j], n timp ce valorile variabilelor j i k vor creste cu 1. Dup ce unul din cei doi vectori a fost n totalitate parcurs i scris n c, vectorul rmas se va copia n c. Exemplu: a=(1,3,5,9), b=(2,4): i=1,j=1,k=1;. a[1]<b[1], deci c[1]=1, i=2, k=2; a[2]>b[1], deci c[2]=2, j=2, k=3; a[2]<b[2], deci c[3]=3, i=3, k=4; a[3]>b[2], deci c[4]=4, j=3, k=5; vectorul b a fost trecut n c n ntregime, n continuare urmnd a se copia ceea ce a rmas neparcurs din vectorul a n vectorul . c[5]=5, c[6]=9. Propunem ca exerciiu scrierea acestui program. n cele ce urmeaz vom utiliza algoritmul de interclasare n vederea sortrii unui vector prin interclasare. Observaie: un vector cu o singur component este un vector sortat; pentru a sorta (cresctor) un vector cu dou componente, acestea se compara ntre ele i, daca prima este mai mare dect cea de-a doua, locurile lor se inverseaz. Algoritmul de sortare prin interclasare se bazeaz pe urmtoarea idee: pentru a sorta un vector cu n elemente l mprim n doi vectori care, odat sortai, se interclaseaz. Conform strategiei generale DIVIDE ET IMPERIA, problema este descompus n alte dou subprobleme de acelai tip i, dup rezolvarea lor, rezultatele se combin (n particular se interclaseaz). Descompunerea unui vector n ali doi vectori care urmeaz a fi sortai are loc pn cnd avem de sortat vectori de una sau dou componente n aplicaie, procedura SORT sorteaz un vector de maxim dou elemente; INTERC interclaseaz rezultatele; DIVIMP implementeaz strategia general a metodei studiate.

program sinter; type vector=array [1..10] of integer; var a:vector; n,i:integer; procedure sort (p,q:integer;var a:vector); var m:integer; begin if a[p]>a[q] then begin m:=a[p]; a[p]:=a[q]; a[q]:=m end; end; procedure interc (p,q,m:integer;var a:vector); var b:vector; i,j,k:integer; begin i:=p; j:=m+1; k:=1; while (i<=m) and (j<=q) do if a[i]<=a[j] then begin b[k]:=a[i]; i:=i+1; k:=k+1 end 33

else begin b[k]:=a[j]; j:=j+1; k:=k+1 end; if i<=m then for j:=i to q do begin b[k]:=a[j]; k:=k+1 end else for i:=j to q do begin b[k]:=a[i]; k:=k+1 end; k:=1; for i:=p to q do begin a[i]:=b[k]; k:=k+1 end; end; procedure divimp (p,q:integer;var a:vector); var m:integer; begin if (q-p)<=1 then sort(p,q,a) else begin m:=(p+q) div 2; divimp (p,m,a); divimp(m+1,q,a); interc(p,q,m,a); end ; end; begin write('n=');readln(n); for i:=1 to n do begin write('a[',i,']='); readln(a[i]) end; divimp(1,n,a); for i:=1 to n do writeln (a[i]); end.

Problema tieturilor
Se d o bucat dreptunghiular de tabl cu lungimea I i nlimea h, avnd pe suprafaa ei n guri de coordonare numere ntregi. Se cere sa se decupeze din ea o bucat de arie maxim care nu prezint guri. Sunt permise numai tieturi verticale i orizontale. Coordonatele gurilor sunt reinute n doi vectori XV si YV. Dreptunghiul iniial, precum i dreptunghiurile care apar n procesul tierii sunt memorate n program prin coordonatele colului din stnga-sus (X,Y), prin lungime l nlime (L,H). Pentru un dreptunghi (iniial pornim cu toat bucata de tabl), verificm dac avem sau nu o gaur n el (se caut practic prima din cele n guri). n situaia cnd acesta prezint o gaur, problema se descompune n alte patru probleme de acelai tip. Dac bucata nu prezint guri, se compar arta ei cu aria unei alte buci fr gaur, gsit n fazele precedente. Menionm c dreptunghiul de arie maxim fr guri este reinut prin aceiai parametri ca i dreptunghiul cu guri. n zonele XF, YF, LF, HF (variabile transmise prin referin de la o faz la alta). n concluzie, problema iniial am descompus-o n alte patru probleme de acelai tip, mai uoare, ntruct fiecare nou dreptunghi are cel mult n-1 guri, dac dreptunghiul iniial avea n guri. La aceast problem compararea soluiilor const n a reine dreptunghiul cu aria maxim dintre cele fr guri. Fie dreptunghiul cu o gaur: Pentru a se afla n interiorul dreptunghiului, gaura trebuie sa ndeplineasc simultan condiiile: 1) xv(i)>x: 2) xv(i)<x+l; 3) yv(i)>y: 4) yv(t)<y+h. Dac facem o tietur vertical prin aceast gaur, obinem dou dreptunghiuri: 1) x, y, xv(i)-x, h; . 2) xv(i), y, l+x-xv(i), h. n urma unei tieturi pe orizontal se obin cele dou dreptunghiuri: 1) x, y ,t ,yv(i)-y; 2) x, yv(i), I, h+y-yv(i). program tai; 34

type vect=array [1..9] of integer; var l,h,i,n,xf,yf,lf,hf:integer; xv,yv:vect; procedure dimp (x,y,l,h:integer;var xf,yf,lf,hf:integer; var xv,yv:vect); var gasit:boolean; i:integer; begin i:=l; gasit:=false; while (i<=n) and (not gasit) do if (xv[i]>x) and (xv[i]<l) and (yv[i]>y) and (yv[i]<y+h) then gasit:=true else i:=i+1; if gasit then begin dimp(x,y,xv[i]-x,h,xf,yf,lf,hf,xv,yv); dimp(xv[i] ,y,l+x-xv[i] ,h,xf,yf,lf,hf,xv,yv); dimp(x,y,l,yv[i]-y,xf,yf,lf,hf,xv,yv); dimp(x,yv[i],l,h+y-yv[i],xf,yf,lf,hf,xv,yv); end else if (l*h)>(lf*hf) then begin xf:=x; yf:=y; lf:=l;hf:=h end; end; begin write('n='); readln(n); for i:=1 to n do begin write('x[',i,']=');readln(xv[i]); write('y[',i,']=');readln(yv[i]); end; write('l=');readln(l); write ('h=') ; readln(h); lf:=0; hf:=0; dimp(0,0,l,h,xf,yf,lf,hf,xv,yv); writeln('x=',xf,' y=',yf,' l=',lf,' h=',hf) end.

Probleme propuse 1) Scriei un program n care calculatorul sa ghiceasc un numr natural ascuns de dumneavoastr (numrul este cuprins ntre 1 si 30000). Atunci cnd calculatorul propune un numr i se va rspunde prin: 1, dac numrul este prea mare; 2, dac numrul este prea mic; 0, dac numrul a fost ghicit. 2) Se consider un vector cu o componente, numere naturale. Definim plierea vectorului ,ca fiind suprapunerea unei jumti, numit donatoare, peste o alta, numit receptoare. n cazul n care vectorul are un numr impar de componente, cea din mijloc este eliminata. In acest fel se ajunge la un vector ale crui elemente au numerotarea jumtii receptoare. Exemplu: vectorul 1,2,3,4,5 se poate plia n dou moduri 1,2 si 4,5. Plierea se aplic n mod repetat pn se ajunge la un vector cu o singur component, iar coninutul ei se numete element final. S se precizeze care sunt elementele finale i care este irul de plieri prin care se poate ajunge la ele. O.N.I. 1992 (enun modificat). 3) Se da un vector cu n componente la nceput nule. O seciune pe poziia K va incrementa toate elementele aflate ntre o alt zon de secionare anterioara i K. Exemplu: 0000000 se secioneaz pe poziia 4: 1111000 se secioneaz pe poziia 1; 2111000 se secioneaz pe poziia 3; 2221000 etc. Sa se determine o ordine de secionare a unui vector cu n elemente astfel nct suma elementelor sale s fie S. Se citesc N i S.

Tehnica Greedy Prezentare general


Cu toate c este practic imposibil s fie dat forma general a unei probleme rezolvabil cu tehnica Greedy, vom ncerca totui. S considerm o mulime A cu n elemente. Se cere o submulime a sa cu m elemente (n cazul m=n este important ordinea alegerii elementelor), astfel nct s fie ndeplinite anumite condiii (acestea difer de la o problem la alta). Exemplu: se consider o mulime de n numere reale. Se cere o submulime a sa, astfel nct suma elementelor sale s fie maxim. 35

Pentru rezolvare, vom alege un prim element al mulimii de numere reale. Dac este posibil, acesta va fi adugat soluiei, iniial vide. Posibilitatea ca acesta s fie adugat este dat de semnul numrului (acesta trebuie s fie mai mare ca O). Se alege un al doilea numr, cu care se procedeaz n mod asemntor. Algoritmul se ncheie cnd au fost alese l eventual adugate toate elementele mulimii. Pentru a rezolva o problem cu Greedy, soluia se construiete, dup regula: Pentru fiecare element care urmeaz s fie adugat soluiei finale, se efectueaz o alegere a sa din elementele mulimii A (dup un mecanism specific fiecrei probleme n parte), iar dac este posibil, acesta este adugat. Algoritmul se termin fie cnd a fost gsit soluia cerut, fie cnd s-a constatat inexistena acesteia. Intuitiv, alegem un element, al doilea,....pn cnd obinem ce dorim sau pn cnd au fost testate toate elementele mulimii. De aici provine si numele metodei (greedy=lacom).

Greedy pare att de simpl nct, la nceput, ne mir faptul c a fost evideniat ca tehnic. La o analiz atent, vom observa c lucrurile nu stau chiar aa. Exemplul prezentat este didactic (l rezolvam i fr s tim c exist aceast tehnic), el nu are alt rol dect de a evidenia caracteristicile tehnicii.

Tehnica Greedy poate fi privita ca o particularizare a tehnicii Backtracking, n care se renuna la mecanismul de ntoarcere. S analizm n paralel, cele dou tehnici, pentru a putea stabili asemnrile i diferenele existente ntre ele: => ambele tehnici ofer soluii sub form de vector; => tehnica Backtracking poate oferi toate soluiile problemei, n timp ce tehnica Greedy ofer o singur soluie => tehnica Greedy nu dispune de mecanismul ntoarcerii, specific tehnicii backtracking. : Aceasta este diferena esenial dintre cele doua tehnici, diferen care are consecine uriae n ce privete aplicabilitatea lor. Consecina 1. Este necesar ca cel care elaboreaz un algoritm Greedy s tie faptul ca, procednd n modul ales de el, ajunge la rezultatul dorit. Pentru fiecare problem n parte, dup ce se identifica un algoritm, este obligatoriu s se demonstreze c acesta conduce la soluia optima. Demonstraia faptului ca se ajunge la soluia optim este specific fiecrei probleme n parte. Consecina 2. Tehnica Greedy conduce la timp de calcul polinomial. Motivul care conduce la acest timp de calcul, tine de mecanismul tehnicii. S presupunem c mulimea din care se face alegerea are n elemente si c soluia are tot n elemente (caz maxim). Se fac n alegeri, la fiecare alegere se fac n teste, rezulta un algoritm cu timp O(n2). De multe ori, este necesar ca elementele mulimii. A s fie sortate, pentru ca apoi s alegem din acestea, iar sortarea necesita un timp minim O(n x log 2n). Ins sortarea se efectueaz la nceput. Prin urmare, acest timp se adun, deci nu influeneaz rezultatul. 0 ntrebare fireasc: fiind dat o problem, exist ntotdeauna un algoritm de tip Greedy care gsete soluia optim? Evident, NU. Exist probleme pentru care nu se cunosc astfel de algoritmi. Mai mult, pentru cele mai multe probleme nu se cunosc algoritmi Greedy. Avantajul timpului polinomial, conduce la necesitatea utilizrii tehnicii Greedy. Din alt punct de vedere, nu tuturor problemelor li se pot aplica algoritmi de acest tip. Ce este de fcut? 36

=> Pentru problemele pentru care nu se cunosc algoritmi care necesit timp polinomial, se caut soluii, chiar dac nu obine, dar apropiate de acestea, dar care au fost obinute n timp util. Multe din aceste soluii sunt obinute cu Greedy. Astfel de algoritmi se numesc algoritmi euristici. n continuare, vom da mai multe exemple de probleme care se rezolv cu Greedy.

Probleme pentru care Greedy obine soluia optim Problema spectacolelor.


ntr-o sal, ntr-o zi, trebuie planificate n spectacole. Pentru fiecare spectacol se cunoate intervalul n care se desfoar: [st, sf]. Se cere s se planifice un numr maxim de spectacole astfel nct sa nu se suprapun. Rezolvare. Fie o planificare optim a spectacolelor (un numr maxim k de spectacole i1, i2...ik. unde i1, i2,... ik aparine {1,2,...N } i spectacolul ij, are loc naintea spectacolului ij+1. O astfel de planificare ndeplinete condiia: spectacolul ij+1 ncepe dup terminarea spectacolului ij. => 0 consecin imediat a condiiei de mai sus este: spectacolul i, ia sfrit naintea terminrii spectacolului tj+1 (consecina nu implica un efort de gndire deosebit). Vom construi o soluie dup urmtorul algoritm: P1 sortm spectacolele dup ora terminrii lor; P2 primul spectacol programat este cel care se termin cel mai devreme; P3 alegem primul spectacol dintre cele care urmeaz n sir ultimului spectacol programat care ndeplinete condiia c ncepe dup ce s-a terminat ultimul spectacol programat; P4 dac tentativa de mai sus a euat (nu am gsit un astfel de spectacol) algoritmul se termin, altfel se programeaz spectacolul gsit i algoritmul se reia de la pasul 3. Lem. Algoritmul de mai sus conduce la soluie optim (numr maxim de spectacole programate). Demonstraie. Fie s1, s2,...si spectacolele ordonate ca mai sus si o soluie optim i1, i2, , ik. => Dac l=k rezult c solutia gsit este optim q.e.d. => Daca l>k rezult ca soluia i1, i2,... ik, nu este optim (se contrazice ipoteza); => Dac l<k procedm ca mai jos. Presupunem c i1 diferit s1. In acest caz putem nlocui i1, (n cadrul soluiei optime) cu s1, (s1 este primul spectacol care se termina si daca ar figura pe alt poziie n cadrul soluiei optime, se contrazice ipoteza), In concluzie, solutia s1, i2,... ik, rmne optim. Fie t mai mic sau egal l primul indice pentru care si diferit it. Soluia s1, s2,...st-i it... este optim. nlocuim it cu st. nlocuirea este posibil pentru c: => st, nu figureaz in cadrul soluiei optime ntre primele t-1 componente; => st, nu figureaz ntre ultimile poziii ale soluiei optime (ncepnd de la poziia t+1) pentru c in acest caz nseamn c st ncepe dup terminarea lui it, caz in care se contrazice modul n care a fost obinut de algoritm alegerea lui st. Procednd astfel, la un moment dat soluia optima este de forma: s1, s2,... si..ik. Aceasta nseamn c dup si a mai fost posibil sa adugm alte spectacole. Se contrazice modul de obinere al soluiei de ctre algoritm (acesta se oprete atunci cnd nu se mai poate programa nici un spectacol) De aici, rezult k=l si c algoritmul obine soluia optim q.e.d. Acest program se ncadreaz in tehnica Greedy pentru c: la un pas se alege un spectacol (cu ora de terminare mai mare dect ora de terminare a ultimului spectacol programat iar dac este posibil (dac are ora de nceput dup ora de 37

sfrit a ultimului spectacol programat) este adugat soluiei. Odat adugat (programat) un spectacol, acesta rmne in cadrul soluiei. program SPECT; type spectacol=array[1..2,1..10] of integer; ordine=array[1..10] of integer; var s:spectacol; o:ordine; n,i,h1,m1,h2,m2:integer;

procedure sortare; var gata:boolean; m,i :integer; begin repeat gata:=true; for i:=1 to n-1 do if s[2,o[i]]>s[2,o[i+1]] then begin m:=o[i]; o[i]:=o[i+1]; o[i+1]:=m; gata:=false end until gata; end;

begin write ('n=') ; readln (n); for i:=1 to n do begin o[i]:=i; write('ora de inceput pentru .spectacolul ',i, ' (hh min)=') ; readln(h1,m1); s[1,i] :=h1*60+m1; write('ora de sfirsit pentru spectacolul ', i , ' (hh min =') ;

readln(h2,m2); s[2,i]:=h2*60+m2; end; sortare; { Greedy } writeln('ordinea spectacolelor este');

writeln(o[ i ] ) ; for i:=2 to n do if s[1,o[i]]>=s[2,o[i-1]] then writeln (o[i]); end.

Problema rucsacului
O persoan are un rucsac cu care poate transporta o greutate maxim G. Persoana are la dispoziie n obiecte si cunoate pentru fiecare obiect greutatea si ctigul care se obine n urma transportului su la destinaie. Se cere s se precizeze ce obiecte trebuie s transporte persoana n aa fel nct ctigul sa fie maxim. O precizare n plus transform aceast problema n alte dou probleme distincte. Aceast precizare se refer la faptul c obiectele pot fi sau nu tiate pentru transportul la destinaie. In prima situaie, problema poart numele de problema continu a rucsacului, iar n a doua avem problema discreta a rucsacului. Aceste dou probleme se rezolv diferit, motiv pentru care ele sunt prezentate separat. Varianta continu a problemei rucsacului este tratat n acest paragraf iar cea discret va fi tratat cu ajutorul programrii dinamice. Problema continu a rucsacului, n care persoana are posibilitatea s taie obiectele. n acest fel, se poate obine o ncrcare mai eficient a rucsacului. Algoritmul este urmtorul: se calculeaz, pentru fiecare obiect n parte, eficiena de transport rezultat prin mprirea ctigului la greutate (de fapt, acesta reprezint ctigul obtinut prin transportul unitii de greutate); obiectele se sorteaz n ordine descresctoare a eficienei de transport si se preiau n calcul n aceast ordine; ctigul iniial va fi 0, iar greutatea rmas de ncrcat va fi greutatea rucsacului; 38

att timp ct nu a fost completat greutatea maxim a rucsacului si nu au fost luate in considerare toate obiectele, se procedeaz astfel: dintre obiectele nencrcate se selecteaz acela cu cea mai ridicat eficien de transport i avem dou posibiliti: acesta ncape n totalitate n rucsac, deci se scade din greutatea rmas de ncrcat greutatea obiectului, la ctig se cumuleaz ctigul datorat transportului acestui obiect; se tiprete 1, n sensul c ntregul obiect a fost ncrcat; obiectul nu ncape n totalitate n rucsac, caz n care se calculeaz ce parte din el poate fi transportat, se cumuleaz ctigul obinut cu transportul acestei pri din obiect, se tiprete procentul care s-a ncrcat din obiect, iar greutatea rmas de ncrcat devine 0. Demonstraia este asemntoare celei de la problema anterioar i o propunem ca tem. Vom considera un exemplu numeric. Greutatea care poate fi transportat cu ajutorul rucsacului aste 3 Avem la dispoziie 3 obiecte. Greutatea i ctigul pentru fiecare obiect sunt prezentate mai jos:

Eficiena de transport este 1 pentru primul obiect, 4 pentru al doilea si 2 pentru al treilea. In concluzie, obiectul 2 se ncarc n ntregime n rucsac, obinnd un ctig de 4 i rmne o capacitate de transport de dou uniti de greutate. Se ncarc 2/3 din obiectul 3 (care este al doilea n ordinea eficienei de transport) pentru care se obine ctigul 4. Ctigul obinut n total este 8. Se remarca strategia GREEDY prin alegerea obiectului care va fi transportat, alegere asupra creia nu se revine. program rucsac; type vector=array [1..9] of real; var c,g,ef:vector; n,i,man1:integer; gv,man,castig:real; inv:boolean; ordine:array [1..9] of integer;

begin write('Greutatea ce poate fi transportata='); readln(gv); write('Numar de obiecte='); readln(n); for i:=1 to n do begin write('c[',i,']='); readln(c[i]); write('g[',i,']='); readln(g[i]); ordine[i]:=i; repeat inv:=false; for i:=1 to n-1 do if ef[i]<ef[i+1] then begin man:=ef[i]; ef[i]:=ef[i+1]; ef[i+1]:=man; man:=c[i]; c[i]:=c[i+1]; c[i+1] :=man; man:=g [ i ]; g[i]:=g[i+1]; g[i+1]:=man; inv:=true; ef[i]:=c[i]/g[i] end;

man1:=ordine[i]; ordine[i]:=ordine[i+1]; ordine[i+1]:=man1 end until not inv; castig:=0; i:=1; while (gv>0) and (i<=n) do begin if gv>g[i] then begin writeln('Obiectul ',ordine[i],' ', 1); gv:=gv-g[i]; castig:=castig+c[i] end else begin writeln('Obiectul ',ordine[i],' ',gv/g[i]:1:2); castig:=castig+c[i]*gv/g[i]; gv:=0; end;

i:=i+1; end; writeln('Castig total=',castig:3:2); end.

39

Problema Maxim (maximizarea valorilor unei expresii).


Se dau n numere ntregi nenule b1, b2, .... bn si m numere ntregi nenule a1,. a2, .... am. S se determine o submulime a mulimii B={b1, b2, .... bn} care s maximizeze valoare expresiei: E = a1x1+a2 x2 + ... + amxm, tiind c n mai mare sau egal ca m i c xl aparine {b1,b2, ... bn}. Rezolvare. Vom sorta cresctor cele dou mulimi de numere ntregi A si B. Pentru nceput, vom considera m=n. Lem. n aceste condiii (elementele celor dou mulimi fiind sortate) suma maxim este E=a1b2+a2b2+...+ambm. Demonstraie. O posibilitate de cuplare poate fi scris sub form de permutare. Fie permutarea:

unde i1, i2,... im sunt tot numerele 1, 2,, m, luate n alt ordine. Pentru permutarea considerat suma va fi:

Vom arta, c dintre toate permutrile, permutarea identica este cea care maximizeaz suma (Atenie! Numerele sunt sortate b1<b2...<bm caz n care permutrii identice i corespunde irul b1, b2,...bm). Fie o permutare oarecare. diferit de cea identica. Privim permutarea ca un ir de numere naturale. Da exemplu, permutarea

o privim ca pe un sir de 5 numere naturale: 3, 1, 2, 5, 4. Sortam cresctor sirul, reinnd in acelai timp rezultatele pariale: Astfel, obinem:

Evident, cu orice permutare a mulimii primelor m numere naturale, putem proceda asemntor. Se obine astfel un sir de permutri, ir caracterizat de faptul c ultimul su element este permutarea identica: ******************=e (prin e am notat permutarea identic. Pe de alt parte dac * si *** sunt dou permutri vecine din sir, ele ofer printr-o singur inversare de elemente (corect inversiune). Fie **** si **** sumele asociate celor dou permutri, Avem **************** Cele dou sume sunt identice, excepie fcnd doi termeni (cei care au fost inversai cnd s-a obinut permutarea). Dup simplificri, rmne: akbk+ak+1bk+1<akbl+1+ak+1bl,+ak+1bl, unde bl>bi+1, ak<ak+1 inegalitatea se transform n: ak(bl-bl+1)-ak+1(bl-bl+1)<0; i apoi n (ak-ak+1)(bl-bl+1)<0, inegalitate evident. Reconsidernd sumele avem: 40

********************** q.e.d. Exemplu n=3, m=3, a1=7, a2=-5, a3=2; b1=-1, b2=2, b3=-3. Sortm a si b i obinem n urma sortrii: a1=-5, a2=2. a3=7; b1=-3, b2=-1, b3=2; Emax=-5x(-3)+2x(-1)+7x2=27. Prob. Scriem b n celelalte moduri cu putin: -1, 2, -3 => E=-5x(-1)+2x(2)+7x(-3)=-12<27; 2, -1, -3 => E=-5x2+2x(-1)+7x(-3)=-33<27; 2, -3, -1 => E=-5x2+2x(-3)+7x(-1)=-23<27; -1, -3, 2 => E=-5x(-1)+2x(-3)+7x2=23<27; -3, 2, -1 => E=-5x(-3)+2x2+7x(-1)=12<27. Trecem la cazul general, n>m. Sortm elementele celor dou mulimi. 1) S presupunem c elementele mulimii A sunt numere ntregi pozitive (naturale). Considerm elementele ordonate ale mulimii B ca un sir de numere ntregi n secven strict cresctoare. Din start, trebuie selectat un subir de m elemente. Conform rezultatului obtinut anterior, subirul trebuie s fie strict cresctor ( contrar, prin sortarea sa am obine un rezultat mai bun). Cum elementele mulimii B sunt ordonate cresctor, rmne condiia ca alegerea s fie un subir cu m componente. Evident, putem alege mai multe subiruri care ndeplinesc condiia ceruta. Din ipotez, elementele mulimii A, ordonate sunt pozitive. Dup cum se tie, produsul unui numr natural, cu un numr ntreg este cu att mai mare, cu ct acesta din urm este mai mare. n aceste condiii, subirul ales va fi alctuit din ultimele m elemente ale irului de n elemente ale mulimii B, ordonate cresctor. 2) S presupunem c elementele mulimii A sunt negative. Produsul dintre un numr negativ i altul, este cu att mai mare, cu ct acesta din urm este mai mic. Prin urmare, subirul ales este subirul format din primele m elemente ale irului B. 3) n cazul general, cnd mulimea A are k elemente negative i p elemente pozitive (k+p=m) vom alege primele k elemente ale subirului A si ultimele p elemente ale subirului B. Exemplu: a1=-3, a2=-1, a3=2; b1=1, b2=2. b3=3, b4=4; Emax= -3x1+(-1)x2+2x4=3. Alte posibiliti: E=-3x1+(-1)x2+2x3=1<3; E=-3x2+(-1)x3+2x4=-1<3; E=-3x1+(-1)x3+2x4=2<3. Conform algoritmului propus, prezentm programul de mai jos: program IoanMaxim; type vector=array[1..9] of integer; var a,b:vector; m,n,i,ind,Maxim:integer; 41

procedure sortare(var v:vector; n:integer); var gata:boolean; i,m:integer; begin repeat gata:=true; for i:=1 to n-1 do if v[i]>v[i+1] then begin m:=v[i];

v[i]:=v[i+1]; v[i+1]:=m; gata:=false end until gata; end; begin write('m='); readln(m); for i:=1 to m do begin write (' a [', i,']='); readln (a [i ] ); end;

write ('n=') ; readln(n); for i:=1 to n do begin write('b[',i,']='); readln(b[i]); end; sortare(a,m); sortare(b,n); ind:=0; while a[ind+1]<0 do ind:=ind+1; {Greedy}

Maxim:=0; for i:=1 to ind do begin writeln (a[i],'*',b[i]); Maxim:=Maxim+a[i]*b[i]; end; for i:=ind+1 to m do begin writeln(a[i],'*',b[n-m+i]); Maxim:=Maxim+a[i]*b[n-m+i]; end; writeln( 'Maxim=' ,Maxim) ; end. Eficiena algoritmului Greedy este evident. Prima idee ar fi fost s selectm toate submulimile de m elemente ale mulimii de n elemente i pe acestea s le permutm, reinnd valoarea maxim. In acest fel, aveam de probat A* mulimi. Pentru aceast problem mecanismul de alegere a fost prezentat si orice element ales este si posibil de adugat.

Probleme la care Greedy nu conduce la soluia optim


Am artat faptul c exist probleme pentru care nu se cunosc algoritmi care s conduc in timp polinomial la soluia optim. In astfel de cazuri se renun la optimalitate, se caut soluii apropiate de cea optim dar care s fie obinute n timp util (polinominal). De multe ori, pentru obinerea lor se folosete tehnica Greedy. Evident, n astfel de cazuri nu se mai pune problema unei demonstraii. De reinut faptul c exist i alte metode care gsesc astfel de soluii.

Problema colorrii hrilor.


N ri sunt date precizndu-se relaiile de vecintate. Se cere s se determine o posibilitate de colorare a hrii (cu cele n ri), astfel nct s nu existe ri vecine colorate la fel. Se tie faptul ca pentru colorarea unei hri sunt necesare cel mult 4 culori. Problema ar fi putut cere colorarea hrii utiliznd un numr minim de culori. Din pcate nu se cunosc algoritmi polinomiali care s poat colora o hart prin utilizarea a numai 4 culori. Totui, colorarea hrilor este o problem real. Presupunnd un numr mare de ri suntem obligai s folosim o metod euristic, n care sa obinem o colorare admisibil, chiar dac nu utilizeaz un numr minim de culori (poate chiar mai mare dect 4) Algoritmul propus este urmtorul: -ara 1 va avea culoarea 1; -presupunem colorate primele i-1 tari: ara i va fi colorat cu cea mai mic culoare, cu un numr ataat mai mare sau rile vecine s nu fie colorat la fel.

egal cu 1, astfel nct nici una din

42

program colorare; var a:array[1..50,1..50] of integer; c:array[1..50] of integer; n,i,j,cl:integer; gasit:boolean; begin write ( 'nurnar tari: '); readln (n); for i:=1 to n do for j:=1 to i-1 do begin write('a[',i,',',j,']='); readln(a[i,j]); a[j,i]:=a[i,j] end; c[1]:=1; for i:=2 to n do begin cl:=1; repeat gasit:=false; for j:=1 to i-1 do if (a[i,j]=1) and (cl=c[j]) then gasit:=true; if not gasit then c[i]:=cl else cl:=cl+1; until not gasit; end; for i:=1 to n do writeln ('tara ',i,' culoarea ',c[i]); end.

Sritura calului
Problema, este binecunoscut, nu o mai enunm aici. Am vzut ca pentru o valoare a lui n, nu foarte mare (de exemplu 8) timpul de calcul este foarte mare. n acest paragraf vom propune o rezolvare Greedy, cu rezultate excelente (care circul ntre rezolvitori). ntruct nu cunosc demonstraia (presupunnd c ar exista), am introdus aceasta problem la Greedy euristic. n ce const algoritmul? Calul pornete din poziia 1,1. La fiecare pas alegem acea mutare care plaseaz calul ntr-o pozitie din care la urmtoarea mutare, avem ct mai puine posibiliti de a muta din nou. O explicaie (nu demonstraie) ar putea fi urmtoarea: calul ocup poziii ct mai puin accesibile (care cu alt ocazie ar fi greu de atins). Principiul este extrem de interesant i merit ncercat i pentru alte probleme.

program cal; type tabla=array[-1..25,-1..25] of integer; var t:tabla; i,j,n,min,nr,nr1,l,c,l1,c1:integer; procedure poz(l,c:integer; var nr:integer); begin nr:=0; if t[l-1,c+2]=0 then nr :=nr+1; if t[l+1,c+2]=0 then nr :=nr+1; if t[l+2,c+1]=0 then nr :=nr+1; if t[l+2,c-1]=0 then nr :=nr+1; 43

if t[l+1,c-2]=0 then nr :=nr+1; if t[l-1,c-2]=0 then nr :=nr+1; if t[l-2,c-1]=0 then nr :=nr+1; if t[l-2,c+1]=0 then nr :=nr+1; end; begin write ('n='); readln(n) ; for i:=1 to n do for j:=1 to n do t[i,j]:=0; for i:=0 to n+1 do begin t[0,i]:=1; t[-1,i]:=1; t[n+1,i]:=1; t[n+2,i]:=1;

t[i,0]:=1; t[i,-1]:=1; t[i,n+1]:=1; t[i,n+2]:=1; end; l:=l; c:=l; t[l,c]:=1; repeat min:=9; writeln(l, ' ' ,c); nr1:=nr1+1; if t[l-1,c+2]=0 then begin poz(l-1,c+2,nr); if min>nr then begin l1:=l-1; c1:=c+2; min:=nr end; end; if t[i+l,c+2]=0 then begin poz(i+l,c+2,nr); if min>nr then begin l1:=l+1; c1:=c+2; min:=nr end; end; if t[l+2,c+1]=0 then begin poz(l+2,c+1,nr); if min>nr then begin l1:=l+2; c1:=c+1; min:=nr end; end; if t[l+2,c-1]=0 then begin poz(l+2,c-1,nr); if min>nr then begin l1:=l+2; c1:=c-1; min:=nr; end; end; if t[l+1,c-2]=0 then begin poz(l+1,c-2,nr); if min>nr then begin l1:=l+1; c1:=c-2; min:=nr; end; end; if t[l-1,c-2]=0 then begin poz(l-1,c-2,nr); if min>nr then begin l1:=l-1; c1:=c-2; min:=nr end; end; if t[l-2,c-1]=0 then begin poz(l-2,c-1,nr); if min>nr then begin l1:=l-2; c1:=c-1; min:=nr; end; end; if t[l-2,c+1]=0 then begin poz(l-2,c+1,nr) ; 44

if min>nr then begin l1:=l-2; c1:=c+1; min:=nr end; end; l:=l1; c:=c1; t[l,c]:=1; until min=9; if nr1=n*n then writeln ('tentativa reusita') else writeln ('tentativa esuata') end.

Problema comis voiajorului


Un comis - vaiajor pleac dintr-un ora, trebuie s viziteze un numr de orae si s se ntoarc n oraul de plecare cu efort minim (de exemplu de, timp, caz n care costul unei muchii a grafului reprezint timpul necesar comis-voiajorului pentru a ajunge dintr-un ora n altul). O modalitate de rezolvare a acestei probleme este de a genera toate ciclurile (aa cum am artat n Capitolul 2) i de a-l reine pe cel de drum minim. O astfel de abordare conduce la un algoritm neperformant (timp exponenial), Din acest motiv, aceast soluie nu are valoare practic. Pentru aceast problem nu se cunosc algoritmi care s nu necesite un timp exponenial, iat motivul pentru care renunm la condiia de optimalitate i ne mrginim s gsim un ciclu care s treac prin toate oraele cu un cost, pe ct posibil, redus. Algoritmul este urmtorul: se citete matricea costurilor A i nodul de pornire vi; dac v1,v2,...,vk este un lan deja ales, dup caz, se procedeaz astfel: daca X=(v1,v2,...vk), se alege muchia (v1,v2); dac X-t(v1,v2,...,vk), se alege muchia de cost minim care are o extremitate n vk, iar cealalt n vk+1, (unde vk+1 nu aparine mulimii (v1,v2,...,vk) Observaie: Odat cu muchia se selecteaz si un nod. La fiecare pas se selecteaz un nod. Din acest motiv algoritmul prezentat se ncadreaz in tehnica GREEDY. Pentru graful din figura urmtoare se consider nodul de pornire 1.

Dintre toate muchiile care au extremitatea n nodul 1 se alege muchia (1,2) pentru c aceasta are costul minim (1). Dintre toate cu extremitatea n nodul 2 se alege muchia (2,3). In continuare, se aleg muchiile (3,5), (5,4), (4,1) si astfel se obine graful din figura urmtoare.

45

Acest graf (de cost 18) nu are costul minim. De exemplu, graful din figura de mai jos are costul 15.

n program, vectorul S reine nodurile selectate (S(i)=1, dac nodul a fost selectat si S(i)=0 Tn caz contrar).

program cv; type matrice=array [1..9,1..9] of integer; vector=array [1..9] of integer; var a:matrice; s:vector; n, i, j, v, vl, v2,min, cost:integer; begin write('n='); readln(n); for i:=1 to n-1 do for j:=i+1 to n do begin write ('a[',i,',',j,']='); readln(a[i,j]); a[j,i]:=a[i,j] end; for i:=1 to n do begin s[i]:=0; a[i,i]:=0 end; write('Nodul de pornire este='); readln(v); s[V]:=1; v2:=v; cost:=0; writeln(v); for i:=1 to n-1 do begin min:=30000; for j:=1 to n do if (a[v2,j]<>0) and (s[j]=0) and (min>a[v2,j]) then begin min:=a[i,j]; vl:=j end; v2:=vl; s[v2]:=1; cost:=cost+min; writeln(vl) end; cost:=cost+a[v2,v]; writeln(v); writeln('Cost total=',cost) end. O mbuntire a algoritmului se poate aduce dac se reiau calculele considernd, pe rnd, ca nod de pornire {1,2,-.,n} modificarea n acest sens a programului se propune ca tem.

Algoritmi genetici
Utilizarea acestor algoritmi este o tehnic de ultim ora, aplicabil cu succes problemelor pentru care nu se cunosc algoritmi n timp polinomial. Algoritmii generici nu sunt specifici tehnicii Greedy. Motivul pentru care au fost introdui n acest capitol este dat de faptul c se ncadreaz n categoria algoritmilor euristici. S ne imaginm situaia (din afara informaticii) n care se d un cmp de 1 km 2 presrat cu tot felul de obiecte cu diverse valori i se cere ca n timp de o or s selectm obiectul cel mai valoros. O explorare sistematica a cmpului necesit un timp cu mult mai mare. Avem o singura ans: ca dup o or s prezentm cel mai valoros obiect gsit in acest timp. Este puin probabil ca acesta s fie cel mai valoros de pe cmp, dar este cel mai valoros pe care l-am gsit. Aceeasi problem dac o rezolvm din nou, poate duce la o soluie mai bun sau...mai proast. Reinem faptul c, n cazul problemelor de acest tip, calitatea soluiei finale depinde i de ans, ns nu numai de ea. Cred c suntei de acord cu faptul c o soluie pentru o problem de acest tip depinde i de modul n care organizm cutarea n timpul pe car il avem la dispoziie. De multe ori aceasta se face respectnd criterii matematice riguroase, care sunt mprumutate din teoria probabilitilor. Revenind la exemplul nostru (didactic), am putea selecta eantioane de obiecte din diversele locuri de pe cmp, pentru ca apoi s organizm cutarea acolo unde densitatea obiectelor valoroase este mai mare. Nimeni nu ne asigur c n acest fel vom da peste obiectul cel mai valoros, dar ansele de gsi la sfritul timpului de cutare a un obiect cu a valoare apropiat de acesta sunt mai mari. 46

Renunnd la exemplul de mai sus, care nu are alt rol dect de a uura nelegerea necesitii existenei unor tehnici euristice de cutare n spaiul soluiilor, vom prezenta, in continuare algoritmii genetici, care abordeaz probleme de tipul celei de mai sus Prezentarea acestora se va face pe un exemplu (deja clasic) i anume de a calcula maximul unei funcii definite pe mulimea numerelor reale. Acesta nu este un exemplu semnificativ, ns l preferm pentru simplitatea sa. Alegerea exemplului de mai sus poate indica urmtoarea ntrebare: care este motivul pentru care nu calculm maximul functiei aa cum am fost obinuii si anume prin a da variabilei x un numr suficient de mare de valori din domeniul de definiie considerat? S nu uitm faptul ca o funcie poate avea salturi semnificative pentru variaii mici ale variabilei x (este posibil s pierdem punctele semnificative). Pe de afl parte, problema devine mult mai complicata dac funcia este definit pe R (funcie de m variabile reale). Pentru exemplul considerat nu vom scrie programul. Motivele care ne determin s procedm astfel sunt date pe de o parte de simplitatea unui astfel de program, pe de alta parte de faptul c noi considerm c o persoana dornic s neleag funcionarea acestor algoritmi are pregtirea necesara pentru a scrie un program. Fie funcia f:[a,b]->R. Se cere s determinm valoarea maxim pe care o ia funcia pe domeniul de definiie considerat. Alegerea funciei o va face cititorul. Este bine s fie aleas o funcie pentru care se cunoate valoarea maxim (pentru a verifica dac ajungem la un rezultat apropiat de acesta). De asemenea se recomand ca funcia s conin mai multe puncte de maxim pe domeniul considerat, din care numai unul este maxim absolut (pentru a verifica daca algoritmul se blocheaz sau nu ntr-un optim local). n continuare vom descrie algoritmul de tip genetic. Etapa 1. Crearea unei populaii iniiale de cromozomi. Ideea de baza n aceast faz este de a considera mai multe valori pentru variabila x (evident, toate n domeniul de definiie considerat), alese arbitrar. Prima ntrebare care poate apare este urmtoarea: care este numrul acestor valori? Un numr mai mare de valori va spori ansele de a ajunge la o soluie apropiat de cea optim, dar va mri timpul de rulare. Din acest motiv vom primi ca parametru de intrare numrul acestor valori (fie el NR). Aceasta ne ofer posibilitatea de a rula programul n condiii diferite i de a compara rezultatele obinute (procedeu mult folosit n analiza algoritmilor euristici). Toate valorile pe care le vom alege vor fi cuantificate sub form de cromozomi. Pentru nceput, vom considera un cromozom o succesiune de k poziii binare. Care este valoarea lui k pentru problema noastr? Alegerea acestei valori depinde de mulimea valorilor posibile pe care dorim s le ia variabila x. S fim mai explicii n domeniul de definiie considerat exist o infinitate de valori pe care le poate lua variabila x. Din motive lesne de neles nu le putem considera pe toate. Totui, putem considera un numr suficient de mare. S presupunem c dorim s considerm 220 valori posibile pentru x. n acest caz vom considera k=20. Cunoatem ca pe 20 de poziii binare se pot reine numere ntre 0 si 2 30-1. In exemplul nostru, un cromozom va fi dat de o succesiune de 20 poziii binare si vom avea NR cromozomi. Observaii. n exemplul considerat spaiul soluiilor va conine 2 20 elemente. Se cere acel element care maximizeaz funcia. Orice cromozom va conine o valoare a variabilei x i avem NR cromozomi. Un cromozom va cuantifica o soluie admisibila (pentru ea funcia are o anumit valoare, chiar dac nu cea optima). Alegerea celor NR cromozomi se face aleator. Pentru fiecare dintre ei se genereaz aleator o succesiune de 20 de valori 0 sau 1. Se poate citi ca parametru de intrare si numarul k. O valoare de 0 sau 1 aflat pe o anumit poziie a unui cromozom o vom numi gen. Cum convertim un cromozom ntr-o variabil real din domeniul de definiie considerat? Vom considera intervalul [a, b] mprit 1n 2k-1 pri egale. Valoarea a va fi dat de cromozomul care are pe toate pozitiile 0, iar valoarea b de acela care are pe toate poziiile 1. Restul valorilor se distribuie proporional. Exemplu: k=2. Intervalul considerat va fi mprit in 4 pri egale. Fiecare punct al diviziunii (aa se numete in matematic o astfel de mprire) va fi numerotat ntre 0 i 3. Prin mecanismul artat mai sus, am stabilit o modalitate de conversie de la un cromozom la o valoare x din domeniul de definiie. 47

Caracteristic algoritmilor genetici este faptul c opereaz simultan cu mai multe soluii admisibile. Se numete cromozom o soluie admisibil scris sub form de vector. Componentele vectorilor pot lua diverse valori (nu neaprat 0 sau 1 ca n exemplu). O component a vectorului cromozom se numete gen. Observaie. Multimea valorilor pe care le poate lua o gen este finit. Etapa 2. Obinerea soluiei. De la bun nceput precizm faptul c un astfel de algoritm se ruleaz in limita timpului disponibil. Deci operaiile corespunztoare acestei etape se repet in limita timpului disponibil. Cei NR cromozomi obinui in etapa anterioar i vom nota prin C1,C2,...CNR. 2.1 Evaluarea populaiei. Pentru fiecare cromozom din populaie se evalueaz funcia obiectiv.

Fiecare cromozom va fi convertit ntr-o variabil real din domeniul de definiie, pentru care se calculeaz funcia al crei maxim se cere. 2.2 Pentru fiecare cromozom n parte se calculeaz probabilitatea cumulativ de selecie. 2.2.1. Se nsumeaz valorile funciilor obiectiv obinute pentru fiecare cromozom n parte.

2.2.2 Pentru fiecare cromozom n parte se calculeaz probabilitatea de selecie:

2.2.3 Pentru fiecare cromozom n parte se calculeaz probabilitatea cumulativ de selecie:

Observaii. irul q1, q2,......qNR va fi cresctor, ultima valoare va fi 1, dup cum se poate observa cu uurin. Cu ct cromozomul Cl+1 conine o valoarea pentru care se obine o valoare mai mare pentru funcia obiectiv, cu att diferena ntre ql+1, i ql este mai mare. irul probabilitilor cumulative de selecie constituie o diviziune a intervalului [0,1].

Crearea unei populaii intermediare de cromozomi.


Se selecteaz NR numere uniform aleatoare din (0,1) (cte unul pentru fiecare cromozom n parte). Dac un numr aleator se gsete n intervalul (0,q1), cromozomul c1 este selectat. Dac numrul aleator se gsete in intervalul (q,qi+1) este selectat ci+1 Observaii: n cadrul acestei populaii un cromozom poate apare de mai multe ori. Se observ faptul c dac cromozomul Cn,i este mai valoros, intervalul (qi,qi+1) este mai mare. n concluzie, exist o probabilitate mai mare ca acesta s fie selectat n noua populaie. Aceasta ns nu garanteaz selectarea automat a acestuia.

48

Selecia cromozomilor supui mperecherii.


Pentru fiecare cromozom din populaia intermediar se selecteaz un numr aleator din (0,1). Dac acesta este mai mic dect o probabilitate controlat pc (parametru de intrare) cromozomul respectiv este selectat spre reproducere. Observaie. Alegerea probabilitii controlate pc este de mare importan. De exemplu, dac se consider pc=0,1 aproximativ 10% din cromozomi vor fi supui mperecherii. O prim ntrebare care se poate pune este urmtoarea: de ce-nu sunt supui mperecherii toi cromozomii? Populaia supus mperecherii se presupune a fi valoroas. Dup cum vom vedea, operaia de mperechere poate duce la soluii mai bune sau mai proaste (se efectueaz mecanic, fr a exista un control al rezultatului). Prin urmare, nu suntem interesai s distrugem ntreaga populaie de cromozomi (riscm prea mult). Pe de alt parte, a nu supune populaia de cromozomi mperecherii nseamn s nu gsim soluii mai bune.

ncruciarea (mutaii ncruciate).


Cromozomii se ncrucieaz n felul urmtor: - primul selectat cu al doilea selectat; - al treilea cu al patrulea; '

Observaie: dac populaia supusa reproducerii conine un numr impar de cromozomi, se renun la ultimul. ncruciarea a doi cromozomi se realizeaz astfel: - se extrage un numr natural, aleator, t ntre 0 si numrul de gene (comun al celor doi cromozomi). - se obin doi noi cromozomi astfel: - primul va conine primele t gene ale primului cromozom si ultimile k-t ale celui de al doilea cromozom: - al doilea va conine primele t gene ale celui de-al doilea cromozom intrat in reproducere si ultimele k-t gene ale primului cromozom supus reproducerii. - cei doi cromozomi obinui vor nlocui n populaie pe cei care s-au reprodus.

Mutaii simple
Populaiei obinute i se aplic, cu o probabilitate mic p, (parametru de intrare, valoare apropiat de 0) mutaii simple n care se schimb coninutul unei gene. Astfel, pentru fiecare gena a fiecrui cromozom se extrage un numr aleator r aparine (0,1). Dac numrul este mal mic dect p, coninutul genei se schimb din 0 n 1 sau invers. n urma derulrii etapei 2 se obine o nou populaie de cromozomi. Etapa se repet in limita timpului disponibil. Observaii. 1) Este bine s se rein in permanen cromozomul cel mai valoros (dei probabilitatea de a obine o populaie mai proast de cromozomi este foarte mic). 2) Nu putem s nu observm uriaa asemnare ntre algoritmii genetici i viata de toate zilele. Dei cromozomii valoroi au toate ansele s ajung n noua populaie, exist si ansa ca unii dintre ei s se piard, important nu este att cromozomul ci populaia de cromozomi. Aceasta trebuie s evolueze. 3) Nu exist reete care s conduc cu certitudine la rezultate valoroase. Programul se va rula cu diveri parametri de intrare, reinnd cal mai bun rezultat. 4) O problem important care apare la utilizarea unui algoritm de acest tip este urmtoarea: in cadrul mutaiilor ncruciate sau simple se pot obine cromozomi care nu mai sunt soluii admisibile (pentru alte probleme). Din acest motiv, de multe ori sunt necesari algoritmi, de corectare (dintr-un cromozom care nu este soluie admisibil s obinem 49

unul care este). Aplicarea unui algoritm de corectare poate duce la pierderea eficienei algoritmilor genetici. Cine ne garanteaz c dup aplicarea coreciei se obine un cromozom valoros? Din acest motiv, corectarea se va face dup criterii care in de specificul fiecrei probleme.

Probleme propuse
1) O numrtoare de la 0 la 2n-1 n codul Gray are proprietatea c oricare dou numere consecutive difer, in reprezentarea lor binar, prin exact un bit (bineneles, numerele nu se repet). Exemplu: numram de la 0 la 7 (pe trei bii); 000, 001, 011, 010. 110, 111, 101, 100 S se scrie programul care numr pe 1*N*30 bii. 2) Se consider o mulime X cu n elemente. Sa se elaboreze un algoritm eficient i s se scrie un program pentru generarea irului tuturor submulimilor lui X, A1, A2, ... astfel nct Ai+1 se obine din AL prin scoaterea sau adugarea unui element. 3) Se dau n compozitori i pentru fiecare din ei, anii de viaa. S se tipreasc numrul maxim de compozitori contemporani. 4) Se citesc de la tastatur un numr natural N i un numr real A. S se genereze o mulime de N puncte n plan, P1, P2, ... Pn astfel nct: 1. P1P2=P2P3=...=PnP=A i 2. PIPJ<A oricare ar fi 1<i<j<N. 5) Se citesc numerele naturale k si n, k<n. a) Afiai toate variantele posibile de tablouri cu n linii i n coloane care ndeplinesc urmtoarele condiii simultan: 1) Conin toate numerele de la 1 la n2 o singur dat; 2) Pe fiecare linie numerele sunt aezate n ordine cresctoare; 3) Suma elementelor de pe coloana k este minim. b) Aceeai problema pentru tablourile ndeplinind condiiile 1 i 2 de mai sus i condiia: 3) Suma elementelor de pe coloana k este maxim. Observaie: Trebuie s se afieze mai nti numrul de variante posibile i apoi se vor afia tablourile. 6) Se consider primele N caractere din codul ASCII, ncepnd cu caracterul 'a'. Cu ele se formeaz cuvinte de lungime m; fiecare cuvnt este format din caractere distincte. Cuvintele se consider n ordine lexicografic (cea din cartea de telefon). Se cer urmtoarele: 1. Fiind dat un astfel de cuvnt, s se determine numrul su de ordine; 2. S se determine cuvntul cu numrul de ordine dat. 7) Rezolvai problema comis voiajorului utiliznd algoritmi genetici.

50