Sunteți pe pagina 1din 0

Vitalie Cotelea

29
7
Structuri de date
7.1. Tehnici de sortare a
secvenelor
Sortarea este una din cele mai importante activiti de
prelucrare a datelor. Exist muli algoritmi de sortare a
secvenelor. Aici, ns, vom considera patru algoritmi diferii
implementai n Prolog: sortarea naiv, sortarea cu inserie,
sortarea prin metoda bulelor i sortarea rapid.
Fiecare program va utiliza predicatul precede(X,Y) ce
este adevrat pentru numere, dac XY. Deci, schimbnd numai
acest predicat, vom putea sorta diferite tipuri de elemente.
7.1.1. Sortarea naiv
Sortarea naiv genereaz permutrile
elementelor din list i apoi verific dac ea este
sortat, fie ascendent.
S aducem programul de generare a unei permutri a
elementelor unei liste. Pentru aceasta se utilizeaz predicatul
eliminare_elem/3. S observm c o permutare a unei liste se
obine prin permutarea cozii listei i apoi prin inseria capului
listei ntr-o poziie posibil.
permutare([],[]).
permutare([Cap|Coada],L):-
permutare(Coada,L1),
eliminare_elem(Cap,L,L1).
Combinnd predicatele de mai sus, se obine programul
de sortare naiv.
sortare_naiva(L,ListaSortata):-
permutare(L,ListaSortata),
ascendenta(ListaSortata).
Programul descris e uor de neles. ns, el este
iremediabil de ineficient. Motivul este c sunt n! permutri
posibile a n elemente. De exemplu, 5!=120, adic, Prologul
poate verifica pn la 120 posibiliti. Dar 10!=3628800 i
25!=15511210043330985984000000 - este clar c Prologul nu
le va putea examina. S observm c permutarea se face nainte
de a verifica dac lista e sortat ascendent. Dac mcar o
sublist nu e sortat, ar fi bine ca permutarea s fie respins.
7.1.2. Sortarea prin inserie
Sortarea prin inserie este, probabil, cea mai natural
metod care deseori se utilizeaz i manual. Capul unei liste este
eliminat, coada se sorteaz, apoi capul se insereaz n poziia
corespunztoare a sublistei sortate.
sortare_insertie([],[]).
sortare_insertie([C|Cd],LSort):-
sortare_insertie(Cd,LSort1),
inserare(C,LSort1,LSort).
Inserarea este prezentat de urmtoarele clauze:
inserare(Elem,[],[Elem]).
inserare(Elem,[C|Cd],[C|Cd1]):-
precede(C,Elem),
inserare(Elem,Cd,Cd1).
inserare(Elem,[C|Cd],[Elem,C|Cd]):-
precede(Elem,C).
ns, acest program de sortare a secvenei are o particularitate
puin stranie:
?-sortare_insertie([4,1,3,1,7,2],L).
L=[1,1,2,3,4,7];
L=[1,1,2,3,4,7]
Prologul la ntrebarea de mai sus produce dou soluii.
Cauza const n prezena n list a celor dou elemente 1 care se
nsereaz n ambele posibile locuri. Aceasta ar putea fi evitat,
dac s-ar utiliza o alt definiie a predicatului precede/2.
7.1.3. Metoda bulei
Sortarea prin metoda bulei este o alt tehnic de sortare
care presupune examinarea perechilor de elemente adiacente,
schimbndu-le cu locul, dac nu corespund ordinii impuse de
predicatul precede/2. Dup ce toate perechile sunt examinate,
lista rezultat devine sortat. Urmtorul program, ce realizeaz
aceast idee, este un exemplu de programare proast n Prolog.
sortare_bubble(Lista,ListaSortata):-
concatinare(X,[A,B|Y],Lista),
precede(B,A),
concatenare(X,[B,A|Y],M),
sortare_bubble(M,ListaSortata).
sortare_bubble(Lista,Lista):-
ascendenta(Lista).
Acest program este extrem de ineficient
?-sortare_bubble([5,4,3,2,1],Lista).
Lista=[1,2,3,4,5]
i are dou dezavantaje. Primul dezavantaj este c programul nu
e scris n stil declarativ. El ru utilizeaz cunotinele despre
ordinea n care Prologul ncearc s efectueze sortarea. Al doilea
dezavantaj este c scopul precede(B,A) ntotdeauna va reui,
dac A=B i deci, programul va avea o bucl infinit, dac dou
elemente n list sunt egale. Motivul e n faptul c sortarea prin
metoda bulelor descris mai sus este un concept procedural. De
aceea el este ideal pentru implementarea ntr-un limbaj
procedural, cum ar fi C. Deci, prin acest exemplu se vede c este
posibil scrierea n Prolog a unui algoritm procedural, rezultatul,
ns, nu este, bineneles, elegant.
7.1.4. Sortarea rapid
Algoritmul de sortare rapid e mult mai potrivit pentru
implementare n Prolog. Algoritmul se bazeaz pe urmtoarea
idee inteligent. Considerm lista [Cap|Coada]. Lista Coada se
poate diviza n dou liste L i M, n aa fel ca L s conin
elementele mai mici dect Cap, iar M - elementele mai mari
dect Cap. Atunci lista sortat va fi compus din versiunea
Vitalie Cotelea
30
sortat a listei L, apoi Cap i apoi versiunea sortat a listei M. La
rndul su, listele L i M sunt sortate recursiv prin sortarea
rapid.
Mai nti, considerm algoritmul de divizare a listei.
Vom utiliza predicatul divizare(Cap,Coada,L,M) pentru
mprirea listei [Cap|Coada] n listele L i M.
divizare(Cap,[A|X],[A|Y],Z):-
precede(A,Cap),
divizare(Cap,X,Y,Z).
divizare(Cap,[A|X],Y,[A|Z]):-
precede(Cap,A),
divizare(Cap,X,Y,Z).
divizare(Cap,[],[],[]).
Atunci algoritmul de sortare rapid e reprezentat de programul
sortare_rapida([C|Cd],LSort):-
divizare(C,Cd,L,M),
sortare_rapida(L,L1),
sortare_rapida(M,M1),
concatenare(L1,[C|M1],LSort).
sortare_rapida([],[]).
Acest algoritm este foarte eficient i lucreaz bine pe
secvene foarte mari.
7.2. Tratarea mulimilor
Mulimile n programare sunt una din structurile de baz.
Multe probleme sunt legate de cutarea soluiilor folosind
mulimile. Unele limbaje de programare, de exemplu, Pascal i
Modula-2, ncorporeaz mulimile i operaiile standarde asupra
lor ca parte a limbajului. Alte limbaje, inclusiv Prolog, nu au
mulimi n calitate de structuri predefinite. Responsabilitatea este
a programatorilor s defineasc mulimi i s elaboreze predicate
ce imit operaiile pe mulimi, utiliznd facilitile disponibile din
limbaj.
7.2.1. Reprezentarea explicit prin liste
Aceast tehnic utilizeaz listele pentru reprezentarea
mulimilor i este cea mai simpl i deci, mai frecvent aplicat.
Mulimile se reprezint i se manipuleaz explicit, scriind
elementele ntr-o list. De exemplu, mulimea {2, 3, 5} e
reprezentat n Prolog cu [2, 3, 5]. Mai exist i alte variante ale
acestei reprezentri, dar mai nti s argumentm necesitatea ei.
Motivul principal const n faptul c n Prolog nu exist nici o
facilitate predefinit pentru reprezentarea mulimilor. De aceea
mulimile se substituie deseori cu liste. Mulimile i listele, ns,
sunt structuri de date diferite. Pentru a implementa o mulime
prin intermediul listei, ultimei se impune unele caracteristici
specifice mulimilor.
Precum s-a mai spus, sunt dou deosebiri majore ntre o
list i o mulime. Una este c ordinea elementelor ntr-o list e
esenial, pe cnd ntr-o mulime - nu. De exemplu, [2, 3, 5] i
[3, 5, 2] sunt liste diferite, dar ele reprezint aceeai mulime. A
doua deosebire este c mulimea este o colecie de obiecte
distincte. Astfel, cnd se concateneaz listele [2, 3, 5] i [5, 6],
obinem [2, 3, 5, 5, 6]. Din alt parte, uniunea mulimilor [2, 3,
5] i [5, 6] este [2, 3, 5, 6].
Reprezentarea prin liste nesortate, de exemplu, [3, 5, 2],
este, probabil, cea mai obinuit form de reprezentare a
mulimilor n Prolog. n seciunile precedente se vorbea despre
predicatul apartine/2. n baza lui poate fi definit predicatul
submultime/2, care este adevrat, dac primul argument este o
submulime a argumentului doi.
submultime([],_).
submultime([X|Xs],Ys):-
apartine(X,Ys),
submultime(Xs,Ys).
Predicatul echivalente(Xs,Ys) este adevrat, dac
mulimile Xs i Ys sunt echivalente.
echivalente(Xs,Ys):-
submultime(Xs,Ys),
submultime(Ys,Xs).
Uniunea a dou mulimi poate fi exprimat prin
urmtoarele clauze:
uniune([],Ys,Ys).
uniune([X|Xs],Ys,[X|Zs]):-
not(apartine(X,Ys)),!,
uniune(Xs,Ys,Zs).
uniune([_|Xs],Ys,Zs):-
uniune(Xs,Ys,Zs).
iar intersecia a dou mulimi prin
intersectie([],_,[]).
intersectie([X|Xs],Ys,[X|Zs]):-
apartine(X,Ys),!,
intersectie(Xs,Ys,Zs).
intersectie([_|Xs],Ys,Zs):-
intersectie(Xs,Ys,Zs).
Tehnica reprezentrii prin liste sortate, fiind de fapt o
variant a tehnicii de mai sus, impune o restricie intern -
elementele listei sunt sortate. S notm, c aici nu se schimb
regula de baz a mulimilor, care ne spune c ordinea
elementelor nu este esenial. Noi impunem aceast restricie
doar n reprezentarea intern.
Acum vom aduce definiiile operaiilor pe mulimi,
reprezentate de liste sortate: uniunea, intersecia i diferena.
Aici se folosete predicatul precede/2, care este adevrat, dac
primul argument precede n list elementul al doilea.
uniune([],Ys,Ys) .
uniune(Xs,[],Xs).
uniune([X|Xs],[X|Ys],[X|Zs]):-!,
uniune(Xs,Ys,Zs).
uniune([X|Xs],[Y|Ys],[X|Zs]):-
precede(X,Y),
uniune(Xs,[Y|Ys],Zs).
uniune([X|Xs],[Y|Ys],[Y|Zs]):-
precede(Y,X),
uniune([X|Xs],Ys,Zs).
intersectie([],_,[]).
intersectie(_,[],[]).
intersectie([X|Xs],[X|Ys],[X|Zs]):-!,
intersectie(Xs,Ys,Zs).
intersectie([X|Xs],[Y|Ys],Zs):-
precede(X,Y),
intersectie(Xs,[Y|Ys],Zs).
intersectie([X|Xs],[Y|Ys],Zs):-
precede(Y,X),
intersectie([X|Xs],Ys,Zs).
diferenta([],_,[]).
diferenta(Xs,[],Xs).
diferenta([X|Xs],[X|Ys],Zs):-!,
diferenta(Xs,Ys,Zs).
diferenta([X|Xs],[Y|Ys],[X|Zs]):-
precede(X,Y),
diferenta(Xs,[Y|Ys],Zs).
diferenta([X|Xs],[Y|Ys],Zs):-
precede(Y,X),
diferenta([X|Xs],Ys,Zs).
Vitalie Cotelea
31
Dac comparm tehnicile descrise, principalele avantaje
i dezavantaje in de complexitatea algoritmilor. Tehnica de
reprezentare prin liste sortate necesit mai mult timp dect
tehnica de reprezentare prin liste nesortate pentru construirea
mulimilor i mai puin timp pentru operaiile ce se aplic asupra
submulimilor. Trebuie s se in cont de urmtoarea regul.
Dac mulimile sunt mai mult sau mai puin statice, adic nu se
prea modific, atunci se aplic tehnica listelor sortate, n caz
contrar tehnica listelor nesortate.
Fie mulimea Xs are m elemente i mulimea Ys are n
elemente. La nceput, ntruct mulimea Xs se sorteaz, se va
cere O(mlog(m)) comparaii, iar cealalt metod nu va necesita
cheltuieli de timp. Inserarea unui element n Xs, folosind tehnica
listelor sortate, necesit O(m) sau O(log(m)) comparaii n
dependen de algoritmul utilizat. n cazul listelor nesortate e
necesar doar O(1) timp, fiindc un element poate fi inserat la
nceputul listei. ns, pentru a face uniunea a dou liste, tehnica
listei sortate necesit O(m+n) timp, pe cnd tehnica listei
nesortate - O(m*n) comparaii.
Mulimilor li se pot asocia un nume. n tehnicile
precedente numele unei mulimi nu era nicicum legat de ea,
cum, spre deosebire, se face n matematic:
a={3, 5, 2}
b={5, 6}
Dar i n unele aplicaii putem dori ca mulimile s fie legate de
numele ce le posed. O cale explicit este cea de utilizare a
faptelor cu liste:
a([3,5,2]).
b([5,6]).
Adic, fiece fapt reprezint o mulime, iar functorul faptului este
numele mulimii.
Aceast tehnic poate fi considerat o variant a tehnicii
listelor nesortate. Aici orice mulime e reprezentat nume(Xs),
dar n tehnica listelor nesortate se utilizeaz numai Xs. Deci,
izolndu-l pe Xs, putem aplica predicatele cunoscute. De
exemplu,
apartine1(X,nume(Xs)):-
apartine(X,Xs).
va determina dac X este un element al mulimii Xs. Similar
submultime1(nume1(Xs),nume2(Ys)):-
submultime(Xs,Ys).
va determina dac mulimea nume1(Xs) este o submulime a
mulimii nume2(Ys).
Dac functorul faptelor este mulime, atunci mulimile a
i b sunt reprezentate astfel
multime(a,[3,5,2]).
multime(b,[5,6]).
Functorul mulime este definit de utilizator, deci aici
poate fi utilizat orice nume. Aceast form de reprezentare
indic explicit care fapte din baza de fapte reprezint mulimi.
Ea, la fel, este o variant a tehnicii de reprezentare prin liste
nesortate. De exemplu,
apartine2(X,Nume):-
multime(Nume,Xs),
apartine(X,Xs).
va determina dac X este un element al mulimii cu numele
Nume. Bineneles, putem considera aici nc o variant, cnd
lista din fapt, ce reprezint mulimea, este sortat.
7.2.2. Reprezentarea explicit a
elementelor mulimii prin fapte
Orice element al mulimii e reprezentat de un fapt Prolog
cu dou argumente. Primul argument este numele mulimii, iar al
doilea este nsui elementul. Deci, mulimile a={3,5,2} i
b={5,6} pot fi reprezentate astfel
elem(a,3).
elem(a,5).
elem(a,2).
elem(b,5).
elem(b,6).
Reprezentrile prin liste sunt mai compacte dect ultima.
De asemenea, reprezentarea prin liste este mai potrivit pentru
reprezentarea diferitor variante de mulimi cum ar fi mulimea
vid, [], sau a unei mulimi de mulimi [[2],[2,3]]. Exist, ns,
situaii cnd reprezentarea elementelor prin fapte este util.
Momentul cheie este c aceast reprezentare este o simpl
colecie de fapte - component de baz a unui program Prolog.
Aceast reprezentare poate fi adus la formele de reprezentare
precedente, aplicnd doi pai. Primul pas transform datele ntr-
o list. La pasul 2 se aplic operaiile pe mulimi asupra noii
structuri, adic tehnicile cunoscute.
Exist unele avantaje n utilizarea faptelor. Odat ce
transformarea e fcut, fiind cunoscute tehnicile reprezentrii
prin liste, scrierea programului de mai departe devine simpl. Un
alt, posibil, avantaj este c operaiile asupra submulimilor
(sublistelor) sunt de obicei mai eficiente dect lucrul direct cu
faptele.
Acum s ncercm s construim o list din colecia de
fapte. De exemplu, din
elem(a,3).
elem(a,5).
elem(a,2).
dorim s obinem un fapt nou
multime(a,[3,5,2]).
Cu toate c ordinea elementelor n mulime e imaterial,
e bine uneori, pentru comparaie, s pstrm aceeai ordine ca n
faptele iniiale.
n unele implementri Prolog exist un predicat
predefinit findall/3. Dac se formuleaz ntrebarea
?-findall(X,elem(a,X),Lista)
sistemul va rspunde
Lista=[3,5,2]
Dup unele operaii cu mulimi, uneori, se dorete
descompunerea mulimilor napoi n fapte. De exemplu, fiind
dat
multime(c,[4,5]).
se cere generarea
elem(c,4).
elem(c,5).
Pentru aceasta putem utiliza predicatele
construire_elem(M):-
Vitalie Cotelea
32
multime(M,Es),
generare(M,Es).
generare(_,[]):-!.
generare(M,[E|Es]):-
assert(elem(M,E)),
generare(M,Es).
Iar ntrebarea
?-construire_elem(c)
va produce rezultatul ateptat.
S examinm acum tehnica de operare direct cu fapte.
Aici, pentru a verifica dac un element E aparine mulimii cu
numele M, e destul s se scrie
?-elem(M,E)
S considerm, acum, relaia de submulime. Fie faptele
elem(a,2).
elem(a,5).
elem(b,2).
elem(b,3).
elem(b,5).
Vom exprima noiunea de submulime prin noiunea de a
nu fi submulime. Atunci, mulimea reprezentat de a este o
submulime a mulimii b, dac predicatul ce exprim relaia de a
nu fi submulime dintre a i b eueaz. Deci, trebuie de scris un
predicat submultime(A.B) care reuete, dac A e submulime a
mulimii B i nu reuete, n caz contrar. E evident c cazul
special submultime(A,A) ntotdeauna reuete. Pentru exemplul
de mai sus, submultime(a,b) trebuie s reueasc, n timp ce
submultime(b,a) - s eueze.
submultime(A,B):-
not(nu_e_submultime(A,B)).
nu_e_submultime(A,B):-
elem(A,E),
not(elem(B,E)).
Dou mulimi sunt echivalente, dac ele sunt submulimi
una alteia. Acest predicat seamn cu cel din reprezentarea prin
liste, numai c n calitate de argumente aici apar nume de
mulimi i nu nsi mulimile.
echivalente(A,B):-
submultime(A,B),
submultime(B,A).
Predicatul e_in_uniune(E,A,B) este adevrat, dac
elementul E este n uniunea mulimilor A i B.
e_in_uniune(E,A,B):-elem(A,E),!.
e_in_uniune(E,A,B):-elem(B,E).
Predicatul e_in_intersectie(E,A,B) este adevrat, dac
elementul E este n intersecia mulimilor A i B.
e_in_intersectie(E,A,B):-
elem(A,E),
elem(B,E).
Predicatul e_in_diferenta(E,A,B) este adevrat, dac
elementul E este n diferena mulimilor A i B.
e_in_diferenta(E,A,B):-
elem(A,E),
not(elem(B,E)).
Menionm c mai sus, realmente, nu s-au produs
operaiile uniunea, intersecia i diferena. Pentru a genera o
nou mulime C n calitate de uniune a mulimilor A i B,
ultimele trebuie s se transforme n liste, apoi s se utilizeze
tehnicile respective i, n sfrit, lista s se desfac iari n
fapte. Aceeai procedur poate fi aplicat, de asemenea, pentru
intersecie i diferen.
Presupunem c mulimea A are m elemente, iar mulimea
B are n elemente. Predicatele submultime/2 i echivalente/2, de
exemplu, scannd faptele ce includ elementele mulimilor A i B,
fac O(m*n) comparaii. Predicatele e_in_uniune/3,
e_in_intersectie/3 i e_in_diferenta/3 necesit O(m+n)
comparaii.
7.2.3. Descrierea implicit a
elementelor mulimilor
Aici elementele mulimilor se descriu de caracteristicile
lor. Astfel, mulimile n Prolog pot fi definite prin predicate. O
serie de operaii pe mulimi pot fi aplicate asupra argumentelor
predicatelor, chiar dac nu se recunoate c predicatele
reprezint mulimi.
Metodele implicite au unele avantaje i dezavantaje. Un
dezavantaj poate fi considerat faptul c nu ntotdeauna pot fi
uor efectuate operaiile pe mulimi exprimate implicit. Dar
ndat ce aceast dificultate este depit, apar unele avantaje. n
primul rnd, aceast reprezentare este foarte compact. Dac
sunt necesare elementele n mod explicit, atunci acest lucru
rmne pe seama calculatorului. n al doilea rnd, e clar c
metoda explicit nu poate descrie o mulime infinit. Pentru
metoda implicit nu exist astfel de restricii. n al treilea rnd,
poate fi uor vzut natura elementelor privind doar
caracteristicile lor.
S considerm, de exemplu, reprezentarea implicit a
numerelor Fibonacci. Pentru numrul N, unde N0, numrul
Fibonacci, notat F
N
, se definete
F
N
=0, pentru N=O
F
N
=1, pentru N=1
F
N
=F
N
-1+F
N
-2, pentru N2
n Prolog, putem defini predicatul fib(N,FN) care
primete valoarea adevr, dac F
N
este al N
ea
numr Fibonacci.
fib(0,0).
fib(1,1).
fib(N,FN):-
N>=2,
N1 is N-1, N2 is N-2,
fib(N1,FN1),fib(N2,FN2),
FN=FN1+FN2.
Acest predicat descrie caracteristica numerelor
Fibonacci. El poate fi considerat o reprezentare implicit a lor.
S ne oprim asupra a trei tehnici de manipulare a
mulimilor reprezentate implicit.
Una din tehnici presupune reducerea la liste a tuturor
mulimilor participante la operaii. Aceast tehnic include doi
pai. La primul pas, utiliznd caracteristica, se genereaz
elementele mulimilor n mod explicit. La pasul 2 se aplic
operaiile pe mulimi conform tehnicilor de reprezentare prin
liste. Este evident c aceast metod nu poate fi aplicat, dac
din diferite motive nu pot fi generate toate elementele.
Vitalie Cotelea
33
Astfel, utiliznd predicatul fib/2, se poate genera
mulimea de numere Fibonacci reprezentate explicit. Predicatul
fibmultime(N,M) reuete, dac M este mulimea primelor N
numere Fibonacci.
fibmultime(0,[0]).
fibmultime(N,M):-
N>=1,
fib(N,FN),
N1 is N-1,
fibmultime(N1,M1),
concatinare(M1,[FN],M).
Menionm c nu ntotdeauna e necesar generarea
tuturor elementelor n form explicit. De exemplu, pentru a
determina dac un obiect este un element al unei definiii
implicite, e de ajuns s se introduc obiectul n definiie. Dac
definiia reuete, atunci obiectul e element al mulimii
reprezentate implicit. n caz contrar - nu este.
Pot fi aplicate operaii cnd o mulime este reprezentat
explicit, iar alta implicit. Fie A o mulime definit explicit, iar B
o mulime definit implicit. Pentru a determina dac A este o
submulime a lui B, se verific dac orice element din A aparine
i lui B. Pentru a calcula mulimea C, intersecie a mulimilor A
i B, se lucreaz cu orice element din A i se verific dac el este
i n B. Dac da, atunci elementul se include ca element n C.
Pentru a face, ns, uniunea mulimilor A i B, trebuie generate
toate elementele i din B.
Bineneles c pot fi aplicate operaii asupra mulimilor
reprezentate implicit fr a genera elementele n form explicit.
Aceasta e o tehnic atractiv. n general, ns, aplicarea
operaiilor este mai dificil dect n cazul reprezentrii explicite.
Astfel, pot fi simulate operaiile uniunea i intersecia,
utiliznd conjuncii i disjuncii de subscopuri. Fie predicatele a
i b definesc implicit mulimile A i B, respectiv. Pentru a genera
reprezentarea implicit a uniunii mulimilor A i B, se
construiete un predicat c:
c:-a.
c:-b.
Similar pentru reprezentarea interseciei mulimilor date
se va scrie predicatul
c:-a,b.
Aici trebuie de menionat c prelucrarea implicit a
mulimilor depinde mult de faptul dac varianta Prolog, ce este
la dispoziie, susine memorarea i tergerea dinamic a regulilor
sau numai a faptelor.

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