Calcul numeric / simbolic cu polinoame in Prolog
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Calcul numeric / simbolic cu polinoame
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Paul Mateescu, grupa 309-Informatica, an III-ZI, 2009.
% Facultatea de Matematica si Informatica, Universitatea Spiru Haret Bucuresti.
% http://fmi.spiruharet.ro/bodorin/aicl.html
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Prolog, TP 2.0
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/************************
Calculul sumei, produsului si derivatei polinoamelor.
Solutia propusa implica aducerea unui polinom la o forma canonica de
lista de monoame ordonate strict crescator in ordinea gradului.
Acest lucru se realizeaza prin transformarea unui polinom intr-un arbore binar in
ale carui noduri avem monoamele, astfel incat toate monoamele din sub-arborele stang
sa aiba grad <= monomul din nodul considerat, iar cele din sub-arborele drept sa aiba
gradul strict mai mare.
Arborele binar va fi apoi traversat in inordine, rezultand o sortare a monoamelor in
sens crescator al gradelor. Polinomului rezultat i se aplica reguli de simplificare:
monoamele de acelasi grad se aduna, monoamele de coeficient zero se elimina.
Suma a doua polinoame se face "injectand" monoamele ambelor in acelasi arbore binar si
aducand rezultatul la forma canonica.
Produsul se face uzual, ca suma de produs al monoamelor unui polinom cu celalalt polinom
si aducand rezultatul la forma canonica.
Derivarea se face monom cu monom si adaugand rezultatul la forma canonica.
Am introdus si predicate care afiseaza rezultatul intr-o forma mai lizibila, producand afisari de genul:
P(X) = +1+X
Q(X) = +1-X+X^2
P(X)*Q(X) = +1+X^3
Testarea usoara se face folosind predicatele fara argumente:
testsuma
testprod
testderiv
*************************/
DOMAINS
i=integer
r=real
lr=r*
li=i*
c=real % coefficient
v=string % symbolic variable
p=integer % power
mon=m(real,integer) % monomial
monom=mon*
poly=mon* % polynomial
tree = t(mon, tree, tree)
PREDICATES
/************************
* De aici incepe rezolvarea temei (a se vedea adresa http://fmi.spiruharet.ro/bodorin/labs/ai/091109-13-ai-a3-s1-l005-symbolic-computation-polynomials.html ):
*************************/
epsilon(r) % Precizia sub care se considera ca un coeficient este de fapt zero
zero(r) % Reuseste daca argumentul este zero in acceptiunea de mai sus, esueaza altfel
unu(r) % Reuseste daca argumentul este 1 in acceptiunea de mai sus
sum(poly, poly, poly) % Insumeaza primele doua polinoame, returnand rezultatul in forma canonica
prod(poly, poly, poly) % Produsul a doua polinoame, returnand rezultatul in forma canonica
prodh(poly, poly, poly, poly) % Ajutator pentru prod(poly, poly, poly)
prodmon(mon, poly, poly) % Produsul unui monom cu un polinom, returnand rezultatul in forma canonica
prodmonh(mon, poly, poly) % Ajutator pentru prodmon(mon, poly, poly)
deriv(poly, poly) % Deriveaza un polinom, returnand rezultatul in forma canonica
derivh(poly, poly) % Ajutator pentru deriv(poly, poly)
proper(poly, poly) % Transforma un polinom in forma canonica
lesseq(mon, mon) % Primul argument este <= cu al doilea daca gradele lor sunt in relatia "<="
maketree(poly, tree) % Transforma un polinom intr-un arbore binar al monoamelor componente, sortate in arbore conform relatiei "<="
inject(tree, mon) % Injecteaza un monom in arborele binar, conform relatiei "<="
inorder(tree, poly) % Traverseaza in inordine arborele binar, returnand polinomul in forma canonica
inorderh(tree, poly, poly) % Predicat ajutator pentru inorder(tree, poly)
simpl(poly, poly) % Simplifica un polinom pentru a-l aduce la forma canonica
simplh(poly, poly) % Predicat ajutator pentru simplifica(poly, poly)
writepoly(poly) % Scrie un polinom intr-o forma cat mai lizibila
writemon(mon) % Scrie un monom intr-o forma cat mai lizibila
testsuma % Predicat de test, cere 2 polinoame, le afiseaza in forma canonica si suma lor
testprod % Predicat de test, cere 2 polinoame, le afiseaza in forma canonica si produsul lor
testderiv % Predicat de test, cere un polinom, il afiseaza in forma canonica si derivata sa
CLAUSES
/***************************************************************/
% Precizia sub care se considera ca un numar este egal cu zero
% (o)
epsilon(1E-16).
/***************************************************************/
% Verifica daca numarul este aproximativ egal cu 0, cu toleranta EPS
% (i)
zero(X) :-
epsilon(EPS),
abs(X) <= EPS.
/***************************************************************/
% Verifica daca numarul este aproximativ egal cu 1, cu toleranta EPS
% (i)
unu(X) :-
Z = X - 1.0,
zero(Z).
/***************************************************************/
% Insumeaza doua polinoame:
% (i,i, o)
% Notand sum(POLY1, POLY2, POLYS), semnificatia argumentelor este:
% POLY1 - primul polinom de insumat
% POLI2 - al doilea polinom de insumat
% POLYS - polinomul-suma, in forma canonica
% Insumarea se face "injectand" succesiv monoamele ambelor polinoame in acelasi
% arbore binar (vezi predicatul proper), apoi aducand rezultatul la forma canonica
sum(POLY1, POLY2, POLYS) :-
maketree(POLY1, TREE),
maketree(POLY2, TREE),
inorder(TREE, POLYSUM),
simpl(POLYSUM, POLYS),
!.
/***************************************************************/
% Calculeaza produsul a doua polinoame date in forma canonica
% (i, i, o)
% Rezultatul este intors tot sub forma canonica
prod(P1, P2, P3) :-
prodh(P1, P2, [m(0, 0)], P),
proper(P, P3), !.
/***************************************************************/
% Predicat ajutator pentru prod(poly, poly, poly)
% (i, i, o, o)
% Notand prodh(P1, P2, SUMI, PROD), semnificatia argumentelor este:
% P1 - primul polinom
% P2 - al doilea polinom
% SUMI - suma intermediara generata de inmultirile monoamelor din P1 cu P2
% PROD - la sfarsit, contine produsul P1 * P2
% Conditia de oprire:
prodh([], _, P, P).
% Produsul celor doua polinoame este produsul primului monom al
% primului polinom cu cel de-al doilea polinom, insumat cu produsul
% restului primului polinom (ignorand deci primul monom) cu al doilea polinom
prodh([MON|T], P2, SUMI, PROD) :-
prodmon(MON, P2, P),
sum(P, SUMI, SUMI1),
!,
prodh(T, P2, SUMI1, PROD).
/***************************************************************/
% Produsul unui monom cu un polinom
% (i, i, o)
% Notand prodh(MON, POLI, PROD), semnificatia argumentelor este:
% MON - monomul de inmultit
% POLI - polinomul cu care se inmulteste
% PROD - produsul MON*POLI, n forma canonica
prodmon(A, B, C) :-
prodmonh(A, B, C1),
proper(C1, C).
/***************************************************************/
% Ajutator pentru prodmon
% (i, i, o)
% Notand prodmonh(MON, POLI, PROD), semnificatia argumentelor este:
% MON - monomul de inmultit
% POLI - polinomul cu care se inmulteste
% PROD - produsul MON*POLI
% Realizeaza parcurgerea parcurgerea polinomului POLI, monom cu monom,
% inmultind pe fiecare cu monomul MON si construind lista rezultatelor
% Conditia de oprire:
prodmonh(_, [], []).
% Parcurgerea element cu element a polinomului:
prodmonh(m(C1, E1), [m(C2, E2)|T], [m(CP, EP)|REST]) :-
CP = C1 * C2,
EP = E1 + E2,
!,
prodmonh(m(C1, E1), T, REST).
/***************************************************************/
% Deriveaza un polinom:
% (i,o)
% Notand deriv(POLY1, POLY2), semnificatia argumentelor este:
% POLY1 - polinomul de derivat
% POLY2 - polinomul derivat, in forma canonica
deriv(POLY, DERIV) :-
derivh(POLY, D),
proper(D, DERIV).
/***************************************************************/
% Ajutator pentru deriv:
% (i,o)
% Notand derivh(POLY1, POLY2), semnificatia argumentelor este:
% POLY1 - polinomul de derivat
% POLY2 - polinomul derivat, care poate sa nu fie in forma canonica
% Conditia de oprire:
derivh([], []) :- !.
% Derivata unei constante este zero:
derivh([m(C, 0)|REST], RESTDERIV) :-
!,
deriv(REST, RESTDERIV).
% Derivata unui monom C*X^G este G*C*X^(G-1)
derivh([m(C, G)|REST], [m(CD, GD)|RESTDERIV]) :-
!,
CD = G * C,
GD = G - 1,
derivh(REST, RESTDERIV).
/***************************************************************/
% Aduce un polinom la forma canonica:
% (i,o)
% Notand prodh(POLI, POLIC), semnificatia argumentelor este:
% POLI - polinomul de adus la forma canonica
% POLIC - polinomul adus la forma canonica
% Aducerea la forma canonica se realizeaza prin:
% 1. "injectarea" fiecarui monom component al polinomului intr-un
% arbore binar conform relatiei "<=" aplicata gradului monoamelor
% 2. arborele binar rezultat este parcurs in inordine pentru a
% rezulta o lista de monoame ordonate crescator dupa gradul lor
% 3. lista ordonata este simplificata dupa anumite reguli, pentru
% a rezulta forma canonica
proper(POLY1, POLY2) :-
maketree(POLY1, TREE),
inorder(TREE, POLY3),
simpl(POLY3, POLY2),
!.
/***************************************************************/
% Verifica daca doua monoame sunt in relatia "<=", privitor la gradele lor
% (i,i)
% Semnificatia parametrilor este evidenta
lesseq(m(_, E1), m(_, E2)) :- E1 <= E2.
/***************************************************************/
% Transforma un polinom intr-un arbore binar, dupa regula descrisa la predicatul inject
% (i,o)
% Notand maketree(POLY, TREE), semnificatia argumentelor este:
% POLY - polinomul de transformat
% TREE - arborele binar rezultat
% injecteaza fiecare monom al polinomului in arbore, incepand din radacina:
maketree([H|T], TREE) :-
inject(TREE, H),
!,
maketree(T, TREE).
% conditia de oprire:
maketree([], _).
/***************************************************************/
% "Injecteaza" un monom intr-un arbore, dupa algoritmul:
% (m,i) - "m" inseamna ca are rol mixt
% Notand inject(NOD, MONOM), semnificatia argumentelor este:
% NOD - nodul din arborele binar la care s-a ajuns
% MONOM - monomul care se "injecteaza"
% Daca nodul este o variabila libera, "aseaza" monomul aici:
inject(X, M) :-
free(X), !, X = t(M, _, _).
% Daca gradul monomului este <= gradul nodului, propaga monomul pe ramura stanga:
inject(X, M) :-
X = t(MV, L, _),
lesseq(M, MV),
!,
inject(L, M).
% Altfel inseamna ca gradul monomului este mai mare, propaga monomul pe ramura dreapta:
inject(t(_, _, R), M) :-
!,
inject(R, M).
/***************************************************************/
% Traverseaza un arbore binar in inordine:
% (i,o)
% Notand inorder(TREE, POLY), semnificatia argumentelor este:
% TREE - arborele de traversat
% POLY - polinomul rezultat
inorder(TREE, POLY) :-
inorderh(TREE, POLY, []).
/***************************************************************/
% Ajutator pentru inorder:
% (i,o)
% Notand inorderh(TREE, POLY1, POLY2), semnificatia argumentelor este:
% TREE - nodul la care s-a ajuns
% POLY1 - elementul de lista al polinomului la care s-a ajuns acum
% POLY2 - elementul de lista liber, care va fi ocupat la urmatoarea prelucrare
% Am ajuns la o frunza-variabila libera, opreste parcurgerea:
inorderh(X, _, _) :-
free(X), !.
% Am ajuns la un nod care este un monom, insa subarborii sai sunt amandoi variabile libere.
% Ataseaza monomul din nod la lista:
inorderh(t(V, L, R), [V|X2], X2) :-
free(L), free(R),
!.
% Am ajuns la un nod-monom, cu subarborele stang var. libera, si dreptul nu.
% Ataseaza monomul la lista si continua cu parcurgerea in inordine pe subarborele drept:
inorderh(t(V, L, R), [V|X1], X2) :-
free(L), !,
inorderh(R, X1, X2).
% Am ajuns la un nod-monom, cu subarborele drept var. libera, si stangul nu.
% Parcurge in inordine subarborele stang, apoi ataseaza la lista monomul:
inorderh(t(V, L, R), X1, X2) :-
free(R), !,
inorderh(L, X1, X3),
X3 = [V|X2].
% Am ajuns la un nod-monom, care are si sub-arbore stang, si sub-arbore drept.
% Parcurge in inordine subarborele stang, ataseaza nodul, apoi parcurge sub-arborele drept:
inorderh(t(V, L, R), X1, X3) :-
!,
inorderh(L, X1, [V|X2]),
!,
inorderh(R, X2, X3).
/***************************************************************/
% Simplifica un polinom cu monoamele in ordine crescatoare a gradelor:
% (i,o)
% Notand simpl(POLY1, POLY2), semnificatia argumentelor este:
% POLY1 - polinomul cu monoamele sortate crescator ca grade
% POLY2 - polinomul in forma canonica
% Pentru cazul in care rezultatul unor operatii este un polinom vid, il egaleaza cu monomul 0*X^0:
simpl([], [m(0,0)]) :- !.
% Daca rezultatul simplificarilor este o lista vida (de ex, polinomul a fost X - X), trece la urmatoarea clauza,
% altfel se opreste aici:
simpl(POL, POLS) :-
simplh(POL, POLS), not(POLS = []), !.
% Ajunge aici doar daca rezultatul simplificarilor este un polinom-lista vida,
% si il transforma in polinomul 0*X^0
simpl(_, [m(0,0)]).
/***************************************************************/
% Ajutator pentru simpl:
% (i,o)
% Semnificatia argumentelor este aceeasi cu ale lui simpl
% Lista vida nu mai poate fi simplificata:
simplh([], []).
% Sterge un monom cu coeficient aproximativ egal cu zero:
simplh([m(Z, _)|REST], X) :-
zero(Z),
!,
simpl(REST, X).
% Aduna doua monoame de acelasi grad si continua evaluarea din acelasi punct
% (pot fi mai multe monoame de acelasi grad):
simplh([m(A, G), m(B, G)|REST], X) :-
C = A + B,
!,
simplh([m(C, G)|REST], X).
% Nu mai exista o regula de simplificare, adauga monomul la rezultat:
simplh([MON|REST], [MON|RESTS]) :-
!,
simplh(REST, RESTS).
/***************************************************************/
% PREDICATE PENTRU SCRIEREA UNUI POLINOM INTR-O FORMA LIZIBILA
% S-au folosit optimizari de genul:
% 3*X^2 se scrie 3X^2
% 1*X^N se scrie X^N
% 2*X^0 se scrie 2
writepoly([m(C, 0)]) :- !, write(C).
writepoly([m(Z, 0)|REST]) :- zero(Z), !, writepoly(REST).
writepoly([MONOM|REST]) :- writemon(MONOM), !, writepoly(REST).
writepoly([]).
writemon(m(0, _)) :- write("0"),!.
writemon(m(C, 0)) :- C<0, !, write(C).
writemon(m(C, 0)) :- C>=0, !, write("+", C).
writemon(m(UNU, 1)) :- unu(UNU), !, write("+X").
writemon(m(UNU, N)) :- unu(UNU), !,write("+X^", N).
writemon(m(MINUSUNU, 1)) :- X = - MINUSUNU, unu(X), !, write("-X").
writemon(m(MINUSUNU, N)) :- X = - MINUSUNU, unu(X), !, write("-X^", N).
writemon(m(C, 1)) :- C >0, !, write("+", C, "X").
writemon(m(C, 1)) :- C < 0, !, write(C, "X").
writemon(m(C, N)) :- C > 0, !, write("+", C, "X^", N).
writemon(m(C, N)) :- C < 0, !, write(C, "X^", N).
/***************************************************************/
% Predicat de test.
% - Cere doua polinoame
% - Le aduce la forma canonica
% - Le afiseaza
% - Calculeaza suma lor
% - Afiseaza si suma
testsuma :-
write("Test suma a 2 polinoame, P+Q."),
nl,
write("Introduceti polinomul P:"), nl,
cinpoly(P), proper(P, PP),
nl,
write("Introduceti polinomul Q:"), nl,
cinpoly(Q), proper(Q, PQ),
nl,
write("============="),
nl,
write("P(X)="), writepoly(PP), nl,
write("Q(X)="), writepoly(PQ), nl,
sum(PP, PQ, SUM),
write("P(X)+Q(X)="), writepoly(SUM), nl.
/***************************************************************/
% Predicat de test.
% - Cere doua polinoame
% - Le aduce la forma canonica
% - Le afiseaza
% - Calculeaza produsul lor
% - Afiseaza si produsul
testprod :-
write("Test produsul a 2 polinoame, P*Q."),
nl,
write("Introduceti polinomul P:"), nl,
cinpoly(P), proper(P, PP),
nl,
write("Introduceti polinomul Q:"), nl,
cinpoly(Q), proper(Q, PQ),
nl,
write("============="),
nl,
write("P(X)="), writepoly(PP), nl,
write("Q(X)="), writepoly(PQ), nl,
prod(PP, PQ, PROD),
write("P(X)*Q(X)="), writepoly(PROD), nl.
/***************************************************************/
% Predicat de test.
% - Cere un polinom
% - Il aduce la forma canonica
% - Il afiseaza
% - Calculeaza derivata sa
% - Afiseaza si derivata
testderiv :-
write("Test derivatei unui polinom P."),
nl,
write("Introduceti polinomul P:"), nl,
cinpoly(P), proper(P, PP),
nl,
write("============="),
nl,
write("P(X)="), writepoly(PP), nl,
deriv(PP, PDERIV),
write("P'(X)="), writepoly(PDERIV), nl.
/*******************************************************
* DE AICI URMEAZA SURSA INITIALA A EXERCITIULUI PROPUS:
* Nicolaie Popescu-Bodorin, 2009.
* http://fmi.spiruharet.ro/bodorin/
*******************************************************/
PREDICATES
cinmon(monom) % read a monomial in console
cinpoly(poly) % read a polynomial in console
cinpoly(integer,poly,poly)
continue(poly,integer) % end or continue to read with cinpoly
liststopoly(lr,li,poly) % from two lists to a polynomial and conversely
pow(r,i,r) % natural power of a real number
pow(r,i,r,r)
polyfeval(poly,r,r) % evaluate a polynomial function in a given point
polyfeval(poly,r,r,r)
CLAUSES
% Read a monomial in console:
cinmon(M):-
write("Give a coefficient:"),readreal(C),nl,
write("Give an exponent:"),readint(P),nl,
M = [m(C,P)],
write("Your monomial is: "),nl,
%write(C),write("X"),write(P),nl,
write(M),nl.
% Read a polynomial in console (symbolic computation):
cinpoly(P):-write("Give a polynomial."),nl,
cinpoly(1,[],P).
cinpoly(0,Ri,Ri):-
write("Your polynomial is: "),nl,
write(Ri),nl.
cinpoly(1,Ri,R):-
cinmon([M]),
continue([M|Ri],Fa),
cinpoly(Fa,[M|Ri],R).
% End a reading (cinpoly):
continue(Ri,B):-
write("Until now, your polynomial is: "),nl,
write(Ri),nl,
write("Do you want to continue?[1/0]"),readint(B).
% Convert two lists (coefficients and exponents)
% into a polynomial and conversely (symbolic computation):
% usage: (i,i,o),(o,o,i) - (lr,li,poly)
liststopoly([],[],[]).
liststopoly([HC|TC],[HE|TE],[m(HC,HE)|T]):-
liststopoly(TC, TE, T).
% Evaluate a polynomial function at a given point
% (converting a symbolic expression to a number, giving a numerical meaning to a symbolic expression):
% usage: (i,i,o) - (poly,real,real)
polyfeval([m(C,E)],X,R):-pow(X,E,P),R=C*P,!.
polyfeval(P,X,R):-polyfeval(P,X,0,R).
polyfeval([],_,Ri,Ri).
polyfeval([H|T],X,Ri,R):-
polyfeval([H],X,RH),
Ria=Ri+RH,
polyfeval(T,X,Ria,R).
% natural power of a real number:
% usage: (i,i,o) - (real,integer,real)
pow(_,0,1):-!.
pow(X,N,R):-pow(X,N,1,R).
pow(_,0,Ri,Ri).
pow(X,N,Ri,R):-N>0,Na=N-1,Ria=Ri*X,pow(X,Na,Ria,R).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Homework assignment:
% Implement sum, product, and derivation of polynomials.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%