Sunteți pe pagina 1din 5

Metode de sortare. Arbori binari.

Metoda de sortare prin generare si testare

Utilizând structura de control a limbajului Prolog, se poate realiza foarte simplu sortarea unei
secvente de elemente utilizând metoda generare şi testare. Aceasta metodă de rezolvare a
problemelor, utilizata în inteligenta artificiala dar puţin potrivită pentru o sortare, are la baza
urmatoarea idee: o componenta generatoare construieste solutii candidate şi o a doua componenta,
componenta de testare, verifica fiecare solutie candidata pentru a vedea dacă este sau nu o solutie a
problemei. În acest fel se pot obtine fie toate solutiile problemei, fie una singura. Aceasta metoda
poate fi exprimata succint în Prolog astfel:

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

Metoda este în general ineficienta chiar în cazul problemelor tipice de inteligenta artificiala
care necesita un proces de cautare a solutiei. Metoda este extrem de ineficienta în cazul rezolvarii
problemelor de sortare, pentru care exista algoritmi eficienti de rezolvare. Cu toate acestea, se
prezinta în continuare solutia de sortare în ordine crescatoare a unei liste de întregi prin metoda
generare şi testare ca exerciţiu Prolog.

% sortare(+Lista, -ListaSortata) - sortare prin metoda generare şi 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).

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


rel(X, Y) :- X =< Y.

% ordonata(+Lista) - verifica dacă Lista este sortata dupa relatia rel(X, Y)


ordonata([ _ ]).
ordonata([Prim, Secund | Rest]) :- rel(Prim, Secund), ordonata([Secund | Rest]).

Se observa ca sortarea se face prin generarea permutarilor elementelor din lista şi verificarea
dacă o permutare generata este o secventa sortata. Componenta generatoare este predicatul
permut(Lista, ListaSortata) şi componenta de testare este predicatul ordonata(Lista). Desi
implementarea este simpla, exploatând facilitatile nedeterministe ale limbajului Prolog, ea este
foarte ineficienta.
Metoda de sortare prin insertie

Sortarea prin insertie a unei liste de elemente L = [H | T] se poate exprima recursiv astfel: se
sorteaza mai întâi lista T în TS si apoi se insereaza elementul H în lista TS acolo unde îi este locul
conform relatiei 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 rosu astfel:

insereaza(Elem, [], [Elem]).


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

A doua varianta de sortare prin insertie este urmatoarea: Lista poate fi privita la un moment
dat ca L' = PS::PN, unde PS este partea sortata si PN = [X | T] este partea nesortata. Se ia primul
element X din PN si se insereaza în PS. Algoritmul porneste cu PS = [] si PN = L, si se opreste
când PN = [], în acel moment PS fiind chiar lista sortata. Se observa ca PS are rol de acumulator,
rezultatul final fiind întors în al treilea parametru (constructia 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).

Arbori binari

Fie urmatoarea 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 afisarea cheilor din noduri, se implementeaza în Prolog
foarte usor folosind facilitatile recursive ale limbajului. Predicatul rsd(Arbore) realizeaza afisarea
cheilor din Arbore în ordinea radacina-stânga-dreapta.
% rsd(+Arbore) - parcurge arborele binar Arbore
% în ordinea radacina-stânga-dreapta, afisând cheile arborelui
rsd(nil).
rsd(arb(Radacina, SubarboreStang, SubarboreDrept)) :-
write(Radacina), write(' '),
rsd(SubarboreStang),
rsd(SubarboreDrept).

Dacă se considera cazul arborilor binari de cautare (cu chei întregi), se pot defini trei
predicate: caut(Cheie, Arbore), de cautare a unei chei în arbore, care reuşeşte dacă cheia este în
arbore şi eşuează în caz contrar; inser(Cheie, Arbore, ArboreRez), de inserare a unei chei în
arbore, cu argumentele Cheie şi Arbore instantiate şi argumentul ArboreRez sintetizat de
program; si elim(Cheie, Arbore, ArboreRez), care sterge o cheie dintr-un arbore.

% caut(+Cheie, +Arbore) - reuşeşte dacă Cheie este în


% arborele binar de cautare Arbore, eşuează în 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 clauza a predicatului caut reuşeşte dacă cheia este în arbore. Pentru a impiedica o
posibila resatisfacere, deci gasirea unei alte aparitii a cheii de cautare în arbore, s-a introdus în acest
caz predicatul cut. Dacă se doreste, de exemplu, afisarea tuturor aparitiilor cheii de cautare în
arbore, se va elimina acest cut şi predicatul caut va avea atâtea solutii câte aparitii ale cheii de
cautare exista în arbore.

% inser(+Cheie, +Arbore, -ArboreRez) - insereaza Cheie în


% arborele binar de cautare Arbore şi 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 cautare, inser, utilizeaza în definitie un cut
verde pentru cresterea eficientei. Se poate elimina conditia Cheie  Radacina, din cea de a treia
regula a predicatului inser, caz în care predicatul cut se transforma într-un cut rosu. Programul
Prolog care urmeaza foloseste definitiile predicatelor caut şi inser pentru a implementa mai multe
operatii de prelucrare a arborilor binari de cautare.

Eliminarea unei chei dintr-un arbore binar de cautare se face dupa algoritmul standard:
% 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 succesor din
% subarborele stâng al nodului cu cheia Cheie in 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 căutare care să permită execuţia
operaţiilor 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).
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, parcurgând


% arborele în 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).

Afisarea arborilor cu ajutorul predicatului afisare(Arbore, Ordine) se poate face în trei


moduri diferite, în functie de valoarea argumentului Ordine: rsd (radacina-stânga-dreapta), srd
(stânga-radacina-dreapta) şi sdr (stânga-dreapta-radacina). Se observa utilizarea operatorului de
disjunctie a scopurilor cu ajutorul caruia se exprima cele trei modalitati de parcurgere a arborelui.
Afisarea repetata a meniului de actiuni este facuta de predicatul meniu(Arb) prin apelul recursiv al
acestuia cât timp nu s-a selectat actiunea Sfarsit. Predicatul are argumentul Arb instantiat. Acesta
poate fi initial arborele vid şi este actualizat de apelul recursiv la noua valoare ArbNou, care
depinde de actiunea selectata. În functie de actiunea selectata în NrActiune şi de arborele dat Arb,
predicatul actiune(NrActiune, Arb, ArbNou) decide care prelucrari sunt necesare pentru a obtine
arborele rezultat ArbNou.

Probleme propuse
1. Scrieti un program Prolog care sa sorteze o lista prin metoda de sortare rapida (quicksort).

2. Scrieti un predicat de sortare a listelor care utilizeaza arbori binari de cautare. Predicatul
binsort(Lista, ListaSortata) sorteaza lista Lista în lista ListaSortata. El construieste un
arbore binar de cautare prin inserarea succesiva a elementelor din Lista cu ajutorul
predicatului inser prezentat anterior. ListaSortata se obtine prin parcurgerea în inordine a
arborelui obtinut.

3. Să se scrie un predicat care elimină o cheie dintr-un arbore binar de căutare.

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

S-ar putea să vă placă și