Sunday, January 26, 2014

Șablonul arhitectural - MVC ( Model - View - Controller )

MVC ( Model - View - Controller ) reprezintă un model arhitectural în care "logica programului" este separată de operațiunile de input și output. Mai bine zis programul va fi împărțit în trei zone:
  • zona Model: unde se vor afla informațiile adică baza de date a programului.
  • zona View: aici se va găsi tot ce este legat de afișare precum și operațiile prin care preluăm date de la utilizator ( input și output ).
  • zona Controller: zona în care se face legătura dintre Model și View; în Controller se preia date de la utilizator prin intermediul View-ului, se verifică datele și apoi se accesează Modelul folosind date primite de la View.
În programele create până acum nu era făcută o diferență intre ceea ce reprezenta informație de la/pentru utilizator și ceea ce reprezinta logica programului.
Cam așa arată în mod normal dependența dintre datele primite de la utilizator și prelucrarea acestora.



MVC-ul întroduce o nouă componentă care ajuta la separarea legăturii dintre cele două componente.

 
Acum că avem acel controller, acesta se va ocupa gestionarea datelor primite de la View, le va verifica și apoi va accesa modelul furnizând acele date primite de la utilizator.



În continuare vom vedea cum ar trebui să arate un program care este proiectat folosind acest șablon. Vom considera mai departe trei clase Agenda( Model-ul ), Controller( Controller ) și Interfata ( View ) fiecare cu câteva metode și atribute sugestive pentru ce avem noi nevoie momentan.

Mai jos avem câteva metode din clasa Agenda:

...
public:
 void adaugaContact( Contact c );
 void eliminaContact();
...


Și câteva metode din clasa Interfata:

...
public:
 int getUserAction();
 void afisareInterfataAdaugareContact(...);
 void afisareInterfataEliminareContact(...);
...


Acum trecem la clasa Controller care va conține un obiect de tip Interfata și un obiect de tip Agenda pe lângă alte atribute.

Cum spuneam mai sus controller-ul are rol de a media cele două componente. Așadar o metoda, spre exemplu de control asupra adăugării unui contact va arăta cam așa:

...
void Controller::controllerAdaugareContact(void)
{
 interfata->afisareInterfataAdaugareContact(...);

 // urmează partea în care se verifică informațiile primite de la utilizator
 // apoi se crează contactul propriu zis pe baza acestora
 // după care se trimit informațiile Model-ului

 agenda->adaugareContact( c );
}
...

Implementarea este sumară deoarece scopul nostru este să înțelegem cât mai bine cum funcționează acest șablon.

După cum ați observat mai sus, clasa Controller va conține metode în care se va realiza legătura dintre interfață și model. Pentru operații simple asupra Model-ului, cum ar fi afișarea unui contact, se poate accesa direct Model-ul printr-o interogare de stare acesta răspunzând printr-o notificare asupra stării acestuia. Ideea e că pentru chestii legate de afișare nu mai este nevoie de controller să medieze legătura dintre Model și View.

Friday, January 24, 2014

Simularea unei cozi folosind două stive implementate prin clase

Coada reprezintă o structură abstractă de date în care inserarea unui nou element se face într-o parte a cozii, iar eliminarea se face din capătul celălalt. Conceptul de coadă apare în orice sistem în care obiectele sunt servite în ordinea sosirii.

Cele două capete ale cozii îşi iau numele din această paralelă cu rândul de asteptare. Mai precis, capătul de la care sunt eliminate articolele se numeşte cap (head) iar capătul la care se adaugă elementele se numeşte coadă (tail); pentru a evita confuziile, vom folosi în acest scop termenii de început şi sfârşit.

Stiva reprezintă de asemenea o structură abstractă de date, în care singura deosebire faţă de coadă este că inserarea şi eliminarea se fac de la acelaşi capăt.

Principiul folosit pentru a simula o coadă folosindu-ne de cele două stive este următorul:

Considerăm că inserarea, eliminarea şi citirea se fac la capătul de la începutul stivei. Tot ce avem de făcut acum este să încercăm să definim operaţiile de inserare, eliminare şi citire pentru coadă folosindu-ne de cele pentru stivă.


  • pentru operaţia de inserare consideram că se inserează la începutul cozii deci putem folosi direct funcţia de inserare ( push () ) de la stivă.
  • pentru operaţiile de eliminare şi citire va trebui să mutăm elementele din prima stivă în cea de-a doua stivă. Pentru a avea o perspectivă cât mai clară în legătură cu această mutare urmăriţi exemplul de mai jos.


    • acum că avem elementul care trebuie eliminat sau citit apelăm funcţiile referitoare la citire sau eliminare ( top(), pop() ) de la stive.
    • Pentru fiecare nod din stivă avem nevoie de o structură cu două câmpuri în care un câmp va conţine informaţia din nodul respectiv, iar celălalt va conţine legatura către următorul nod.

      typedef int Type; 
      //asociem tipului int aliasul Type 
      //de asemenea putem asocia orice tip de dată pentru Type 
      //singura modificare se va face aici
       
       typedef struct NodeStack {
       Type info; //informatia continuta in nod
       struct NodeStack *next; //legatura catre nodul urmator
       } NodeStack;
       
      Pentru a nu exista neclarităţi la nivelul codului mai creem o structură care va conţine pointer către primul nod din stivă şi numărul elementelor din stivă.

      typedef struct Stack {
       NodeStack *prim; //pointer catre primul element din lista
       int nrElt;  //numarul de elemente in lista
      } Stack;

      Acum că avem structura definită pentru stivă, trecem la implementarea clasei ce va conţine ca şi metode, operaţiile caracteristice pentru stivă - push(), pop(), top().

      class MyStack
      {
      public:
       MyStack(void);  //constructor
       ~MyStack(void); //destructor
       void pop();       //metodele referitoare la operatiile cu stive
       void push(Type);  //si de asemenea Getters && Setters pentru elementele stivei
       Type top();
       bool isEmpty();
       void setNrElt(int);
       void setPrim(NodeStack*);
       NodeStack* getPrim();
       int getNrElt();
      private:
       Stack stack;
      };

      Având definită clasa MyStack, începem simularea cozii. Pentru aceasta avem nevoie de două obiecte de tip MyStack.

      MyStack *stack1 = new MyStack(), *stack2= new MyStack();

      Vom stoca informaţiile primite în obiectul stack1 iar obiectul stack2 va fi folosit pentru mutarea elementelor în cazul unei eliminări sau citiri.

      Pentru funcţia de înserare în coadă se va folosi funcţia de inserare în stivă:

      Type e;
      printf("Dati elementul: \n");
      scanf("%d",&e);
      stack2->push(e);
       
      Pentru funcţiile de eliminare sau citire din coadă avem nevoie sa mutăm elementele din primul obiect în cel de-al doilea şi să afisăm/eliminăm elementul utilizând această secvenţă de cod:

      while(!stack1->isEmpty())
      {
       stack2->push(stack1->top());
       stack1->pop();
      }
      printf("Elementul eliminat este %d ",stack2->top());
      stack2->pop();
      //sau pentru citire
      printf("Elementul cel mai vechi este %d",stack2->top());

      În cazul în care coada este goala se va afişa un mesaj corespunzător.

      Tuesday, January 21, 2014

      Implementarea unui arbore binar folosind liste înlanţuite

      Un arbore binar este un arbore în care fiecare nod are cel mult doi fii, arborele din stânga şi arborele din dreapta. Fiecare nod dintr-un arbore binar se reprezintă de obicei printr-o structură în care folosim un câmp pentru a stoca informaţia (eticheta) şi doi pointeri către cele două noduri, pointerul către nodul fiu din stânga si pointerul către nodul fiu din dreapta. Dacă unul sau amândoi fiii unui nod lipsesc, pointerii corespunzători au valoarea zero.

      Definim structura cu specificaţiile de mai sus.

      typedef struct NodeTree {
      
          char info;   //informaţia conţinută în nod
      
          struct NodeTree *stg; //legatura către nodul stang
      
          struct NodeTree *drp;       //legatura către nodul drept
      
      } NodeTree;

      Acum că avem definită structura NodeTree, trecem la funcţia ce va crea un arbore binar dintr-o secvenţa de caractere primită ca parametru. Secvenţa este constituită din litere mari şi caracterele ( , ) $. Seminificaţia acestora este următoarea:

      • literele reprezintă informaţia dintr-un nod;
      • '(' defineşte un nivel inferior;
      • ',' separă sub-arborele stâng de cel drept;
      • ')' marchează stârşitul nivelului inferior;
      • '$' marchează lipsa sub-arborelui respectiv.
      Exemplu: "A ( B , C ( D , E ( F , $ ) )", iar arborele va arăta cam asa:

      Funcţia "create(char *)" va primi ca argument secvenţa de caractere şi va returna nodul rădăcină al arborelui. Logica din spatele acestei funcţii este următoarea:
      • parcugem secvenţa de caractere şi verificăm dacă caracterul este literă mare. Dacă da, atunci alocăm spaţiu pentru un nod de tip NodeTree, în campul pentru informaţie salvăm caracterul, fii săi vor fi nuli, apoi îl punem pe o stivă.



        Cam aşa va arata stiva noastră după ce au fost introduse toate nodurile.

      • după ce avem elementele pe stivă începem să facem legăturile dintre noduri şi fii lor.
        Ultimul element de pe stivă va fi nodul rădăcină pe care funcţia noastă îl va returna.

      Legăturile dintre noduri şi fii lor sunt realizate de următoarea scvenţă de cod:

      NodeTree *fs, *fd;
           fs=(NodeTree*)malloc(sizeof(NodeTree));
           fd=(NodeTree*)malloc(sizeof(NodeTree));
           fd=top(&S);   //scoatem fiul drept de pe stivă
           pop(&S);      //eliminăm fiul drept
           fs=top(&S);   //scoatem fiul stâng de pe stivă
           pop(&S);      //eliminăm fiul stâng
           v=top(&S);    //scoatem nodul părinte pentru cei doi fii
           pop(&S);      //îl eliminăm de pe stivă
           v->stg=fs;    //realizăm legăturile dintre aceştia
           v->drp=fd;
           push(&S,v);   //îl punem din nou pe stivă
       
      Acum că avem arborele avem nevoie de o modalitate de a-l afişa. Exista 4 modalităţi de afişare a unui arbore binar: PreOrdine, InOrdine, PostOrdine şi parcurgerea BFS( Breadth-first Search ). Primele 3 metode diferă doar prin ordinea în care sunt vizitate cele 3 componente ale arborelui.
        • în parcurgerea PreOrdine mai întâi este vizitată rădăcina apoi subarborele stâng apoi cel drept. 
        • void RSD(NodeTree* v, void viziteaza(NodeTree*))
          {
              if (v ==NULL)return;
              else
              {
                  viziteaza(v); // o procedură ce afisează informaţia din nod
                  RSD(v->stg, viziteaza); // parcurgere recursivă pentru fiecare subarbore
                  RSD(v->drp, viziteaza);
              }
          }
           
           
        • în parcurgerea InOrdine mai întâi este vizitat subarborele stâng, rădăcina apoi cel drept.
        • în parcurgerea PostOrdine mai întâi este vizitat subarborele stâng, subarborele drept apoi rădăcina.
      În ultima metodă de afişare a arborelui parcurgerea este realizată pe nivele, începând cu rădăcina şi continuând cu fiecare nivel.

      void BFS(NodeTree *t, void viziteaza(NodeTree*))
      {
          if (t==NULL) return;
          else
          {
              Queue q;
        q.prim=NULL;
        q.nrElt=0;
              insert(&q, t);
              while (!isEmpty(&q))
              {
                  NodeTree* v;
                  v=read(&q);
                  viziteaza(v);
                  if (v->stg!=NULL)insert(&q, v->stg);
                  if (v->drp!=NULL)insert(&q, v->drp);
                  remove(&q);
              }
          }
      }

      Monday, January 20, 2014

      ASM - Clase în limbaj de asamblare (notiuni introductive)

      O clasă este o construcție prin care se definesc noi tipuri de date prin asocierea unui set de funcții(metode) la o structură de date. Iată un exemplu de clasă:

      class Persoana{
      private:
       char nume[25]; // vector de caractere unde vom salva numele persoanei
       int size; // variabila ce va memora dimensiunea numelui
      public:
       Persoana(); // constructor implicit
       ~Persoana(); // destructor implicit
       void setNume(char*);
      };
       
      După cum am văzut mai sus apar niște etichete ce desemnează drepturi de acces asupra acelor elemente și anume: public, private și protected.
      • public: toate elementele de sub această etichetă pot fi accesate în interiorul clase cât și în afara acesteia.
      • private: elementele de sub această etichetă nu pot fi accesate decât în interiorul clasei.
      • protected: elementele din această categorie pot fi accesate doar în clasele ce derivă din această clasă.
      Deoarece Assembler-ul nu ține cont de numele variabilelor acesta nu va ține cont nici de restricțiile impuse de aceste etichete. 
       
      În principiu pentru a avea acces la un element din clasă trebuie ca cunoaștem practic adresa relativă fată de pointerul this la care se află elementul în memorie. Pentru aceasta avem nevoie să știm dimensiunea clasei. Dimensiunea clasei se calculează fiind suma dimensiunii fiecărui element.

      În cazul de față clasa noastră ar avea dimensiunea de 29 de octeți. De fapt aceasta nu este dimensiunea reală deoarece se produce alinierea datelor, adica poziționarea lor în funcție de tipul cel mai mare de dată, în cazul nostru, int. Deci adevărata dimensiune a clasei este de 32 de octeți.

      Cam așa arată clasa noastră în memorie.

      Deoarece dimensiunea elementului nume este de 25, nefiind un multiplu de 4 (int fiind cel mai mare tip de dată din clasa), s-a produs alinierea datelor la tipul int.

      Încă un lucru trebuie să mai știm ca să putem accesa un element din clasă, și anume că pointerul this se află în registrul ecx. Deci când lucrăm cu o clasă în limbaj de asamblare trebuie să avem grijă ca acest registru să nu fie modificat pentru a nu pierde adresa obiectului.

      Adresa absolută a unui atribut din clasă se construiește din adresa de început a clasei, care se afla în pointerul this sau, în Assembler, în registrul ecx, și poziția de început atributul în memorie. În cazul nostru atributul size se găsește în memorie la adresa ecx+28, deoarece 28 reprezintă poziția de început a atributului size.

      Acum ca avem aceste lucruri lămurite, putem trece să implementăm în limbaj de asamblare metoda din această clasă.

      Ca idee de bază, verificăm dimensiunea șirului care este primit ca parametru, comparăm cu dimensiunea maximă a șirului din clasă. În cazul în care dimensiunea șirului primit ca parametru depășește dimensiunea șirului din clasă ieșim din funcție. Dacă dimensiunea este în regulă trecem mai departe la copierea celor șirurilor.

      Pentru o bună vizualizare asupra codului, comentariile din partea de Assembler vor începe tot cu "//".

      void setNume(char*){
       _asm{
        mov ebx,[ebp+8]
                         // mutăm în ebx valoarea parametrului primit
        xor edi, edi
                         // folosim registrul edi pentru a salva dimensiunea
                         // șirului primit ca argument
      dim_param:
                         // calculăm dimensiunea parametrului primit ca argument
        cmp [ebx+edi],'\0'
        je verificare_dim
                         // cât timp nu am ajuns la sfârșitul șirului incrementăm registrul edi
        inc edi
        jmp dim_param
      verificare_dim:
                         // verificam dacă depășește dimensiunea vectorului din clasă
        cmp edi,24
                         // comparăm cu dimensiunea maximă a șirului din clasă
        jge final
      
        xor esi, esi
      bucla_1:
                         // copiem în vectorul din clasă șirul primit ca argument
                         // totodată ținând cont și de lungimea acestuia
        cmp [ebx+esi], '\0'
        je final_bucla_1
        mov dl, [ebx+esi]
                         // copiem fiecare caracter din șirul primit
                         // ca argument în șirul din clasa
        mov [ecx+esi], dl
        inc esi
        jmp bucla_1
      final_bucla_1:
        mov byte ptr [ecx+esi],'\0'
        mov [ecx+28], esi
                         // punem dimensiunea actuală a șirului în variabila size
        jmp final
      final:
       }
      }

      Mai sunt de explicat câteva lucruri în legătură cu codul de mai sus cum ar fi: folosirea registrului "dl " din instrucțiunea "mov dl, [ebx+esi]" sau de ce se pune "byte ptr" în instrucțiunea "mov byte ptr [ecx+esi],'\0'".

      Pentru prima instrucțiune, folosirea registrului "dl" este necesară deoarece elementele din șir sunt de tip "char", dimensiunea lor fiind de 1 octet, avem nevoie de un registru de 1 octet pentru manipularea valorilor.

      Cât despre a doua instrucțiune, practic îi spunem să pună "\0" într-un singur byte la adresa specificată.

      Sunday, January 19, 2014

      Minimizarea funcțiilor booleene prin metoda diagramelor Karnaugh

      Minimizați cu ajutorul diagramelor Karnaugh funcția logică: Σ(0, 2, 6, 7, 11, 13) + Σ*(3, 8, 9, 14), unde prin Σ* sunt notate combinațiile imposibile.

      Pentru aceste tipuri de exerciții trebuie să știm că o diagramă Karnaugh este un tablou bidimensional cu 2n locații, unde n reprezinta numărul de variabile pe care le are funcția. De asemenea trebuie să mai știm sa generăm codul Gray pentru diagrama noastră. Avem nevoie de codul Gray deoarece in diagramele Karnaugh etichetele nu se scriu în ordinea naturală, ci în ordinea Gray.

      O modalitate simpla de a genera codul Gray este metoda asa numită "oglindire + completare". În tabelul de mai jos aveți un exemplu despre cum se generează codul Gray pentru o funcție cu 3 variabile.
      z y x y x x
      0 0 0 0 0 0
      0 0 1 0 1 1
      0 1 1 1 1
      0 1 0 1 0
      1 1 0
      1 1 1
      1 0 1
      1 0 0
      Rosu - Partea inițială Albastru - Oglidirea Verde - Completarea;


      Acum că avem codul lui Gray trebuie să deteminăm câte variabile are funcția noastră. În cazul în care funcția este dată sub forma Σ-notație atunci trebuie să scriem cel mai mare număr ce apare în Σ-notație în baza 2, iar numărul de cifre pe care îl va avea reprezintă numărul de variabile. Observăm că cel mai mare număr din Σ-notație este 14 care în baza 2 este scris sub forma: 0110, deci funcția noastră este de 4 variabile.

      Desenăm diagrama Karnaugh pentru 4 variabile și introducem valorile care se dau:

      Acum încercăm să cautăm blocuri conţinând numai valori 1 și ajutându-ne de combinațiile imposibile, astfel încât:

      • fiecare valoare 1 să fie inclusă în cel puţin un bloc;
      • blocurile să fie cât mai mari şi mai puţine;
      • un bloc să conţină un număr de locaţii egal cu o putere a lui 2, eventual puterea 0;
      Trebuie reținut că minimizările rezultate nu sunt unice!

      O posibilă variată de minimizare este reprezentată de următoarea diagramă.


      Trebuie doar să scriem funcția rezultată din diagrama Karnaugh de mai sus. Pentru asta trebuie să ținem de următoarele lucruri:

      • în termen apar acele variabile ale căror etichete sunt constante pentru toate locațiile din bloc, adică dacă o variabilă apare etichetată cu 1 și apoi apare etichetată și cu 0 nu se mai scrie;
      • o variabilă apare negată dacă eticheta sa constantă este 0 și nenegată altfel;
      • termenii astfel obținuți (după considerarea tuturor blocurilor) sunt legați prin disjuncție.
      După ce considerăm toate blocurile, funcția noastră va arăta cam așa:

      F = !A*C + !B*!C*!D + A*!B*D + A*!C*D;

      Saturday, January 18, 2014

      Algoritmul Skolem

      Algoritmul Skolem este un algoritm ce servește la restrângerea unei formule la forma normală Skolem. Pentru aceasta formula trebuie să treacă prin mai multe transformări.

      Primul pas în rezolvarea acestor tipuri de exerciții este de a trece formula în forma normală rectificată(FNR). O formulă este în FNR dacă nu conține variabile care sunt atât libere cât și legate și nu are cuantificatori care să lege aceeași variabilă dar pe poziții diferite. O variabilă x este considerată legată dacă apare într-o subformulă G a unei subformule a lui F de forma: H = (∀x)(G) (sau (∃x)(G)). În restul cazurilor apariția considerată este numită liberă.

      Șablonul arhitectural - Singleton

      Singleton reprezintă un model arhitectural care asigură crearea unei singure instanțe a unei clase, totodată oferind și un punct de acces global la aceasta.

      Acest model este folosit destul de des în aplicațiile pe care le folosim zi de zi, cum ar fi Winamp, Skype și altele, care nu iți permit să rulezi acea aplicație de mai multe ori pe același calculator.

      Structura de bază a unei clase ce respectă aceste șablon este următoarea:


      După cum se vede în imaginea de mai sus constructorul este trecut la private: pentru ca să nu fie apelat în afara clasei. Metoda instance() va returna un obiect de tip Singleton nou în cazul în care nu a fost creat încă sau va returna un pointer la instanța deja creată.

      Să vedem cum va arata implementarea unei clase proiectată cu ajutorul șablonului Singleton:

      class Singleton
      {
      public:
           static Singleton* instance(void);
           ~Singleton(void);
      private:
           Singleton(void);
           static Singleton* uniqueInstance;
      }
       
      Acum să vedem cum arată implementarea metodelor acestei clase:

      Singleton* Singleton::uniqueInstance = 0;
      
      Singleton::Singleton(void)
      {
      }
      
      Singleton::~Singleton(void)
      {
      }
      
      Singleton* Singleton::instance(void)
      {
           if(uniqueInstance == 0)
                uniqueInstance = new Singleton();
           return uniqueInstance;
      }
       
      Utilizatorii au acces la obiectele de tip Singleton exclusiv prin metoda instance(void). Membrul uniqueInstance este inițializat cu 0 la pornirea programului. Metoda instance(void) verifică dacă instanța noastă este nulă, atunci se crează o instantă a clasei iar dacă nu este va returna instanța deja creată.

      Friday, January 17, 2014

      Șablon arhitectural - Strategy

      Șablonul arhitectural Strategy este un șablon de tip "behavioral"(comportamental) ce a fost conceput pentru a defini o familie de algoritmi ce sunt încapsulați de către o clasă de tip "interfață". Scopul acestui șablon este de a selecta algoritmul optim pentru necesitatea fiecărui client, în funcție de specificațiile acestuia. De exemplu, avem de sortat un vector de numere. Pentru aceasta putem folosi algoritmi ca: Bubble Sort, Merge Sort sau Quick Sort etc. Șablonul Strategy oferă clientului posibilitatea de a alege singur ce algoritm poate folosi pentru a sorta vectorul sau. Pentru o privire de ansamblu cât mai clară urmăriți imaginea de mai jos.

      După cum se vede în exemplu de mai sus, avem o clasă MyClass care are o metodă ce sortează un vector. Alături, avem o altă clasă de tip interfață ce conține o metodă sort(); care va fi suprascrisă mai târziu de către cele 3 clase ce implementează această interfață. Astfel în proiectul nostru avem o clasă ce conține modalități de sortare separate de obiectul de sortat, evitând astfel duplicarea codului în cazul în care dorim să mai folosim una dintre metodele de sortare în altă situație.

      Acest șablon este folosit deseori în situații ca
      • atunci când putem folosi mai mulți algoritmi pentru o operație (vezi cazul de mai sus).
      • atunci când într-o clasă sunt definite mai multe comportamente (switch statements), Strategy oferă posibilitatea de a restrânge acele instrucțiuni de control prin implementarea sa.
      • atunci când dorim să evităm duplicarea codului.

      Mai jos aveți un exemplu de implementare a acestui șablon (mai precis cum funcționează acest șablon la nivel de cod).


      #include 
      using namespace std;
      
      class SortAlg{
      public:
       SortAlg();
       ~SortAlg();
       virtual void sort();
      };
      
      class MergeSort : public SortAlg{
      public:
       MergeSort();
       ~MergeSort();
       void sort();
      };
      
      void MergeSort::sort(){
       ...
       // se implementeaza functia pentru merge sort;
       ...
      }
      
      class QuickSort : public SortAlg{
      public:
       QuickSort();
       ~QuickSort();
       void sort();
      };
      
      void QuickSort::sort(){
       ...
       // se implementeaza functia pentru quick sort;
       ...
      }
      
      class BubbleSort : public SortAlg{
      public:
       BubbleSort();
       ~BubbleSort();
       void sort();
      };
      
      void BubbleSort::sort(){
       ...
       // se implementeaza functia pentru bubble sort
       ...
      }
      
      class MyClass{
      public:
       MyClass();
       ~MyClass();
       void sortArray(int);
      private:
       SortAlg *sortAlg;
       int *array;
      };
      
      void MyClass::sortArray(int x){
       ...
       // in loc de a avea un switch in functie de care se alegea
       // variata de sortare pentru array-ul nostru,
       // acum avem doar o singura instructiune de folosit
       this->sortAlg->sort();
       ...
      }

      Thursday, January 16, 2014

      Logică - Varianta 3

      O posibilă rezolvare pentru subiectul postat pe Info3BNecenzurat .

      1) a) Teorema de reprezentare a unei funcții booleene ca o FNCP (enunț).

      Fie orice n ∈ N*, orice f ∈ FB şi oricare numedistincte de variabile x1, x2, ... , xn. Atunci f se poate reprezenta în mod unic ca o FNCP peste X = {x1, x2, ... , xn}, sub forma:


      b) Să se arate că în B =legile absorbției și distributivității sunt adevărate.

      Legea absorbției: (x + y) * x = x și (x * y) + x = x;



      Legea distributivității: (x + y) * z = (z * x) + (z * y) și (x * y) + z = (z + x) * (z + y).



      2) a) Definiția constructivă a arborelui atașat formulelor din LP (Arb(f) pentru fiecare f ∈ LP).
      Baza: Fie F = A. Atunci arborele atașat lui F este:

      Pas constructiv: Fie F = (¬F1) iar arborele atașat lui F1 se cunoaște, Arb(F1). Atunci, arborele atașat lui F va fi:

      Fie F = (F1 ∨ F2) și se cunoaște Arb(F1) și Arb(F2). Atunci Arb(F) va fi (pentru F = (F1 ∧ F2) se obține similar):

      b) Fie F = A ∧ ¬(B ∨ C) și structura S dată prin S(A) = 0, S(B) = 1, S(C) = 0. Să se găsească S(F) folosind definiția.

      S(F) = S(A ∧ ¬(B ∨ C)) = S(A) ∧ S(¬(B ∨ C)) = S(A) ∧ ¬(S(B ∨ C)) = S(A) ∧ ¬(S(B) ∨ S(C)) = 0 ∧ ¬(1 ∨ 0) = 0 ∧ 0 = 0.

      3) a) Teorema rezoluției pentru LP (enunț).

      Fie clauzele C1, C2 , R. Spunem că R este rezolventul lui C1, C2, pe scurt, R = ResL(C1, C2), dacă şi numai dacă există un literal L ∈ C1 astfel încât ¬L ∈ C2 şi R = (C1 \ {L}) U (C2 \ {L}).

      b) Să se arate ca formula A este consecință semantică din mulțimea de formule M = {A ∨ B ∨ C, A ∨ ¬B, A ∨ ¬C, ¬A ∨ ¬C} folosind metoda rezoluției.

      Rezolvarea acestui exercițiu presupune scrierea elementelor din M sub următoarea formă adăugând la sfârșit:
      (A ∨ B ∨ C) ∧ (A ∨ ¬B) ∧ (A ∨ ¬C) ∧ (¬A ∨ ¬C) ∧ ¬A.
      Prin rezoluție rezultă următoarea schemă din care rezultă că A este consecință semantică.



      Wednesday, January 15, 2014

      ACSO (ASM) - Varianta 3

      Să se scrie în limbaj de asamblare o funcție definită astfel: 

      void rotateVector(int *v,int n,int pos);

      - parametrul v este adresa primului element al unui vector
      - parametrul n reprezintă numărul de elemente din vector
      - parametrul pos reprezintă numărul de poziții cu care se va roti vectorul 


      Funcția deplasează elementele vectorului cu pos poziții.


      Exemplu:

      Fie vectorul V={10, 5, 20, 8, 30}. După apelul rotateVector(V,5,2), vectorul va arăta în felul următor: {8, 30, 10, 5, 20}.

      Rezolvare:

      void rotateVector(int *, int, int){
       _asm{
        mov edi, [ebp+8]
        mov ecx,0
      loop_1:
        // retinem de fiecare data ultima valoare din vector
        // ca sa putem sa o adaugam pe prima pozitie
        cmp ecx, [ebp+16]
        je final_loop_1
        mov ebx, [ebp+12]
        dec ebx
        mov eax, [edi+ebx*4]
        
      loop_2:
        // scriem valorile vectorului una peste alta de
        // la stanga la dreapta
        cmp ebx, 0
        je final_loop_2
        mov esi, ebx
        dec esi
        mov edx, [edi+esi*4]
        mov [edi+ebx*4],edx
        dec ebx
        jmp loop_2
      final_loop_2:
        mov [edi+0*4], eax
        inc ecx
        jmp loop_1
      final_loop_1:
       }
      }
      

      Tuesday, January 14, 2014

      ACSO (ASM) - Varianta 2

      Să se scrie o funcție f(int n) ce primește ca parametru variabila int n și returnează rezultatul calculului: 

      2 + 2*4 + 2*4*6 + 2*4*6*8 + … +2*4*6*8*…*2n.


      Rezolvare:

      int f(int){
       _asm{
        mov esi, [ebp+8]
        shl esi, 1 // folosim siftare pentru inmultirea cu 2 
        // deoarece este mai rapida decat instructiunea "mul"
        mov eax, 1 // folosim eax pentru produsele din adunare
        mov ecx, 2 // contor
        mov ebx, 0 // suma propriu-zisa
      loop_1:
        cmp ecx, esi
        jg final_loop_1
        mul ecx
        add ebx, eax
        add ecx, 2
        jmp loop_1
      final_loop_1:
        mov eax, ebx
      
       }
      }
      

      Monday, January 13, 2014

      ACSO (ASM) - Varianta 1

      Să se scrie în limbaj de asamblare o funcție definită astfel: 

      int Produs(int *v);

       
      unde v este adresa de început a unui vector, care se termină printr-un element cu valoarea 0.
      Elementele vectorului sunt nenule. 


      Funcția returnează produsul numerelor mai mici decât media numerelor din vector (din care
      nu face parte elementul final, cu valoarea 0).


      Rezolvare:

      int Produs(int *){
       _asm{
        mov ebx, [ebp+8]
        xor ecx, ecx // retinem suma numerelor pentru medie
        xor edi, edi // contor pentru medie
      loop_1:   // calculam media numerelor din vector
        cmp [ebx], 0
        je final_loop_1
        add ecx, [ebx]
        inc edi
        add ebx, 4
        jmp loop_1
      final_loop_1:
        mov eax, ecx
        xor edx, edx
        div edi
        mov esi, eax // punem rezultatul mediei in esi pentru 
         // a avea liber registrul eax pentru produsul numerelor
        mov eax, 1
        mov ebx, [ebp+8]
      loop_2:    // calculam produsul numerelor
          // mai mici decat media
        cmp [ebx], esi
        jge final_loop_2
        mul [ebx]
        add ebx, 4
        jmp loop_2
      final_loop_2:
        // valoarea returnata de functie va fi
        // ceea ce se afla in registrul eax
        // in cazul nostru va fi direct produsul
       }
      }
      

      Sunday, January 12, 2014

      ACSO (teorie) - Varianta 1

      Subiectul a fost preluat de pe site-ul Info3BNecenzurat. Rezolvarea subiectului este originală.


      1. De ce diferă ordinea logică a instrucțiunilor de ordinea lor fizică?

      Datorită structurilor de control (if, while, for, ...), implementate prin salturi.

      2. În codul Grey scris pe 6 poziții, care sunt vecinii șirului de biți 010110?

      010010
      010111

      3. Demonstrați că {XOR, AND} reprezintă o mulțime de generatori pentru funcțiile booleene.

      ¬x = x ⊕1
      x + y = (x ⊕ y) ⊕ (x ⋅ y)

      4. Fie reprezentarea în C2n,m a unui număr N; extinderea sa la C2n+k,m se realizează prin repetarea bitului de semn spre stânga, de k ori. Cum se poate realiza o extindere similară de la C1n,m la C1n+k,m ?

      Extinderea se realizează la fel ca la C2.

      5. Fiind dat un afișaj cu 7 segmente, ca în figura de mai jos, să se implementeze cu ajutorul unui decodor funcția booleană pentru comanda segmentului g. 
       

      Trebuie desenat un decodor ca în figura de mai jos:


      Cele 7 output-uri le punem într-o poartă SAU deoarece doar în acele cazul segmentul g este activ.

      6. Minimizați cu ajutorul diagramelor Karnaugh funcția logică: Σ(0, 2, 6, 7, 11, 13) + Σ*(3, 8, 9, 14), unde prin Σ* sunt notate combinațiile imposibile.


      Tabelul cu minimizarea corespunzătoare este:


      Varianta minimala: !A ⋅ C + !B ⋅ !C ⋅ !D + A ⋅ !B ⋅ D + A ⋅ !C ⋅ D;

      7. Se consideră numerele având următoarele reprezentări în C23,1: r1=1011, r2=1100. Propuneți două reprezentări în C23,1 , notate r3 şi r4, astfel încât r1+r3 şi r1-r4 să producă depăşire, iar r2-r3 şi r2+r4 să nu producă depăşire.

      r1 = -2.5, r2 = -2
      Intervalul este: -4 ... 3.5
      Cele două numere ce satisfac relațiile de mai sus au următoarele reprezentări: 1010 și 0110.

      8. Într-un procesor, componenta cea mai utilizată este unitatea aritmetico-logică (ALU), care este folosită 60% din timp. Dacă se proiectează o ALU cu 50% mai rapidă decât cea existentă, dar astfel prețul procesorului crește tot cu 50%, este sau nu justificat să înlocuim vechiul procesor cu cel nou? De ce?

      Nu este justificată înlocuirea vechiului procesor deoarece performanța noului procesor nu justifică prețul.

      9. În standard IEEE 754, simplă precizie, câte reprezentări diferite corespund valorii speciale NaN (Not a Number)?

      NaN: semnul nu contează, exponentul are valoarea maximă, mantisa diferită de 0. Mantisa are 23 biți, deci
      avem 223-1 reprezentări diferite.

      10. Se consideră numerele N1 şi N2, care următoarele reprezentări în exces-5: r1=0111, r2=1010. Care sunt reprezentările în exces-5 ale numerelor N1+N2 şi N2-N1?

      N1 = 2, N2 = 5

      N1 + N2 = 7 -> 1100
      N2 - N1 = 3 -> 1000

      11. Se poate schimba conținutul PC de două ori în timpul execuției aceleiași instrucțiuni? Dacă da, cum?
      Dacă nu, de ce?


      Da se poate din cauza instrucțiunilor de salt.

      12. Fiind date semnalele de mai jos, ieşirea Dout corespunde unui latch sau unui flip-flop D? Justificare.

      Ieșirea Dout corespunde unui flip-flop deoarece se modifică în funcție de Din doar pe frontul crescător al ceasului.

      13. Care este reprezentarea în standard IEEE 754, simplă precizie, a numărului 412.5625?

      Numărul 412,5625 scris în baza 2 este: 110011100,1001
      Caracteristica: E = 8 ⇒ C = 135 = 10000111
      Mantisa: 10011100100100000000000
      Valoarea finală scrisă în baza 2: 01000011110011100100100000000000
      Valoarea finală scrisă în baza 16: 43CE4800

      Saturday, January 11, 2014

      FII Admitere 2012

      Subiectul I


      Pentru itemul 1, scrieți pe foaia de examen litera corespunzătoare răspunsului corect.

      1. Indicați care dintre expresiile C/C++ de mai jos exprimă corect relația de apartenență x ∈ [1, 7] ∩ [3, 9].

      a) !(x < 3 && x > 7)

      b) !(x < 3 || x > 7)

      c) x >= 3 || x <= 7

      d) x > 3 && x < 7

      Răspuns corect: varianta b).

      2. Se consideră algoritmul alăturat, descris în pseudocod:
       
      citeste a, b
      
       (numere naturale)
      
      daca a < b atunci
      
       interschimba a cu b
      
      c -> 0
      
      cat timp b != 0 atunci
      
       c
      a) Scrieți valoarea returnată de algoritm dacă secvența de intrare este formată din numerele 78 și 770

      a = 78
      b = 770
      a < b atunci a = 770, b = 78
      c = 0
      b != 0 atunci c = 68, a = 78, b = 68
      b != 0 atunci c = 10, a = 68, b = 10
      b != 0 atunci c = 8, a = 10, b = 8
      b != 0 atunci c = 2, a = 8, b = 2
      b != 0 atunci c = 0, a = 2, b = 0
      b = 0
      return a = 2

      Răspuns corect: a = 2.

      b) Care este cea mai mică valoare pe care o poate lua variabila a în intervalul [20, 100] și cea mai mare valoare pe care o poate lua variabila b în același interval, astfel încât rezultatul returnat de algoritm să fie 7.

      După cum observăm algoritmul de mai sus calculează cmmdc-ul celor două numere. Deci pentru variabila a avem cea mai mica valoare din intervalul [20, 100] divizibilă cu 7 și anume 7*3 = 21, iar pentru variabila b avem cea mai mare valoare din acest interval divizibila cu 7 adica 7*7*2 = 98.

      Verificăm cele două valori:

      a = 21
      b = 98
      a < b atunci a = 98, b = 21
      c = 0
      b !=0 atunci c = 14, a = 21, b = 14
      b !=0 atunci c = 7, a = 14, b = 7
      b !=0 atunci c = 0, a = 7, b = 0
      b = 0
      return a = 7.

      Valoarea returnată este 7 deci răspunsul corect este: a = 21, b = 98.

      c) Scrieți în pseudocod un algoritm care să nu folosească operatorii aritmetici % (modulo) și / (împărțire) și care să fie echivalent cu cel dat.
       
      citeste a, b
      
       (numere naturale)
      
      cat timp a != b atunci
      
       daca a > b atunci 
      
        a = a - b
      
       altfel
      
        b = b - a
      
      returneaza a

      d) Scrieți programul C/C++ corespunzător algoritmului de mai sus.
      #include
      
      using namespace std;
      
      int cmmdc(int a, int b)
      {
       int c, aux;
       if(a < b)
       {
        aux = a;
        a = b;
        b = aux;
       }
       c = 0;
       while(b != 0)
       {
        c = a%b;
        a = b;
        b = c;
       }
       return a;
      }
      void main()
      {
       int a, b;
       cout<<"Dati a: ";
       cin>>a;
       cout<<"Dati b: ";
       cin>>b;
       cout<<"Valoarea returnata este: "<<cmmdc(a, b)<<endl;
      }
       

      Subiectul II


      Pentru fiecare dintre itemii 1 și 2, scrieți pe foaia de examen litera corespunzătoare răspunsului corect.

      1. Se consideră graful neorientat complet cu 10 vârfuri. Numărul de muchii ce pot fi eliminate astfel încât graful parțial rezultat să fie arbore este:

      a) 36
      b) 38
      c) 46
      d) 81

      Răspuns corect: varianta a)

      2. Câte frunze are arborele cu rădăcină, cu 8 noduri numerotate de la 1 la 8, al cărui vector de părinți este: (7, 5, 5, 5, 0, 2, 3, 7)?

      a) 1
      b) 2
      c) 3
      d) 4

      Răspuns corect: varianta d)

      Scrieți pe foaia de examen răspunsurile pentru fiecare dintre cerințele următoare:

      3. Se consideră 3 stive: s1, s2 și s3. Prima stivă conține trei elemente, în timp ce celelalte două stive sunt vide. Să se scrie o secvență de instrucțiuni ce utilizează apeluri ale funcțiilor push și pop pentru a muta elementele stivei s1 în stiva s2, folosind stiva s3 ca stivă auxiliară. La final, ordinea elementelor în stiva s2 va fi aceeași cu ordinea inițială a elementelor în stiva s1.
      Funcția push(s, x) inserează elementul în vârful stivei x. Funcția pop(s) returnează elementul din vârful stivei s și îl elimină de pe stivă.


      Rezolvare: push(s3, pop(s1)), push(s3, pop(s1)), push(s3, pop(s1)), push(s2, pop(s3)), push(s2, pop(s3)), push(s2, pop(s3)).

      4. Scrieți un program C/C++ care citește din fișierul standard de intrare (tastatura) un șir de caractere format doar din litere mici ale alfabetului englez și afișează numărul de apariții al fiecărei litere din cuvântul introdus. Datele de intrare se consideră a fi corecte.
       
      Exemplu: dacă la intrare s-a introdus cuvântul abecedare atunci pe ecran se afișează:
      a: 2
      b: 1
      c: 1
      d: 1
      e: 3.

      #include
      #include
      
      void main()
      {
       //cei doi vectori pentru frecvente si pentru sirul citit
       int frecvente[128];
       char sir[256];
       printf("Dati sirul\n");
       //citim sirul
       scanf("%s",sir);
       //initializam vectorul de frecvente cu 0
       for(int i = 0;i < 128; i++)
        frecvente[i] = 0;
       //incrementam frecventa de aparitie a fiecarui caracter din sir
       for(int i = 0;i < strlen(sir); i++)
        frecvente[sir[i]]++;
       //daca frecventa este mai mare ca 0 atunci afisam caracterul si frecventa sa.
       //consideram i ca fiind codul ascii pentru caracterul in curs.
       for(int i = 0;i < 128; i++)
        if(frecvente[i])
         printf("%c: %d\n",i, frecvente[i]);
      }

      5. Scrieți un program C/C++ care citește din fișierul standard (tastatura) de intrare un număr natural n (n >= 2) și construiește o matrice pătratică nxn în care pe fiecare linie și pe fiecare coloană apar toate numerele de la 1 la n.
       
      Exemplu: dacă la intrare s-a introdus n = 4, atunci un posibil rezultat este:
      1 2 3 4
      2 3 4 1
      3 4 1 2
      4 1 2 3

      #include 
      
      void main()
      {
       //declaram matricea, n si variabilele auxiliare
       int a[10][10], n, i, j;
       //incercam sa citim un n >= 2
       do
       {
        printf("Dati n: ");
        scanf("%d",&n);
       }while(n < 2);
       //contruim matricea dupa model
       for(i = 0; i < n; i++)
       {
        for(j = 0; j < n; j++)
        {
         int c;
         c = (i+1+j)%n;
         if(c)
          a[i][j] = c;
         else
          a[i][j] = n;
        }
       }
       //afisam matricea pe ecran
       for(i = 0; i < n; i++)
       {
        for(j = 0; j < n; j++)
        {
         printf("%d ",a[i][j]);
        }
        printf("\n");
       }
      }
       

      Subiectul III


      Pentru itemul 1, scrieți pe foaia de examen litera corespunzătoare răspunsului corect.

      1. În câte dintre permutările elementelor mulțimii {L, I, M, B, A, J}, litera A apare pe poziția a doua?
      a) 720
      b) 360
      c) 180
      d) 120

      Răspuns corect: varianta d).

      Scrieți pe foaia de examen răspunsul pentru fiecare dintre cerințele următoare.

      2. Pentru funcția de mai jos ce valoare va returna apelul F(11)?
       
      int F(int x)
      {
       if(x == 0) return 1;
       if(x%2 == 0) return F(x/2)*F(x/2);
       return 2*F((x-1)/2)*F((x-1)/2);
      }
       
      Observăm ca F este o funcție recursivă. Apelul F(11) va returna valoarea 2048.

      F(11) = 2 * F(5) * F(5) =
      = 2 * 2 * F(2) * F(2) * 2 * F(2) * F(2) =
      = 2 * 2 * F(1) * F(1) * F(1) * F(1) * 2 * F(1) * F(1) * F(1) * F(1) =
      = 2 * 2 * 2 * F(0) * F(0) * 2 * F(0) * F(0) * 2 * F(0) * F(0) * 2 * F(0) * F(0) * 2 * * 2 * F(0) * F(0) * 2 * F(0) * F(0) * 2 * F(0) * F(0) * 2 * F(0) * F(0) =
      = 2 * 2 * 2 * 1 * 1 * 2 * 1 * 1 * 2 * 1 * 1 * 2 * 1 * 1 * 2 * 2 * 1 * 1 * 2 * 1 * 1 * 2 * 1 * 1 * 2 * 1 * 1
      = 2048

      3. Se consideră ecuația liniară a0x0 + a1x1 + .... + anxn = b, unde atât b cât și coeficienții ecuației sunt numere întregi. Fiecare dintre variabilele ecuației poate lua valori întregi doar din intervalul [inf, sup] același pentru toate variabilele (inf < sup, deasemenea numere întregi).

      a) Descrieți în limbaj natural o metodă pentru rezolvarea ecuației în cazul în care n = 2 și parametrii a0, a1, b, inf, sup sunt cunoscuți.

      În cazul în care n = 2 și cunoaștem și restul variabilelor putem aplica soluția naivă, adică pentru fiecare x0 și pentru fiecare x1 se luau valori apoi se verifica ecuația pentru a vedea dacă perechea aleasă reprezintă soluție.

      b) Scrieți în limbajul C/C++ două funcții min și max care primesc ca argumente numărul n, tabloul coeficienților a, valoarea b, marginile inf și sup și un index k, k < n și returnează cea mai mică și cea mai mare sumă posibilă oricare ar fi x din intervalul [inf, sup].

      Pentru a înțelege mai bine ce se vrea de la această problemă funcția min va returna cea mai mică sumă posibilă indiferent de variabilele x1, ..., xn, adică în cazul în care un coeficient este pozitiv suma va fi egală cu acel coeficient înmulțit cu marginea inferioară pentru a obține cea mai mica valoare iar dacă coeficientul este negativ atunci el va trebui înmulțit cu marginea superioară a intervalului pentru a obține o valoare minimă. Pentru funcția max se face la fel doar că se va schimba situația, adică coeficientul negativ va fi înmulțit cu marginea inferioară iar cel pozitiv cu marginea superioară.

      int min(int n, int a[], int b, int inf, int sup, int k)
      {
          int sum_min = 0;
          int i;
       int coeficient;
          for (i = k; i < n; ++i)
       {
              coeficient = a[i];
              if (coeficient > 0) 
        {
                  sum_min += coeficient * inf;
              }
        else 
        {
                  sum_min += coeficient * sup;
              }
          }
          return sum_min;
      }
      int max(int n, int a[], int b, int inf, int sup, int k)
      {
          int sum_max = 0;
          int i;
       int coeficient;
          for (i = k; i < n; ++i)
       {
              coeficient = a[i];
              if (coeficient > 0) 
        {
                  sum_max += coeficient * sup;
              }
        else 
        {
                  sum_max += coeficient * inf;
              }
          }
          return sum_max;
      }

      c) Scrieți în limbajul C/C++ o funcție care primește ca argument numărul n, tabloul coeficienților a, valoarea b, marginile inf și sup și returnează un tablou cu n elemente conținând o soluție a ecuației date sau NULL dacă ecuația nu are nicio soluție. Folosiți funcțiile min si max definite mai sus pentru a eficientiza procesul de căutare a soluțiilor.
       
      #include 
      #include 
      
      int min(int n, int a[], int b, int inf, int sup, int k)
      {
          int sum_min = 0;
          int i;
       int coeficient;
          for (i = k; i < n; ++i)
       {
              coeficient = a[i];
              if (coeficient > 0) 
        {
                  sum_min += coeficient * inf;
              }
        else 
        {
                  sum_min += coeficient * sup;
              }
          }
          return sum_min;
      }
      
      int max(int n, int a[], int b, int inf, int sup, int k)
      {
          int sum_max = 0;
          int i;
       int coeficient;
          for (i = k; i < n; ++i)
       {
              coeficient = a[i];
              if (coeficient > 0) 
        {
                  sum_max += coeficient * sup;
              }
        else 
        {
                  sum_max += coeficient * inf;
              }
          }
          return sum_max;
      }
      
      int* solutie(int n, int a[], int b, int inf, int sup)
      {
          static int* t = NULL;
          if (!t) 
        t = (int*)malloc((size_t)n * sizeof(int));
          static int pos = 0;
          static int sum = 0;
          int i;
          for (i = inf; i <= sup; i++) 
       {
              int sumaCurenta = sum + i * a[pos];
              t[pos] = i;
              if (pos == n - 1) 
        {
                  if (sumaCurenta == b) 
         {
                      return t;
                  }
              } 
        else 
        {
                  if (b >= (sumaCurenta + min(n,a,b,inf,sup,pos + 1)) &&
                      b <= (sumaCurenta + max(n,a,b,inf,sup,pos + 1))) 
         {
                      int sumaAnterioara = sum;
                      sum = sumaCurenta;
                      pos++;
                      int* rez = solutie(n, a, b, inf, sup);
                      if (rez) return rez; 
                      --pos;
                      sum = sumaAnterioara;
                  }
              }
          }
          return NULL;
      }
      
      int main()
      {
          int n, b, inf, sup,a[100];
       printf("Dati n: ");
       scanf("%d",&n);
       printf("Dati marginea inferioara a intervalului: ");
       scanf("%d",&inf);
       printf("Dati marginea superioara a intervalului: ");
       scanf("%d",&sup);
       printf("Dati rezultatul ecuatiei: ");
       scanf("%d",&b);
       for(int i = 0;i < n;i++)
       {
        printf("Coeficientul %d este: ", i);
        scanf("%d",&a[i]);
       }
       int *ret;
       ret = solutie(n, a, b, inf, sup);
          if (ret)
       {
        printf("Rezultatul ecuatiei este:\n");
              for (int i = 0; i < n; i++)
         printf("x%d: %d\n", i, ret[i]);
          } 
       else 
        printf("Nu exista solutie.\n");
          return 0;
      }

      Thursday, January 9, 2014

      PH (ASM) - Varianta 1

      Se dau următoarele clase:
       
      class Contact
      {
      public:
       Contact(){
                   size1=0;
            size2=0;
              };
       ~Contact(){};
       char nume[30];
       int size1;
       char prenume[30];
       int size2;
       void setNume(char *nume);
              void setPrenume(char *prenume);
              char* getNume();
              char* getPrenume();
      };
      
      class Acquaintance : public Contact
      {
      public:
       Acquaintance(){};
       ~Acquaintance(){} ;
       char email[30];
       int size;
       void setEmail(char* email);
              char* getEmail();
      };
      
      class Friend : public Contact
      {
      public:
       Friend(){};
       ~Friend(){};
       int nrTel;
       void setNrTel( int nrTel );
              int getNrTel();
      };
       
      Se cere să se implementeze în limbaj de asamblare metodele void setNume(char*), void setPrenume(char*), char* getNume(), char* getPrenume() din clasa Contact precum și metodele void setEmail(char*), char* getEmail(), void setNrTel(int), int getNrTel() din clasele derivate, Acquaintance și Friend.

       Rezolvare:

      void setNume(char *nume)
       {
       _asm{
        mov ebx,[ebp+8]
        xor eax, eax
        xor edi, edi
      verif:          // verificam daca stringul primit ca parametru
                      // nu depaseste dimensiunea maxima pentru atribut
        cmp [ebx+edi],'\0'
        je final_verfi
        inc edi
        jmp verif
      final_verfi:    // daca se depaseste atunci iesim din functie
        cmp edi,29
        jge final
        xor esi, esi
      for_1:          // copiem fiecare caracter din stringul parametru
                      // in atributul corespunzator
        cmp [ebx+esi], '\0'
        je finish_1
        mov dl, [ebx+esi]
        mov [ecx+esi], dl
        inc esi
        jmp for_1
      finish_1:       // actualizam atributul pentru lungimea sirului 
                      // cu lungimea sirului primit ca parametru 
        mov byte ptr [ecx+esi],'\0'
        mov [ecx+32], esi
        jmp final
      final:
       }
      }
      
      void setPrenume(char *prenume)
      {
       _asm{
        mov ebx,[ebp+8]
        xor eax, eax
        xor edi, edi
      verif:          // verificam daca stringul primit ca parametru
                      // nu depaseste dimensiunea maxima pentru atribut
        cmp [ebx+edi],'\0'
        je final_verfi
        inc edi
        jmp verif
      final_verfi:    // daca se depaseste atunci iesim din functie
        cmp edi,29
        jge final
        xor esi, esi
      for_1:          // copiem fiecare caracter din stringul parametru
                      // in atributul corespunzator
        cmp [ebx+esi], '\0'
        je finish_1
        mov dl, [ebx+esi]
        mov [ecx+36+esi], dl
        inc esi
        jmp for_1
      finish_1:       // actualizam atributul pentru lungimea sirului 
                      // cu lungimea sirului primit ca parametru 
        mov byte ptr [ecx+36+esi],'\0'
        mov [ecx+68], esi
        jmp final
      final:
       }
      }
      
      char* getNume()
      {
       _asm{
                      // returnam primul atribut
        mov eax, ecx
       }
      }
      
      char* getPrenume()
      {
       _asm{
                      // mergem la adresa primului atribut dupa care
                      // ne deplasam la cel de-al treilea adaugand 
                      // dimensiunea primului si celui de-al doilea atribut
        mov eax, ecx
        add eax, 36
       }
      }
      
      void setEmail(char* email)
      {
       _asm{
        mov ebx,[ebp+8]
        xor eax, eax
        xor edi, edi
      verif:
        cmp [ebx+edi],'\0'
        je final_verfi
        inc edi
        jmp verif
      final_verfi:
        cmp edi,29
        jge final
      
        xor esi, esi
      for_1:
        cmp [ebx+esi], '\0'
        je finish_1
        mov dl, [ebx+esi]
        mov [ecx+72+esi], dl
        inc esi
        jmp for_1
      finish_1:
        mov byte ptr [ecx+72+esi],'\0'
        mov [ecx+104], esi
        jmp final
      final:
       }
      }
      char* getEmail()
      {
       _asm{
        mov eax, ecx
        add eax, 72
       }
      }
      
      void setNrTel( int nrTel )
      {
       _asm{
        mov ebx, [ebp+8]
        mov [ecx+72], ebx
       }
      }
      
      int getNrTel ()
      {
       _asm{
        mov eax, ecx
        add eax, 72
       }
      }

      Logică - Varianta 2

      Subiectul a fost preluat de pe site-ul Info3BNecenzurat. Rezolvarea subiectului este originală.

      1. a) Definiția constructivă a noțiunii de term in LP1.

      Baza. X T şi Fo T (variabilele şi constantele sunt termi).

      Pas constructiv. Pentru fiecare n ∈ N*, pentru fiecare f ∈ Fn, pentru fiecare t1, t2, …, tn T, avem f(t1, t2, ……, tn) ∈ T. Concluzionăm această etapă a definiţiei prin a „spune” că At LP1.


      b) Să se găsescă o structură S care să fie model pentru: F = (∀z)(P(f(z), g(z))) ∧ Q(a, b).

      Trebuie să găsim o structură de forma: S = <US, IS>.

      Luăm US = N;

      Ps: N2 → B; ps (x, y) = 1 dacă x < y

      Qs: N2 → B; qs (x, y) = 1 daca x = y

      fs: N → N; fs (x) = x+1

      gs: N → N; gs (x) = x+2

      Se observă că pentru a, b = 1 și oricare ar fi z formula este adevărată tot timpul.
       

      2. a) Teorema de substituție din LP1.

      Fie H ∈ LP1, oarecare. Fie orice F, G ∈ LP1 astfel încît F este o subformulă a lui H şi G este tare echivalentă cu F. Fie H’ formula obţinută din H prin înlocuirea (unei apariţii fixate a) lui F cu G. atunci H este echivalent cu H’.
       

      b) Aduceți următoarea formulă în FNSC: (∃x)(Q(x,y)) → (P(x) → ¬(∃x)(Q(y,x))).

      Mai întâi aducem formula în formă cauzală:

      F = (∃x)(Q(x,y)) → (¬P(x) ∨ (∃x)(Q(y,x))). ⇔

      F = (∀x)¬(Q(x,y)) ∨ ¬P(x) ∨ (∃x)(Q(y,x)).

      Acum trecem formula F în FNR utilizând substituția [x/z], [y/u] și [x/t] pentru clauza a doua și a treia.

      F = (∀x)¬(Q(x,y)) ∨ ¬P(z) ∨ (∃t)(Q(y,t)).

      Trecem formula în FNPR.

      F = (∀x)(∃y)(∃z)(∃t)(¬(Q(x,y)) ∨ ¬P(z) ∨ Q(y,t)).

      Aplicăm substituțiile următoare: [y/f(x)], [z/g(x)] și [t/h(x)] și formula noatră devine:

      F = (∀x)(¬(Q(x,f(x))) ∨ ¬P(g(x)) ∨ Q(f(x),h(x))), formulă ce se afla în FNSC.


      3. a) Definiția noțiunii de teoremă într-un sistem deductiv.

      Fie SD = <A, R> un sistem deductiv în FORM. Mulţimea teoremelor lui SD este mulţimea metaformulelor care admit demonstraţii în SD, adică:

      Th(SD) = {F ∈ FORM | există o demonstraţie D pentru F în SD }.


      b) Demonstați, folosind rezoluția pură, că următoarea formulă este nesatisfiabilă:

      F = (∀x)( ∀y)(P(x,x) ∧ (¬P(x,g(y)) ∨ Q(y)) ∧ ¬Q(f(a))).

      Despărțim formula F în clauze și aplicăm algoritmul rezoluției și avem:


      Am ajuns la clauza vidă deci formula F este nesatisfiabilă.

      Logică - Varianta 1

      Subiectul a fost preluat de pe site-ul Info3BNecenzurat. Rezolvarea subiectului este originală.

      1. a) Definiția noțiunii de algebră booleană

      Se numește algebră booleană un 4-uplu P, P = <M, ⊥, ∇, ~>, format din orice mulțime nevida M (suportul algebrei) două operații binare ⊥, ∇ : M x M -> M și o operație unară ~ : M -> M, care satisfac condițiile:

      1. x ⊥ y = y ⊥ x comutativitatea (a lui ⊥)

      2. (x ⊥ y) ⊥ z = x ⊥ (y ⊥ z) asociativitatea (a lui ⊥)

      3. x ⊥ (y ∇ z) = (x ⊥ y) ∇ (x ⊥ z) distributivitatea ( a lui ⊥ fața de ∇)

      4. (x ⊥ y) ∇ y = y absorbție

      5. (x ⊥ (~x)) ∇ y = y legea contradicției

      6. x ∇ y = y ∇ x comutativitatea (a lui ∇)

      7. (x ∇ y) ∇ z = x ∇ (y ∇ z) asociativitatea (a lui ∇)

      8. x ∇ (y ⊥ z) = (x ∇ y) ⊥ (x ∇ z) distributivitatea ( a lui ∇ față de ⊥)

      9. (x ∇ y) ⊥ y = y absorbție

      10. (x ∇ (~x)) ⊥ y = y legea tautologiei

      b) Să se exemplifice teorema de descompunere pentru funcțiile boolene pentru n = 3 și k = 2.

      Teorema de descompunere pentru cazul în care n = 3 și k = 2 este următoarea:

      Unde x1, x2, x3 sunt variabilele funcției f, α1, α2 sunt valorile lui x1 respectiv x2

      Se stie că x1 = x și x0 = ¬x.

      2. a) Algoritmul lui Horn (enunț).

        Pasul 1. i := 0.

        Pasul 2.

              Cât_timp ((există în F o clauză C de forma A1 ∧ A2 ∧ A3 …… ∧ Ak → B, cu A1, A2 , A3 , ... , Ak marcaţi şi B nemarcat sau de forma A1 ∧ A2 ∧ A3 …… ∧ Ak0, cu A1, A2, A3, ... , Ak marcaţi) şi (i = 0))

              execută

                  Pasul 3. Alege un asemenea C ca mai sus.

                  Pasul 4. Dacă ( C = A1 ∧ A2 ∧ A3 …… ∧ Ak → B )

                  atunci

                      Pasul 5. Marchează B peste tot în F.

                  altfel

                      Pasul 6. i := 1.

                  Sf_Dacă

              Sf_Cât_timp

        Pasul 7. Dacă ( i = 0 )

        atunci

              Pasul 8. Scrie „DA”.

              Pasul 9. Scrie S, cu S(A) = 1 dacă ş i numai dacă A apare în F şi este marcată.

        altfel

              Pasul 10. Scrie „NU”.

        Sf_Dacă.

      b) Să se arate că următoarea formulă din LP este tautologie folosing metoda rezoluției.

      F = (A ∧ B ∧ C) ∨ (A ∧ ¬B) ∨ (A ∧ ¬C) ∨ (¬A ∧ ¬C) ∨ ¬A

      Pentru a arăta ca o formulă este tautologie este de ajuns să demonstrăm ca negația acesteia este falsă. Formula F după negație arată cam așa:

      F = (¬A ∨ ¬B ∨ ¬C) ∧ (¬A ∨ B) ∧ (¬A ∨ C) ∧ (A ∨ C) ∧ A

      Folosind metoda rezoluției trebuie să gasim clauza vidă pentru ca formula negată să fie falsă. Obervam ca dacă luam prima si a patra cauză apoi rezultatul cu clauza a doua si apoi cu ultima clauză ajungem la clauza vidă.





















      Am ajuns la clauza vidă deci formula inițială este tautologie.

      3. a) Definiția constructivă a sintaxei logicii propoziționale (LP).

      Fie o mulţime numărabilă de variabile propoziţionale (formule elementare , formule atomice pozitive , atomi pozitivi), A = {A1, A2, … }. Fie, de asemenea, C = {¬, ∨, ∧} mulţimea conectorilor/conectivelor logici/logice non (negaţia), sau (disjuncţia), respectiv şi (conjuncţia) şi P = { ( , ) } mulţimea parantezelor (rotunde). Formulele (elementele lui LP) vor fi „cuvinte” (expresii bine formate) peste alfabetul L = A U C U P:

      Baza (formulele elementare sunt formule). A LP .

      Pas constructiv (obţinere formule noi din formule vechi).

      (i) Dacă F ∈ LP atunci (¬F) ∈ LP.

      (ii) Dacă F1, F2 ∈ LP atunci ( F1 ∨ F2 ) ∈ LP.

      (iii) Dacă F1, F2 ∈ LP atunci ( F1 ∧ F2 ) ∈ LP.

      (iv) Dacă F ∈ LP atunci (F) ∈ LP.

      (v) Nimic altceva nu este formulă.

      b) Să se calculeze numărul total de n-FND-uri.

      Vor exista C de 3 la n luate cate k forme normale disjunctive n-are, deci numărul de n-FND-uri va fi egal cu:

      C de 3 la n luate cate 0 + ... + = 23n