Sunteți pe pagina 1din 8

Universitatea Tehnică a Moldovei

Facultatea Calculatoare Informatică și Microelectronică


Specialitatea Tehnologii Informaționale
Departament Inginerie, Software și Automatică

RAPORT
despre lucrarea de laborator nr. 6

la Structuri de Date și Algoritmi

Tema: Analiza algoritmilor ale TEHNICII PROGRAMĂRII

Varianta 18

A îndeplinit: st.gr.TI-163 Radeanu Stefan

A controlat: Victoria Lazu


Chișinău 2017
Scopul lucrării: Însuşirea tehnicilor de algoritmizare, parcurgere, ștergere, inserție în prelucrarea
structurilor de date în C.
Sarcina şi obiectivele:
1. de studiat şi însuşit materialul teoretic pentru evidenţierea esenţialului tehnicilor de programare în
elaborarea modelelor soluţiei problemelor: esenţa metodelor (strategiilor tehnicilor de programare)
şi specificul realizării;

2. să se analizeze şi să se descrie tehnica modelării şi scenariile programării eficiente pentru diverse


compartimente ale diferitor situaţii cu argumentări şi modele de structuri abstracte;

3. să se preia varianta problemei din compartimentul “3.Problemele cu exemple pentru însuşire,


modificare şi rulare”, pentru analiză, aprofundare şi rularea programelor în limbajul C.

4. să se elaboreze scenariile succinte de modificare, incluzând pointeri, subprograme şi fişiere cu teste


de verificare şi vizualizări şi explicaţii la principalele subprograme prin schemele logice.

5. în raport de descris concis esenţa fiecărei metode ( ca strategie sau tehnică de programare).

Importanţa studiului algoritmilor:

În rezolvarea unei probleme cu calculatorul este importanta definirea strategiei de rezolvare a


problemei si definirea optima a algoritmului. Exista cateva metode speciale de elaborare a algoritmilor.
Alegerea uneia sau alteia fiind facuta in concordanta cu specificul problemei de rezolvat si cu eficienta
algoritmului. Fiecare algoritm are un context in care este folosit. Metodele (strategiele, tehnicile de
rezolvare) care vor fi luate in discutie sunt: metoda Backtracking , metoda "Greedy", metoda "divide et
impera", metoda programarii dinamice si metoda Branch and Bound.

Algoritmi:

- Backtracking: (in traducere aproximativa, “cautare cu revenire”) este un principiu fundamental de


elaborare a algoritmilor pentru probleme de optimizare, sau de gasire a unor solutii care indeplinesc anumite
conditii. Algoritmii de tip backtracking se bazeaza pe o tehnica speciala de explorare a grafurilor orientate
implicite. Aceste grafuri sunt de obicei arbori, sau, cel putin, nu contin cicluri.
Metoda backtracking se mai poate utiliza atunci când soluţia problemei se poate reprezenta sub forma
unui vector X = (x1,x2,…,xn) ∈ S = S1xS2x…xSn, unde S este spaţiul tuturor valorilor posibile, iar Si sunt mulţimi
finite ordonate (1 ≤ i ≤ n). Mulţimea soluţiilor rezultat se defineşte astfel:
R = {X∈S| X îndeplineşte condiţiile interne}

- Greedy: (greedy = lacom) sunt în general simpli şi sunt folosiţi în probleme de optimizare în care se poate
obţine optimul global în alegeri succesive ale optimului local, ceea ce permite rezolvarea problemei fãrã nici
o revenire la deciziile anterioare. În acest sens avem:
 o mulţime de candidaţi (lucrari de executat, varfuri ale grafului etc)

 o funcţie care verificã dacã o anumitã mulţime de candidaţi constituie o soluţie posibilã, nu neapãrat
optimã, a problemei
 o funcţie care verificã dacã o mulţime de candidaţi este fezabilã, adicã dacã este posibil sã completãm
aceastã mulţime astfel încât sã obţinem o soluţie posibilã, nu neapãrat optimã, a problemei
 o funcţie de selecţie care indicã la orice moment care este cel mai promiţãtor dintre candidaţii încã
nefolosiţi
 o funcţie obiectiv care dã valoarea unei soluţii; aceasta este funcţia pe care dorim sã o optimizãm
(minim/maxim).

- Divide et Impera: este o metodã ce constã în:


 descompunerea problemei ce trebuie rezolvatã într-un numãr de subprobleme mai mici ale aceleiaşi
probleme
 rezolvarea succesivã şi independentã a fiecãreia dintre subprobleme

 recompunerea subsoluţiilor astfel obţinute pentru a gãsi soluţia problemei iniţiale.

- Programarea dinamica: ca si metoda divide et impera, rezolva problemele combinand solutiile


subproblemelor. Dupa cum am vazut, algoritmii divide et impera partitioneaza problemele in subprobleme
independente, rezolva subproblemele in mod recursiv, iar apoi combina solutiile lor pentru a rezolva
problema initiala. Daca subproblemele contin subsubprobleme comune, in locul metodei divide et impera
este mai avantajos de aplicat tehnica programarii dinamice. Programarea dinamica este folosita de obicei in
probleme de optimizare. In acest context, conform celui de-al treilea principiu fundamental, programarea
dinamica este utilizata pentru a optimiza o problema care satisface principiul optimalitatii: intr-o secventa
optima de decizii sau alegeri, fiecare subsecventa trebuie sa fie de asemenea optima. Cu toate ca pare
evident, acest principiu nu este intotdeauna valabil si aceasta se intampla atunci cand subsecventele nu sunt
independente, adica atunci cand optimizarea unei secvente intra in conflict cu optimizarea celorlalte
subsecvente.

- Branch and Bound: foloseste la rezolvarea problemelor la care domeniul în care se caută soluţia este foarte
mare şi nu se cunoaşte un alt algoritm care să conducă mai rapid la rezultat. Problemele care pot fi abordate
prin această metodă pot fi modelate într-un mod asemănător celui folosit la metoda Backtracking. Se pleacă
de la o configuraţie iniţială şi se reţine şirul de operaţii prin care aceasta este transformată într-o configuraţie
finală dacă aceasta este dată, în alte cazuri se cere configuraţia finală ştiind că trebuie să verifice anumite
condiţii de optim.

REZOLVARE
PROBLEMA 24.

Reţeaua binară: Se dau numerele naturale m şi n. Se consideră o reţea dreptunghiulară având 2m linii şi 2n coloane. Se
cere să se atribuie nodurilor reţelei numere naturale distincte din mulţimea {0,1,...,2m+n-1}, astfel încât pentru orice
două vârfuri vecine (pe orizontală sau pe verticală) reprezentarea lor binară să difere pe o unică poziţie.
R. Folosim metoda backtracking generând cele 2m+n numere, verificând condiţiile ca numărul din poziţia R(k-1) şi R(k-
2n) pentru k corespunzător să difere pe o unică poziţie faţă de R(k), R(k) {0,1,...,2m+n-1}, k=1,2m+n.

Elaborarea programului propriu-zis:


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>

int rows,cols,nums;
int **network;
//Verifică dacă 2 numere se deosebesc doar printr-un bit
bool compareBinary(int a,int b){
int x=a^b;
int count = 0;
while(x){
x&=(x-1);
count++;
if(count>1) return false;
}
if(count==1) return true;
}
//Afisarea retelei binare
void displayNetwork(int **network){
printf("Reteaua binara este:\n");
int i,j;
for(i=0;i<rows;i++){
for(j=0;j<cols;j++){
printf("%d\t",network[i][j]);
}
putchar('\n');
}
}
//Verificare dacă numărul introdus nu se repetă, și dacă pe orizontală sau
//verticală are vecini ce se deosebesc printr-un bit doar
bool check(int **network,int i,int j){
int x,y,k,m;
for(x=0;x<rows;x++){
for(y=0;y<cols;y++){
for(m=0;m<rows;m++){
for(k=0;k<cols;k++){
if(x!=m || y!=k)
if(network[x][y]!=-1 &&
network[x][y]==network[m][k]){
return false;
}
}
}
}
}
//check top
if(i!=0 && network[i-1][j]!=-1)
if(compareBinary(network[i][j],network[i-1][j])==false)
return false;
//check right
if(j<cols-1 && network[i][j+1]!=-1)
if(compareBinary(network[i][j],network[i][j+1])==false)
return false;
//check down
if(i<rows-1 && network[i+1][j]!=-1)
if(compareBinary(network[i][j],network[i+1][j])==false)
return false;
//check left
if(j!=0 && network[i][j-1]!=-1)
if(compareBinary(network[i][j],network[i][j-1])==false)
return false;
return true;
}
//Găsirea numerelor ce respectă condițiile de mai sus și inserarea în rețea
bool findSolution(int **network){
int i,j,k;
for(i=0;i<rows;i++)
for(j=0;j<cols;j++){
if(network[i][j]==-1){
for(k=0;k<=nums;k++){
network[i][j]=k;
if(check(network,i,j)){
if (findSolution(network))
return true;
}
}
}
}
}
int main(int argc, char *argv[]) {
int i,j,m,n;
printf("Introduceti m si n:\n");
scanf("%d %d",&m,&n);
rows=pow(2,m);
cols=pow(2,n);
nums=pow(2,n+m)-1;
network=(int**)malloc(sizeof(int*)*rows);
for(i=0;i<rows;i++){
network[i]=(int*)malloc(sizeof(int)*cols);
for(j=0;j<cols;j++){
network[i][j]=-1;
}
}
if(findSolution(network))
displayNetwork(network);
return 0;
}
Rezultat:

PROBLEMA 32.

Mergesort(sortarea prin interclasare, Divide et Impera): Fie T[1 .. n] un tablou pe care dorim sa-l sortam crescator.
Prin tehnica divide et impera putem proceda astfel: separam tabloul T in doua parti de marimi cat mai apropiate,
sortam aceste parti prin apeluri recursive, apoi interclasam solutiile pentru fiecare parte, fiind atenti sa pastram
ordonarea crescatoare a elementelor.

Elaborarea programului propriu-zis:


#include <stdlib.h>
#include <stdio.h>
#include <time.h>

void merge(int arr[], int l, int m, int r);


void mergeSort(int arr[], int l, int r);
void printArray(int A[], int size);

int main(){
int n,*arr,i;
srand(time(0));
printf("Introduceti numarul de elemente: ");
scanf("%d",&n);
arr=malloc(n*sizeof(int));
for(i=0;i<n;i++)
arr[i]=rand()%50-rand()%50+1;
printArray(arr,n);
mergeSort(arr,0,n-1);
printf("Array-ul sortat: \n");
printArray(arr,n);
return 0;
}
void merge(int arr[], int l, int m, int r){
int i,j,k;
int n1=m-l+1;
int n2=r-m;
int L[n1],R[n2];
for (i=0;i<n1;i++)
L[i]=arr[l+i];
for (j=0;j<n2;j++)
R[j]=arr[m+1+j];

i=0,j=0,k=l;
while(i<n1 && j<n2){
if (L[i]<=R[j]){
arr[k]=L[i];
i++;
}
else{
arr[k]=R[j];
j++;
}
k++;
}
while (i<n1){
arr[k]=L[i];
i++;k++;
}
while (j<n2){
arr[k]=R[j];
j++;k++;
}
}
void mergeSort(int arr[], int l, int r){
if(l<r){
int m=l+(r-l)/2;
mergeSort(arr,l,m);
mergeSort(arr,m+1,r);
merge(arr, l, m, r);
}
}

void printArray(int A[], int size){


int i;
for (i=0;i<size;i++)
printf("%d ",A[i]);
printf("\n");
}

Rezultat:
Concluzie:
În urma efectuării lucrării de laborator nr.6, am însușit un lucru extrem de important, tipuri de algoritmi
și utilizarea lor corespunzătoare. Algoritmii sunt foarte puternici în rezolvarea problemelor complicate, în
optimizare, sau găsirea multitudinilor de soluții. Laboratorul dat conține un set de probleme de o
complexitatea mult mai mare decît cele întîlnite pînă acum, iar asta m-a provocat într-o măsură. În sarcinile
oferite am utilizat algoritmul “backtracking” pentru a găsi o rețea binară, ce ar fi destul complicat de
obținut același rezultat fără utilizarea lui, și metoda “Divide et Impera” pentru a sorta un array prin
algoritmul mergesort, adică divizînd array-ul în părți mai mici și sortîndu-le pe fiecare, apoi combinîndu-le
înapoi.

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