Cap 5 Sortare Si Cautare FN

9
Capitolul 5 Sortare şi căutare 5.1 Metoda de sortare prin generare şi testare Utilizând structura de control a limbajului Prolog, se poate realiza foarte simplu sortarea unei secvenţe de elemente, folosind metoda generare şi testare. Această metodă de rezolvare a problemelor, utilizată în inteligenţa artificială, dar puţin potrivită pentru o sortare, are la bază următoarea idee: o componentă generatoare construieşte soluţii candidate şi o a doua componentă, componenta de testare, verifică fiecare soluţie candidată pentru a vedea dacă este sau nu o soluţie a problemei. În acest fel, se pot obţine fie toate soluţiile problemei, fie una singură. Această metodă poate fi exprimată succint în Prolog astfel: gaseste(Solutie) :- genereaza(Solutie), testeaza(Solutie). Metoda este în general ineficientă, chiar în cazul problemelor tipice de inteligenţă artificială care necesită un proces de căutare a soluţiei. Metoda este extrem de ineficientă în cazul rezolvării problemelor de sortare, pentru care există algoritmi eficienţi de rezolvare. Cu toate acestea, se prezintă în continuare soluţia de sortare în ordine crescătoare a unei liste de întregi prin metoda generare şi testare ca exerciţiu Prolog. % sortare(+Lista, -ListaSortata) - sortare prin metoda generare si testare sortare(Lista, ListaSortata) :- permut(Lista, ListaSortata), ordonata(ListaSortata). % permut(+Lista, -PermutareLista) permut([], []). permut(Lista, [Prim | Rest]) :- elim(Prim, Lista, L), permut(L, Rest). % elim(+Element, +Lista, -ListaMinusElement) elim(Elem, [Elem | Rest], Rest). elim(Elem, [Prim | Rest], [Prim | L]) :- elim(Elem, Rest, L).

description

5

Transcript of Cap 5 Sortare Si Cautare FN

  • Capitolul 5

    Sortare i cutare

    5.1 Metoda de sortare prin generare i testare Utiliznd structura de control a limbajului Prolog, se poate realiza foarte simplu sortarea unei secvene de elemente, folosind metoda generare i testare. Aceast metod de rezolvare a problemelor, utilizat n inteligena artificial, dar puin potrivit pentru o sortare, are la baz urmtoarea idee: o component generatoare construiete soluii candidate i o a doua component, componenta de testare, verific fiecare soluie candidat pentru a vedea dac este sau nu o soluie a problemei. n acest fel, se pot obine fie toate soluiile problemei, fie una singur. Aceast metod poate fi exprimat succint n Prolog astfel:

    gaseste(Solutie) :- genereaza(Solutie), testeaza(Solutie).

    Metoda este n general ineficient, chiar n cazul problemelor tipice de inteligen artificial care necesit un proces de cutare a soluiei. Metoda este extrem de ineficient n cazul rezolvrii problemelor de sortare, pentru care exist algoritmi eficieni de rezolvare. Cu toate acestea, se prezint n continuare soluia de sortare n ordine cresctoare a unei liste de ntregi prin metoda generare i testare ca exerciiu Prolog.

    % sortare(+Lista, -ListaSortata) - sortare prin metoda generare si testare sortare(Lista, ListaSortata) :- permut(Lista, ListaSortata), ordonata(ListaSortata).

    % permut(+Lista, -PermutareLista) permut([], []). permut(Lista, [Prim | Rest]) :- elim(Prim, Lista, L), permut(L, Rest).

    % elim(+Element, +Lista, -ListaMinusElement) elim(Elem, [Elem | Rest], Rest). elim(Elem, [Prim | Rest], [Prim | L]) :- elim(Elem, Rest, L).

  • 88 CAPITOLUL 5

    % rel(+X, +Y) - verifica daca X si Y respecta relatia de ordine rel rel(X, Y) :- X =< Y.

    % ordonata(+Lista) - verifica daca Lista este sortata dupa relatia rel(X, Y) ordonata([ _ ]). ordonata([Prim, Secund | Rest]) :- rel(Prim, Secund), ordonata([Secund | Rest]).

    Se observ c sortarea se face prin generarea permutrilor elementelor din list i verificarea dac o permutare generat este o secven sortat. Componenta generatoare este predicatul permut(Lista, ListaSortata) i componenta de testare este predicatul ordonata(Lista). Dei implementarea este simpl, exploatnd facilitile nedeterministe ale limbajului Prolog, ea este foarte ineficient.

    5.2 Metoda de sortare prin inserie Sortarea prin inserie a unei liste de elemente L = [H | T] se poate exprima recursiv astfel: se sorteaz mai nti lista T n TS i apoi se insereaz elementul H n lista TS, acolo unde i este locul conform relaiei de ordine rel(X, Y). % isort1(+Lista, -ListaSortata) - sortare prin insertie, varianta 1 isort1([], []) :- !. isort1([H | T], LS) :- isort1(T, TS), insereaza(H, TS, LS).

    % rel(+X, +Y) - verifica daca X si Y respecta relatia de ordine rel(X, Y) :- X =< Y.

    % insereaza(+Element, +Lista, -ListaPlusElement) - % insereaza Element in Lista sortata astfel incat % ListaPlusElement sa ramana sortata insereaza(Elem, [], [Elem]). insereaza(Elem, [Prim | Rest], [Elem, Prim | Rest]) :- rel(Elem, Prim), !. insereaza(Elem, [Prim | Rest], [Prim | L]) :- not(rel(Elem, Prim)), insereaza(Elem, Rest, L). Predicatul cut folosit n definirea predicatului insereaza este un cut verde. Se poate rescrie predicatul insereaza folosind un cut rou astfel:

    insereaza(Elem, [], [Elem]). insereaza(Elem, [Prim | Rest], [Elem, Prim | Rest]) :- rel(Elem, Prim), !. insereaza(Elem, [Prim | Rest], [Prim | L]) :- insereaza(Elem, Rest, L).

  • SORTARE I CUTARE 89

    A doua variant de sortare prin inserie este urmtoarea: Lista poate fi privit la un moment dat ca L' = PS::PN, unde PS este partea sortat i PN = [X | T] este partea nesortat. Se ia primul element X din PN i se insereaz n PS. Algoritmul pornete cu PS = [] i PN = L, i se oprete cnd PN = [], n acel moment PS fiind chiar lista sortat. Se observa c PS are rol de acumulator, rezultatul final fiind ntors n al treilea parametru (construcia rezultatului se face pe apelul recursiv). % isort2(+Lista, -ListaSortata) - sortare prin insertie, varianta 2 isort2(L, LS) :- isort2( [], L, LS). isort2(PS, [], PS). isort2(PS, [X | T], LS) :- insereaza(X, PS, PS1), isort2(PS1, T, LS).

    5.3 Metoda de sortare rapid Sortarea rapid (quicksort) a unei liste de elemente L se definete recursiv astfel:

    1. Elimin un element Pivot din lista L i obine Rest = L - Pivot. 2. mparte lista Rest n dou liste: ListaInf, cu toate elementele din Rest

    inferioare elementului Pivot i ListaSup cu toate lementele din Rest superioare elementului Pivot.

    3. Sorteaz ListaInf i obine ListaInfSort. 4. Sorteaz ListaSup i obine ListaSupSort. 5. Concateneaz listele ListaInfSort, lista format din Pivot, ListaSupSort

    i obine ListaSortat.

    Predicatul quicksort(Lista, ListaSortat), definit mai jos, implementeaz acest algoritm. Elementul Pivot este considerat, n implementarea urmtoare, primul element al listei Lista, iar mprirea listei n ListaInf i ListaSup, n funcie de elementul pivot se face cu ajutorul predicatului scindeaz(Element, Lista, ListaInf, ListaSup). % quicksort(+Lista, -ListaSortata) - sortare prin metoda sortarii rapide quicksort([], []). quicksort([Pivot | Rest], ListaSortata) :- scindeaza(Pivot, Rest, L1, L2), quicksort(L1, L1Sortata), quicksort(L2, L2Sortata), conc(L1Sortata, [Pivot | L2Sortata], ListaSortata).

    % conc(+L1, +L2, -L) - concateneaza listele L1 si L2 in lista L conc([], L, L). conc([Prim | Rest], L, [Prim | Rest1]) :- conc(Rest, L, Rest1).

  • 90 CAPITOLUL 5

    % scindeaza(+Element, +Lista, -ListaInf, -ListaSup) - % imparte Lista n ListaInf (cu elemente inferioare lui Element) % si ListaSup (cu elemente superioare lui Element) % in functie de relatia rel(X, Y) scindeaza(Elem, [], [], []). scindeaza(Elem, [Prim | Rest], [Prim | L1], L2) :- not(rel(Elem, Prim)), !, scindeaza(Elem, Rest, L1, L2). scindeaza(Elem, [Prim | Rest], L1, [Prim | L2]) :- rel(Elem, Prim), scindeaza(Elem, Rest, L1, L2).

    S testm acum implementrile metodelor de sortare prezentate i s comparm rezultatele lor cu rezultatul produs de predicatul sort, care este predefinit n SWI-Prolog:

    % testarea metodelor de sortare test(F) :- L = [2, 2, 4, 6, 9, 8, 1, 3, 5, 7, 0], P =.. [F, L, S], call(P), write(P), nl. test :- test(isort1), test(isort2), test(quicksort), test(sort). Testarea va decurge astfel:

    ?- test. isort1([2,2,4,6,9,8,1,3,5,7,0], [0,1,2,2,3,4,5,6,7,8,9]) isort2([2,2,4,6,9,8,1,3,5,7,0], [0,1,2,2,3,4,5,6,7,8,9]) quicksort([2,2,4,6,9,8,1,3,5,7,0], [0,1,2,2,3,4,5,6,7,8,9]) sort([2,2,4,6,9,8,1,3,5,7,0], [0,1,2,2,3,4,5,6,7,8,9])

    5.4 Arbori binari Considerm urmtoarea reprezentare n Prolog a arborilor binari:

    1) arborele vid este codificat cu nil; 2) un arbore nevid este codificat cu arb(Cheie, SubarboreStang,

    SubarboreDrept). Traversarea unui arbore binar, cu afiarea cheilor din noduri, se

    implementeaz n Prolog foarte uor folosind facilitile recursive ale limbajului. Predicatul rsd(Arbore) realizeaz afiarea cheilor din Arbore n ordinea rdcin-stnga-dreapta.

    % rsd(+Arbore) - parcurge arborele binar Arbore % n ordinea radacina stanga dreapta, afisand cheile arborelui rsd(nil). rsd(arb(Radacina, SubarboreStang, SubarboreDrept)) :- write(Radacina), write(' '),

  • SORTARE I CUTARE 91

    rsd(SubarboreStang), rsd(SubarboreDrept).

    Dac se consider cazul arborilor binari de cutare (cu chei ntregi), se pot defini trei predicate: caut(Cheie, Arbore), de cutare a unei chei n arbore, care reuete dac cheia este n arbore i eueaz n caz contrar; inser(Cheie, Arbore, ArboreRez), de inserare a unei chei n arbore, cu argumentele Cheie i Arbore instaniate i argumentul ArboreRez sintetizat de program; i elim(Cheie, Arbore, ArboreRez), care terge o cheie dintr-un arbore. % caut(+Cheie, +Arbore) - reuseste daca Cheie este in % arborele binar de cautare Arbore, esueaza in caz contrar caut(Cheie, arb(Cheie, _ , _ )) :- !. caut(Cheie, arb(Radacina, ArbStg, _)) :- Cheie < Radacina, caut(Cheie, ArbStg). caut(Cheie, arb(Radacina, _ , ArbDr)) :- Cheie > Radacina, caut(Cheie, ArbDr).

    Prima clauz a predicatului caut reuete dac cheia este n arbore. Pentru a impiedica o posibil resatisfacere, deci gsirea unei alte apariii a cheii de cutare n arbore, s-a introdus n acest caz predicatul cut. Dac se dorete, de exemplu, afiarea tuturor apariiilor cheii de cutare n arbore, se va elimina acest cut i predicatul caut va avea attea soluii cte apariii ale cheii de cutare exist n arbore.

    % inser(+Cheie, +Arbore, -ArboreRez) - insereaza Cheie in % arborele binar de cautare Arbore si produce ArboreRez inser(Cheie, nil, arb(Cheie, nil, nil)). inser(Cheie, arb(Cheie, ArbStg, ArbDr), arb(Cheie, ArbStg, ArbDr)):-!. inser(Cheie, arb(Radacina, ArbStg, ArbDr), arb(Radacina, ArbStg1, ArbDr)) :- Cheie < Radacina, !, inser(Cheie, ArbStg, ArbStg1). inser(Cheie, arb(Radacina, ArbStg, ArbDr), arb(Radacina, ArbStg, ArbDr1)) :- Cheie > Radacina, inser(Cheie, ArbDr, ArbDr1).

    Predicatul de inserare a unei chei ntr-un arbore de cutare, inser, utilizeaz n definiie un cut verde pentru creterea eficienei. Se poate elimina condiia Cheie >>>> Radacina, din cea de a treia regul a predicatului inser, caz n care predicatul cut se transform ntr-un cut rou. Programul Prolog urmtor folosete definiiile predicatelor caut i inser pentru a implementa mai multe operaii de prelucrare a arborilor binari de cutare.

    Eliminarea unei chei dintr-un arbore binar de cutare se face dup algoritmul standard:

  • 92 CAPITOLUL 5

    % elim(+Cheie,+Arb,-ArbNou) elimina Cheie din Arb % cu rezultat in ArbNou elim(Cheie, nil, nil). elim(Cheie, arb(Cheie, nil, nil), nil). elim(Cheie, arb(Cheie, ArbStg, nil), ArbStg). elim(Cheie, arb(Cheie, nil, ArbDr), ArbDr). elim(Cheie, arb(Cheie, ArbStg, ArbDr), arb(Cheie1, Stg1, ArbDr)) :- drept(ArbStg, Cheie1, Stg1),!. elim(Cheie, arb(Cheie1, ArbStg, ArbDr), arb(Cheie1, ArbStg1, ArbDr1)) :- (Cheie < Cheie1, !, elim(Cheie, ArbStg, ArbStg1), ArbDr1=ArbDr) ; elim(Cheie, ArbDr, ArbDr1), ArbStg1=ArbStg.

    % drept(+Arb,-Cheie,-SuccDr) - intoarce cel mai din dreapta nod % din arborele Arb. drept(arb(Cheie, ArbStg, nil), Cheie, ArbStg). drept(arb(Cheie, ArbStg, ArbDr), Cheie1, arb(Cheie, ArbStg, ArbDr1)) :- drept(ArbDr, Cheie1, ArbDr1).

    Se poate defini un meniu de prelucrare arborilor binari de cutare care s permit execuia operaiilor definite anterior, la cerere.

    meniu(Arb):- nl, write('1. Sfarsit'), nl, write('2. Creeaza arbore'), nl, write('3. Insereaza o cheie'), nl, write('4. Cauta o cheie'), nl, write('5. Sterge o cheie'), nl, write('6. Afiseaza arbore'), nl, read(Opt), Opt \= 1, !, actiune(Opt, Arb, ArbNou), meniu(ArbNou). meniu( _ ) :- write('Sfarsit'), nl.

    actiune(2, Arb, ArbNou) :- creare(Arb, ArbNou). actiune(3, Arb, ArbNou) :- write('Introduceti cheia: '), read(Cheie), inser(Cheie, Arb, ArbNou). actiune(4, Arb, Arb) :- write('Introduceti cheia: '), read(Cheie), (caut(Cheie, Arb), write('Cheia a fost gasita'); write('Cheia nu este in arbore')), nl. actiune(5, Arb, ArbNou) :- write('Introduceti cheia: '), read(Cheie), elim(Cheie, Arb, ArbNou).

  • SORTARE I CUTARE 93

    actiune(6, Arb, Arb) :- write(' In ce ordine? '), read(Ordine), afisare(Arb, Ordine).

    creare(Arb, ArbNou) :- write('Introduceti o cheie, 0 pentru terminare: '), read(Cheie), Cheie \= 0, !, inser(Cheie, Arb, A), creare(A, ArbNou). creare(Arb, Arb).

    % afisare(Arbore, Ordine) - afiseaza cheile arborelui binar Arbore, % parcurgand arborele in ordinea specificata de Ordine: rsd, srd, sdr afisare(nil, _ ). afisare(arb(Radacina, SubarboreStang, SubarboreDrept), Ordine) :- (Ordine = rsd, write(Radacina), write(' '); true), afisare( SubarboreStang, Ordine), (Ordine = srd, write(Radacina), write(' '); true), afisare( SubarboreDrept, Ordine), (Ordine = sdr, write(Radacina), write(' '); true).

    Afiarea arborilor cu ajutorul predicatului afisare(Arbore, Ordine) se poate face n trei moduri diferite, n funcie de valoarea argumentului Ordine: rsd (rdcin-stnga-dreapta), srd (stnga-rdcin-dreapta) i sdr (stnga-dreapta-rdcin). Se observ utilizarea operatorului de disjuncie a scopurilor, cu ajutorul cruia se exprim cele trei modaliti de parcurgere a arborelui. Afiarea repetat a meniului de aciuni este fcut de predicatul meniu(Arb) prin apelul recursiv al acestuia, ct timp nu s-a selectat aciunea Sfarsit. Predicatul are argumentul Arb instaniat. Acesta poate fi iniial arborele vid i este actualizat de apelul recursiv la noua valoare ArbNou, care depinde de aciunea selectat. n funcie de aciunea selectat n NrActiune i de arborele dat Arb, predicatul actiune(NrActiune, Arb, ArbNou) decide care prelucrri sunt necesare pentru a obine arborele rezultat ArbNou.

    S vedem acum un predicat de sortare a listelor, care utilizeaz arbori binari de cutare. Predicatul binsort(Lista, ListaSortata) sorteaz lista Lista n lista ListaSortata. El construiete un arbore binar de cutare prin inserarea succesiv a elementelor din Lista cu ajutorul predicatului inser prezentat anterior. ListaSortata se obine prin parcurgerea n ordine a arborelui obinut.

    % binsort(+Lista, -ListaSortata) binsort(L, LSort) :- constr_arb(L, Tree, nil), write('Arborele este: '), nl, write(Tree), nl, parc_ord(Tree, LSort, []).

    % constr_arb - construieste un arbore binar de cautare

  • 94 CAPITOLUL 5

    constr_arb([], Acc, Acc) :- !. constr_arb([K | Rest], Sol, Acc) :- inser(K, Acc, NoulAcc), constr_arb(Rest, Sol, NoulAcc).

    % parc_ord - parcurge in ordine un arbore binar, % construind lista cheilor sale parc_ord(nil, Acc, Acc) :- !. parc_ord(arb(Cheie, Stang, Drept), L, Acc) :- parc_ord(Drept, L1, Acc), parc_ord(Stang, L, [Cheie | L1]).

    5.5 Exerciii propuse EP1. Care este complexitatea timp a algoritmului de sortare prin metoda generare i testare, prezentat n Seciunea 5.1 ?

    EP2. Comentai asupra complexitii timp a unei implementari Prolog a algoritmului de cutare binar a unei chei ntr-o list sortat cresctor.

    EP3. Scriei predicatul ssort(-L,+LS) care sorteaz lista L n lista LS conform metodei de sortare prin selecie. La un moment dat lista este L' = PS::PN, unde PS este partea sortat i PN partea nesortat. Se extrage n mod repetat din PN elementul X de valoare minim i se adaug la sfritul listei PS. Algoritmul ncepe cu PS = [] i PN = L, i se termin cnd PN = [], n acel moment PS fiind chiar lista sortat LS. Pentru eliminarea unui element dintr-o list i concatenarea a dou liste trebuie s mai scriei dou predicate: elim i append. Pentru a evita folosirea lui append se poate extrage maximul din PN la fiecare pas i insera naintea lui SP.

    EP4. S se scrie un program Prolog care realizeaz sortarea prin interclasare.

    EP5. O concordan este o list de cuvinte care apar ntr-un text, lista sortat n ordine alfabetic mpreun cu numrul de apariii ale fiecrui cuvnt n text. S se scrie un program care genereaz o concordan pornind de la un text dat. S se propun o reprezentare pentru text i o reprezentare pentru concordan.

    EP6. S se scrie un predicat care elimin o cheie dintr-un arbore binar de cutare.

    EP7. S se rescrie predicatul meniu(Arb), nlocuind definiia recursiv a acestuia cu o definiie care utilizeaz predicatul repeat.

    EP8. S se scrie un predicat Prolog care verific egalitatea a doi arbori binari, adic dac au aceleai chei.

    EP9. S se scrie un predicat Prolog care verific egalitatea structural a doi arbori binari, adic dac au aceleai numr de chei i arat la fel.

  • SORTARE I CUTARE 95

    EP10. S se defineasc o structur Prolog care s reprezinte un arbore multici. S se scrie un predicat Prolog pentru afiarea unui astfel de arbore.

    EP11. S se scrie un predicat Prolog care verific egalitatea a doi arbori multici.