Calculator de polinoame în Java

Diagrama UML Calculator Polinom

Înainte de toate trebuie menționat că acesta a fost un proiect ce l-am avut la una din materiile de facultă. Tocmai de aceea, acest proiect trebuie să fie doar o mică inspirație și recomand tuturor să încerce singuri o astfel de provocare. Utilitatea didactică se dovedește atunci când muncești integral și personal la căte-o chestie de genul.

Pentru a vedea codul asociat, mergeți pe pagina mea de GIT: marianstefi20

Pentru a vedea un demo, vizionați videoclipul de mai jos:

Mai jos puteți vedea documentația asociată proiectului.

Cuprins

  1. Introducere 
  2. Analiza problemei și modelarea acesteia 
    1. Cazuri de utilizare 
    2. Scenarii
  3. Proiectare
    1. Clasa Panel
    2. Clasa PolyInterpreter 
    3. Clasa Polinom
      1.  Adunarea a două polinoame
      2.  Scăderea a două polinoame
      3. Înmulțirea a două polinoame
      4. Împărțirea a două polinoame 
    4.  Clasa Monom
    5. Cartezian
    6. Rezultate
  4. Concluzii

 

1.  Introducere

Polinoamele și operațiile cu acestea sunt unele dintre cele mai comune structuri matematice din Computer Science. Motivul principal este simplitatea algebrică a operațiilor – vorbim doar de adunări și înmulțiri (care la rândul lor sunt adunări repetate). Avantajul este că polinoamele pot aproxima oricât de bine orice funcție continuă și derivabilă, deci pot “interpola” funcții mult mai complexe.

Tocmai de aceea, în multe aplicații se pot regăsi polinoame, într-o formă sau alta, alături de un set de funcții de bază cu care se vor pot manipula.

Scopul acestui proiect este de a implementa operații și funcții de bază pe polinoame, cu scop demonstrativ, pentru a reliefa modul în care acestea pot fi create și manipulate prin intermediul paradigmelor programării orientate pe obiecte. De aceea, materializarea acestui obiectiv va fi reprezentată de crearea unei aplicații de sine stătătoare, independentă, care să poată realiza operații elementare pe un spațiu cât mai larg.

Printre operațiile de bază se vor număra cele de adunare, scădere, înmulțire și împărțire, dar și o serie de operații auxiliare care nu sunt în directă legătura cu funcționalitățile clasei principale(aici vorbim de parsatoarele de string-uri).

În final, voi pune accentul pe afișarea grafică a polinomului prin intermediul unor aproximări liniare cu pas mic, în felul acesta “păcălind” observatorul.

 

2.  Analiza problemei și modelarea acesteia

Prin analiza problemei, ne referim la un prim set abstract de operații și proprietăți prin care încercăm să depistăm eventualele însușiri și comportamente ale proceselor necunoscute. Programarea orientată ne oferă aici un avantaj clar, tocmai fiindcă ea permite să taclăm problema de la un nivel superiror, fără a mai fi constrâși, într-o așa măsură, de caracteristicile tehnice.

Această strategie de conceptualizare, mai poartă numele și de bottom-up design. Este foarte avantajoasă din prisma găsirii componentelor constituente, deoarece pot fi găsite, relativ ușor, structuri cu o legătură directă în lumea reală( obiecte, acțiuni etc.). Din păcate această versatilitate vine cu prețul complexității, ea crescând spre măsură ce se avansează pe nivelele inferioare.

De cele mai multe ori se pornește de la specificația proiectului, căutându-se:

  • Substantive, care devin eventuale clase candidat
  • Verbe ce ar putea juca rolul metodelor din clasă.

Odată realizat pasul de mai sus, ar trebui să avem o idee foarte generală asupra problemei. Pasul următor constă în descrierea funcțională a acesteia. Ce trebuie să facă aplicația la intrările X?

Programul va putea fi accesat de un număr ridicat de persoane, de aceea interfața cu utilizatorul devine punctul de pornire al proiectului. Ea trebuie să permită, într-o manieră cât mai convenabilă, comunicarea utilizatorului cu aplicația.

În cazul Calculatorului de Polinoame, se cunoaște că aplicația trebuie să implementeze operațiile algebrice elementare – or asta implică că trebuie să existe minim două polinoame și/sau două câmpuri de intrare. Modul de codificare al informației, dar și formatul acesteia ocupă un rol foarte important.

În cadrul CP m-am decis ca intrările aplicației să codifice string-uri. Utilizatorul poate astfel introduce structuri de text care nu trebuie să fie în legătură directă cu forma canonică a unui polinom – de ex. comenzi pentru gestiunea coeficientilor.

 

2.1.  Cazuri de utilizare

Din momentul stabilirii metodei de intrare accentul se va pune, paradoxal, mai mult pe cazurile limită, care vor fi tratate mai special. Schematic, în CP, vorbim de următoarele:

  • User-ul introduce un polinom “dezordonat”, iar rezultatul este un polinom minificat și ordonat
  • Suma/Diferența/Produsul și Împărțirea între primul polinom și al doilea
  • Calcularea rezultatelor numerice pentru primul polinom și al doilea
  • Cuvinte speciale utilizate în string-urile ce conțin polinoamele
  • Trasarea graficului pentru primul polinom

 

2.2.  Scenarii

Utilizarea normală a programului presupune introducerea primului polinom în formatul clasic LateX, în mod aleator, apoi se apasă butonul de OK pentru a vedea polinomul minificat și ordonat. Se repetă procesul și pentru polinomul numărul doi, iar după ce user-ul îl poate vizualiza în zona nr. II, poate să efectueze operațiile elementare existente sub TextField-uri. La fiecare din operațiile menționate mai sus, utilizatorul poate să atribuie valori specifice lui x pentru a vedea rezultatul numeric.

Totuși, cazul de mai sus este puțin probabil și adesea lucruri neașteptate pot să apară. Dintre acestea se remarcă :

  • Introducerea de text, fără semnificație numerică, în câmpurile test
  • Introducerea de necunoscute suplimentare(pe langă x)
  • Folosirea altor cuvinte cheie decât cele specificate
  • Scriere corectă a polinoamelor, dar moduri sintactice care diferă puțin de la user la user( de ex. 2x + 2 vs. 2x^3+5x )

 

3. Proiectare

Diagrama UML Calculator Polinom

Calculatorul de Polinoame conține 5 clase de bază și o clasă main ce le instanțiază.

  1. Clasa Panel – cu ajutorul ei se creează interfața grafică folosită în tot proiectul. Tot aici se instanțiază și asculatătorii. Ei au rolul de a surprinde eventualele acțiuni ale utilizatorilor prin intermediul evenimentelor. Tocmai de aceea, la nivelul câmpurilor de text și a butoanelor există câte un ascultător separat.
  2. Clasa PolyInterpreter – această clasă procesează datele care sunt “prinse” de ascultători și activează modulele corespunzatoare în funcție de opțiunea aleasă.
  3. Clasa Polinom – permite crearea polinoamelor ca liste de monoame, entități și mai fundamentale, dar conține și partea sortare și minificare a acestora. În acest fel se tratează unul din cazurile de utilizare mai deosebite, când utilizatorul nu respectă în totalitate formatul default al scrierii unui polinom.
  4. Clasa Monom – clasa fundamentală a proiectului ce definește structura de monom, entitate ce conține doar un coeficient ( real sau întreg), o putere și, opțional, un String pentru a printa rapid monomul. Tot aici, se află și operațiile fundamentale: +, –, *, / .
  5. Clasa Cartezian – nu este neapărat necesară, între ea și celelalte clase existând doar o dependență foarte slabă. Această clasă permite trasarea, relativ dinamic, a polinoamelor, ținându-se cont de valorile de intrare.

Pe lângă tipurile de date primitive existente în clasele specificate mai sus, se folosesc cu preponderență și colecții. Una din cele mai importante colecții se află în clasa Polinom, ce conține o listă de monoame. Desigur, există o de compoziție între un polinom și elementele sale fundamentale, monoamele – în absența lor, o instanță Polinom nu există.

În ansamblu, există o similitudine între modul de proiectare a claselor și MVC pattern. Panel joacă rolul unui View, PolyInterpreter este Controller-ul și în final Polinom, Monom și Cartezian reprezintă clase model.

 

3.1.  Clasa Panel

Conține un Jframe pe care se construiește întreaga interfață.

S-a adoptat un layout de tip grid, în special deoarece chiar de la început se pot remarca două zone distincte ale interfeței cu utilizatorul:

  1. Partea de control, reprezentată de câmpurile și butoanele prin care utilizatorul introduce datele și poate să vadă primele rezultate concrete asupra polinoamelor;
  2. Partea de afișare grafică, ce folosește clasa Cartezian într-un mod aproape separat de fluxul normal al programului, afișând automat polinomul 1.

În partea de control, există câteva segmente de cod de aceeași natură, care se repetă cu mici variații. De exemplu, cele sașe zone în care se afișează informațiile despre polinoame și rezultatul operațiilor elementare sunt structural identice, atâta doar că își procură informația din alte locuri.

În snippet-ul de mai sus se poate remarca structura de bază a unei zone de afișare a datelor despre polinoame. Cele două label-uri se află într-un panel cu layout-ul tot de tip grid, deoarece la redimensionarea ecranului, lățimea se modifică și vrem o oarecare repoziționare a câmpurilor.

Metoda showEventDemo() conține cu precădere ascultătorii, care sunt utilizați mai departe în PolyInterpreter.

 

3.2.  Clasa PolyInterpreter

Este clasa care gestionează toate acțiunile venite din partea utilizatorilor și le corelează cu funcționalitățile preexistente. Tocmai de aceea, această clasă implementează interfața ActionListener și implicit metoda actionPerformed(). Există astfel o singură metodă care gestionează toate evenimentele venite din partea de view și asta fiindcă nu avem nevoie de un paralelism la nivelul tratării acțiunilor – în sensul că utilizatorul nu va executa două sau mai multe acțiuni în mod simutan.

Una din cele mai importante trăsături a clasei este că ea conține tot timpul două String-uri. Utilizatorul va introduce tot timpul informația sub formă de text în cele 2 JTextField-uri. Informația este neschimbată atâta timp cât utilizatorul nu actualizează datele din text field-uri. Rămâne doar să “selectăm” între diferitele acțiuni folosindu-ne de ActionCommand setate în clasa Panel.

Indiferent de tipul acțiunii, la fiecare nou apel trebuie să creăm noi polinoame (deoarece informațiile se schimbă), dar asta nu înseamnă că modul de creare al acestora diferă. Metoda crearePolinom() este prima metodă care face cea dintâi parsare a string-urilor.

  1. Se verifică dacă există cuvintele cheie integ sau deriv în fata polinomului. Daca da, atunci se va crea un polinom integrat, respectiv derivat și se vor face operațiile asupra acestor noi polinoame.
  2. Dacă nu s-au găsit cuvinte cheie se încearcă “spargerea” string-ului. Pentru a nu fi nevoiți să folosim metode puțin diferite în funcție de context, am ales o abordare mai puțin elegantă, dar eficientă prin care putem trata polinoame cu coeficienți pozitivi sau negativi.
La final, această metodă trimite la constructorul clasei Polinom un vector de String-uri, care vor și acolo parsate suplimentar la nivelul fiecărui monom.

 

3.3. Clasa Polinom

Clasa ce permite crearea efectivă a polinoamelor, ca și colecții de monoame. Ea are un singur constructor public, cel care necesită un vector de String-uri și doi constructori privați ce sunt folosiți strict în structura internă a clasei Polinom, la crearea de polinoame intermediare necesare pentru diferite operații.

La nivelul constructorului public, partea de parsare laborioasă se află în clasa Monom, rezultatul fiind că, în lipsa unei excepții, apelul acestui constructor va crea cu siguranță un obiect de tipul Polinom, ce va conține un ArrayList<Monom> de monoame.

Pe lângă toate acestea, constructorul conține și o metodă de sortare și minificare (sort_and_minify()). În lipsa acestei metode, ne-am putea întâlni cu situații în care am avea, în același polinom, monoame de acelasi grad. Sortarea este mai mult o operație auxiliară, dar benefică, eficientizând alte metode.

 

3.3.1. Adunarea a două polinoame

În cazul favorabil, adunarea a două polinoame se rezumă la a suma coeficienții monoamelor, rând pe rând cât timp gradul lor este egal. Totuși, sumarea trebuie să fie valabilă pe caz general.

p_1(x) = 2x^2+3x^3-5x^4

p_2(x) = 4x^3+6x^4+9x^5-3x^6+7x^3

Să luăm cele două polinoame de mai sus. Un algoritm mai general de însumare se reduce la :

În felul acesta tratăm adunarea pentru cele trei cazuri:

Algoritm calculare grad polinom

 

3.3.2.     Scăderea a două polinoame

Nu este decât un caz particular de adunare, unde coeficienții polinomului 2 sunt cu semnul inversat.

  1. (-1) * p2.monom.coeficient
  2. add(p1, p2)

 

3.3.3.  Înmulțirea a două polinoame

Ne folosim de funcția de sortare care la final va ordona și minifica polinomul rezultat.

 

3.3.4.   Împărțirea a două polinoame

Se poate remarca că în algoritmii prezentați mai sus se fac anumite operații asupra monoamelor. Aceste metode sunt descrise în clasa Monom.

 

3.4. Clasa Monom

Clasa care conține operațiile elementare de adunare, scădere, înmulțire și împărțire. Aceste operații nu sunt complexe în vreo manieră și nu necesită explicații suplimentare.

La nivelul acestei clase, se află metoda validare(). Ea nu este decât o implementare a unui arbore decizional pentru a trata diferitele cazuri ce apar la introducerea datelor.

Schematizat, clasa de mai sus face următoarele verificări asupra string-ului formatat, înainte de a crea monoamele specifice:

  1. Verifică dacă string-ul conține caractere speciale care nu au ce căuta în descrierea unui polinom(simboluri de tipul !, ?, ;)
  2. Se caută apoi după ^ și se tratează separat situațiile: când există și când nu.
  3. Căutăm apoi coeficienții din fața lui x, și tratăm cazurile când nu există nimic în fața acestuia, sau când nu există x.

 

3.5. Cartezian

Este clasa cu care se desenează automat polinomul 1. Această clasă extinde JPanel și suprascrie metoda void paintComponent(Graphics g). Funcționalitatea principală se află în corpul a două metode, cea menționată și cea care calculează valoarea funcției date ca parametru.

Primul pas este să cream axele de coordonate, iar mai apoi să ne desenăm valori într-o manieră dinamică. Pentru aceasta este necesar să folosim un raport de scalare, pentru a ști fată de ce valori să ne raportăm. Ideea este simplă:

  1. În loc să lăsăm dimensiunile fixe, lucrăm cu un raport
  2. Apoi doar desenăm în locații k * raport pentru a obține acea scalare dorită
Cheia unei afișări corespunzătoare este găsirea unui raport la care să raportăm tot timpul valorile de axa Ox cu cele de pe Oy. În esență ne trebuie un factor de scalare care să depindă de lățimea pe care putem desena în raport cu intervalul Ox pe care lucrăm. De aceea, cel mai important lucru este să definim un raport:

Acum, tot timpul, vom vorbi despre ratio * x si ratio * fx.

 

3.6. Rezultate

Cu excepția unor mici cazuri când programul este forțat la în zona de input-uri, programul nu va rula corespunzător deoarece nu există parte de parsare pentru eventualele string-uri aleatoare.

În rest, atât rezultatele, cât și valorile se află în grila celor așteptate, programul având un oarecare grad de flexibilitate la nivelului modului de procesare a polinoamelor și parsare a expresiilor speciale.

 

4. Concluzii

Din această primă temă, mi-am dat seama, din păcate la final de importanța sepărării, la nivel conceptual, a parsării string-urilor fată de interpretarea lor de către clasele Polinom și Monom. Această greșeală am facut-o în principal, fiindcă nu am reușit să estimez complexitatea reală a problemei. O clasă separată, Parsare, de exemplu, ar fi fost o alternativă mult mai viabilă fiindcă în acest fel nu ar fi fost nevoie să existe vreo legătură între procesul de determinare al monoamelor din string-uri și crearea efectivă a polinomului.

Ca și dezvoltări ulteriore, ideea de a converti dintr-o bază polinomială într-o alta pare foarte atractivă, necesitând algoritmi pentru manipularea matricilor de transformare a bazelor. Desigur, o bază generală ar pune mari probleme de implementare, dar și din punct de vedere matematic, așa că implementarea unor baze standard(Laplace, Newton, Legendre) pare o alternativă fezabilă.

Ce am învățat este rolul pe care separarea obiectivelor îl are chiar la începutul problemei. Nu o dată, a trebuit să elimin bucăți semnificative de cod fiindcă încercam să compensez lipsa de rigurozitate din faza de planificare cu clauze suplimentare.

Postare asemănătoare

Ștefănescu Marian

Pasionat de științe exacte, drumeții.

1 Comment

Leave a Reply

Your email address will not be published.