Sunteți pe pagina 1din 9

Note de laborator Specializare

Algoritmi si structuri de date 2 Info 1

Laborator 5
Descriere: Divide et Impera

1. Introducere

Divide et impera este o tehnica de elaborare a algoritmilor care consta din:


ƒ Descompunerea repetata a problemei (subproblemei) ce trebuie rezolvata ın subprobleme de
aceeaşi natură, dar de dimensiuni mai mici.
ƒ Rezolvarea ın acelasi mod (recursiv) a tuturor subproblemelor.
ƒ Compunerea subsolutiilor pentru a obtine solutia problemei (subproblemei) initiale.

Subproblemele obţinute pot fi de două tipuri: elementare şi ne-elementare. Subproblemele


elementare se rezolvă direct, având soluţie imediată şi un grad de complexitate scăzut.
Subproblemele ne-elementare se descompun în continuare, în alte subprobleme elementare şi ne-
elementare. Procesul de descompunere continuă până când s-au obţinut numai subprobleme
elementare.

Caracteristici:
• Nu toate problemele pot fi rezolvate prin folosirea acestei tehnici. Numărul problemelor
care pot fi rezolvate folosind tehnica Divide Et Impera este relativ mic, deoarece
descompunerea unei probleme (subprobleme) în subprobleme de aceeaşi natură este greu de
realizat
• Algoritmii Divide Et Impera se implementează de obicei într-o metodă recursivă, aceasta
fiind apelată recursiv pentru subproblemele create prin descompunere.

Algoritm:

O implementare generală a unui algoritm de tip Divide et Impera poate fi dată sub forma unei
metode recursive.

public void rezolva(problema P){


if (P e divizibil in subprobleme){
divide pe P in subprobleme P1,...,Pk
rezolva(P1);
...
rezolva(PK);
combina solutiile partiale intr-o solutie pentru P
}
else
rezolva pe P direct
}

Metoda primeşte ca argument o problemă P. Există două cazuri :

1. Problema P admite o rezolvare imediată, caz în care se aplică procedura specifică de


rezolvare şi se revine din apel (condiţia de terminare pentru metoda recursivă)

2. Pentru o situaţie diferită de cea de la punctul 1, problema se descompune în două sau mai
multe subprobleme notate P1, P2,… Pk, şi metoda este reapelată pentru fiecare dintre ele. În
final, se combină rezultatele şi se revine din apel.
ASD

2. Exemple de algoritmi Divide et Impera

ƒ determinarea minimului sau maximului dintre elementele unui tablou


ƒ căutarea binară
ƒ sortare Quicksort
ƒ sortare prin interclasare MergeSort
ƒ problema turnurilor din Hanoi
ƒ ridicarea la o putere foarte mare
ƒ dreptunghi de arie maxima in placa cu gauri, etc

2.1 Determinarea minimului într-un tablou de elemente întregi prin Divide et Impera

Programul se bazează pe faptul că min{a[0],.....,a[n]} = min{min{a[1],...,a[j]}, min{a[j+1],..a[n]}}


unde j este mijlocul intervalului [0,n].

import java.io.*;

public class DivImpMin{

private static int[] a;

public int min(int s, int d){


int m1,m2,j;

if (s==d) return a[s];


else {
j = (s + d ) / 2;

m1=min(s,j);
m2=min(j+1,d);

if(m1<m2) return m1;


else return m2;
}
}

public static void main(String []args) throws Exception{


BufferedReader stdin=new BufferedReader(new InputStreamReader(System.in));

System.out.print("Introduceti nr. de elem :");


int nrElem=Integer.parseInt(stdin.readLine());

a=new int[nrElem];

for (int i=0;i<nrElem;i++){


System.out.print("a["+i+"]=");
a[i]=Integer.parseInt(stdin.readLine());
}

DivImpMin divMin = new DivImpMin();

System.out.println("Minimul este : " +


divMin.min(0,nrElem-1));
}
}

2.2 Căutare binară

Căutarea binară se poate aplica numai pentru tablouri ordonate în prealabil. Numită şi căutare
logaritmică sau căutare bisectoare, metoda împarte în mod repetat vectorul în doi vectori de
ASD

lungime egală, sau care diferă prin cel mult un element. Se compară apoi valoarea căutată k cu
valoarea elementului aflat la mijlocul vectorului. Ca rezultat al comparării se indică subvectorul în
care se va repeta procesul. Procesul continuă până când se ajunge la un vector de lungime 0 (dacă
valoarea căutată k nu a fost găsită) sau avem egalitatea a[m] == k unde m este indicele care
desparte cei doi vectori. Valoarea căutată va fi găsită, sau se va constata că lipseşte, după un număr
de cel mult log2n comparaţii, complexitatea algoritmului de căutare binară fiind O(log2n). Varianta
recursiva, specifica algoritmilor de tip Divide et Impera este urmatoarea

public int cautaRecursiv(double valoare,int s,int d){

int j=(s+d)/2; //mijlocul domeniului

if (a[j]==valoare) //daca a fost gasita valoarea


return j; //intoarcem indicele elementului

else if (s>d) //valoarea nu a fost gasita


return -1;

else {
if (a[j]<valoare)
return cautaRecursiv(valoare,j+1,d);
else return cautaRecursiv(valoare,s,j-1);
}
}

La acest algoritm de tip Divide Et Impera lipseşte etapa de combinare a soluţiilor subproblemelor,
soluţia finală fiind aleasă dintre soluţiile parţiale.

2.3 Sortare prin partitionare – Quicksort

Se bazeaza pe metoda interschimbarii, ınsa interschimbarea se face pe distante mai mari. Astfel,
avand tabloul a[], se aplica urmatorul algoritm

1. se alege la ıntamplare un element x al tabloului drept element pivot


2. se scaneaza tabloul a[] la stanga lui x pana cand se gaseste un element ai > x
3. se scaneaza tabloul la dreapta lui x pana cand se gaseste un element aj < x
4. se interschimba ai cu aj
5. se repeta pasii 2, 3, 4 pana cand scanarile se vor ıntalni pe undeva la mijlocul tabloului.

In acel moment, tabloul a[] va fi partitionat ın 2 astfel, la stanga lui x se vor gasi elemente mai mici
ca si x, la dreapta, elemente mai mari ca si x. Dupa aceasta, se aplica acelasi proces subsirurilor de
la stanga si de la dreapta lui x, pana cand aceste subsiruri sunt suficient de mici (se reduc la
un singur element). Algoritmul este exemplificat grafic in figura 1
ASD
ASD

class QuickSort{

static int x[]={90,100,20,60,80,110,120,40,10,30,50,70};

public static void main(String[]args){


quickSort(0,x.length-1,x);
for(int i=0;i<x.length;i++)
System.out.print(x[i]+" ");
}//main

static void quickSort(int st,int dr,int a[]){


int i=st;
int j=dr;
int x, temp;

int mijloc = (st+dr)/2;

x=a[mijloc];

do{
while (a[i]<x) i++;
while (a[j]>x) --j;

//interschimbare
if(i<=j){
temp=a[i];
a[i]=a[j];
a[j]=temp;
j--;
i++;
}
}while(i<=j);
ASD

if(st<j) quickSort(st,j,a);
if(i<dr) quickSort(i,dr,a);
}// quickSort
}//class

2.4 Sortare prin interclasare (Merge Sort)

Algoritmul de sortare MergeSort foloseşte strategia clasică a metodei Divide Et Impera. Un vector
cu n elemente este împărţit în doi subvectori cu câte n/2 elemente. Apoi se sortează cei doi vectori
şi soluţia finală se obţine prin interclasarea lor. Etapa de combinare a soluţiilor parţiale de la
metoda Divide Et Impera este reprezentată de interclasare. Sortarea subvectorilor se face tot prin
divizare şi interclasare, până când se ajunge la un vector de dimensiune 1.

public void mergeSortRecursiv(int s, int d, int [] temp){

if( s==d ) // in cazul in care vectorul are un


// singur element, este deja sortat
return;

else{
int mid = (s+d)/2; //mijlocul domeniului

mergeSortRecursiv(s, mid, temp); //sortare parte stanga


mergeSortRecursiv(mid+1, d, temp); //sortate parte dreapta

interclasare(s, mid+1, d, temp); //interclasare

}
}

Metoda de interclasare reuneşte secvenţele ordonate a[inf], ... ,a[mid] şi a[mid+1], ... , a[sup] mai
întăi în vectorul auxiliar temp, şi apoi copiază toate elementele acestuia în vectorul a. Interclasarea
a doi vectori ordonaţi se face prin compararea elementelor succesive din cei doi vectori: cel mai
mic dintre elementele comparate este copiat în vectorul rezultat şi se avansează în vectorul din care
acesta a fost copiat la următorul element. După ce toate elementele apaţinând unuia dintre cei doi
vectori au fost copiate, elementele rămase în celălalt vector sunt transferate în vectorul rezultat.
public void interclasare(int s, int m, int d, int [] temp){

int j = 0; // indicele elementului curent


int limitaStanga = s;
int mijloc = m-1;
int n = d-limitaStanga+1; // numarul de elemente

while(s <= mijloc && m <= d)


if( a[s] < a[m] )
temp[j++] = a[s++];
else
temp[j++] = a[m++];

while(s <= mijloc)


temp[j++] = a[s++];

while(m <= d)
temp[j++] = a[m++];

for(j=0; j<n; j++)


a[limitaStanga+j] = temp[j];
}
ASD

2.5 Placa cu gauri

Se considera o placa dreptunghiulara ın care exista n gauri punctiforme de coordonate cunoscute.


Sa se determine pe aceasta placa un dreptunghi de arie maxima, cu laturile paralele cu laturile
placii, care sa nu contina gauri ın interior ci numai eventual pe laturi. Vom considera placa ıntr-un
sistem cartezian. Consideram o subproblema de forma urmatoare: dreptunghiul determinat de
diagonala (x1, y1) (din stanga-jos) si (x2, y2) din dreapta-sus este analizat pentru a vedea daca ın
interior contine vreo gaura; daca nu contine, este o solutie posibila (se va alege cea de arie
maxima); daca exista o gaura ın interiorul sau, atunci se considera patru subprobleme generate de
cele 4 dreptunghiuri obtinute prin descompunerea pe orizontala si pe verticala de cele doua drepte
paralele cu axele care trec prin punctul care reprezinta gaura.

Pentru figura de mai sus, datele de intrare sunt

0 0 100 50
1
20 10

import java.io.*;
class PlacaGauri{

static int x1,y1,x2,y2,n,x1s,y1s,x2s,y2s,amax;


static int[] x;
static int[] y;

public static void main (String[] args) throws IOException{


int i;
StreamTokenizer st=new StreamTokenizer(new BufferedReader(
new FileReader("dreptunghi.in")));
PrintWriter out=new PrintWriter(new BufferedWriter(
new FileWriter("dreptunghi.out")));

st.nextToken(); x1=(int) st.nval;


st.nextToken(); y1=(int) st.nval;

st.nextToken(); x2=(int) st.nval;


st.nextToken(); y2=(int) st.nval;

st.nextToken(); n=(int) st.nval;

x=new int [n+1];


y=new int [n+1];

for(i=1;i<=n;i++){
st.nextToken(); x[i]=(int) st.nval;
st.nextToken(); y[i]=(int) st.nval;
}

dr(x1,y1,x2,y2);
out.println(amax);
out.println(x1s+" "+y1s+" "+x2s+" "+y2s);
out.close();
}
ASD

static void dr(int x1,int y1,int x2,int y2){


int i;
int s=(x2-x1)*(y2-y1);

if(s<=amax) return;
boolean gasit=false;

for(i=1;i<=n;i++)
if((x1<x[i])&&(x[i]<x2)&&(y1<y[i])&&(y[i]<y2)){
gasit=true;
break;
}

if(gasit){
dr(x1,y1,x[i],y2);
dr(x[i],y1,x2,y2);
dr(x1,y[i],x2,y2);
dr(x1,y1,x2,y[i]);
}
else {
amax=s;
x1s=x1;
y1s=y1;
x2s=x2;
y2s=y2;
}
}
}

2.3 Turnurile din Hanoi

Se dau trei tije verticale A, B si C. Pe tija A se gasesc n discuri de diametre diferite, aranjate ın
ordine descrescatoare a diametrelor de la baza spre varf. Se cere sa se gaseasca o strategie de
mutare a discurilor de pe tija A pe tija C respectand urmatoarele reguli:

ƒ la un moment dat se va muta un singur disc (cel care se afla deasupra celorlalte discuri pe o
tija)
ƒ un disc poate fi asezat doar peste un disc de diametru mai mare decat al sau ori pe o tija
goala.

Impartim problema astfel:


ƒ se muta primele n − 1 discuri de pe tija sursa A pe tija intermediara B
ƒ se muta ultimul disc de pe tija sursa A pe tija destinatie C
ƒ se muta cele n − 1 discuri de pe tija intermediara B pe tija destinatie C
ASD

import java.io.*;

class Hanoi{
static int nrMutare=0;

public static void main(String[] args) throws IOException{

int nrDiscuri;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Introduceti numarul discurilor: ");

nrDiscuri=Integer.parseInt(br.readLine());
solutiaHanoi(nrDiscuri, 'A', 'B', 'C');
}// main()

static void solutiaHanoi(int nrDiscuri,


char tijaSursa,
char tijaIntermediara,
char tijaDestinatie){

if(nrDiscuri==1)
System.out.println(++nrMutare + " Disc 1 " + tijaSursa + " ==> "+
tijaDestinatie);
else{
solutiaHanoi(nrDiscuri-1,tijaSursa,tijaDestinatie,tijaIntermediara);
System.out.println(++nrMutare + " Disk " + nrDiscuri +
" " + tijaSursa + " --> "+ tijaDestinatie);
solutiaHanoi(nrDiscuri-1,tijaIntermediara,tijaSursa,tijaDestinatie);
}
}// solutiaHanoi
}// class

Probleme

1. Realizati un program care citeste dintr-un fisier elementele unui vector, si le ordoneaza folosind
QuickSort. Rezultatul va fi salvat intr-un fisier.

2. Realizati un program care citeste dintr-un fisier elementele unui vector, si le ordoneaza folosind
MergeSort. Rezultatul va fi salvat intr-un fisier

3. Realizati un program care insereaza elementele date de la tastatura in pozitiile corespunzatoare


astfel incat la sfarsitul executiei programului, elementele sa fie ordonate (utilizati metoda cautarii
binare). Programul va salva valorile ordonate intr-un fisier.

4. Se considera un sir de numere naturale x1, x2, ..., xn si o succesiune de decizii d1, d2, ... unde di
∈{S,D} au urmatoarea semnificatie: la pasul i, sirul ramas ın acel moment se ımparte in doua
subsiruri cu numar egal elemente (daca numarul de elemente este impar, elementul situat la mijloc
se elimina) si se elimina jumatatea din partea stanga a sirului daca di = S (daca di = D se elimina
jumatatea din partea drepta). Deciziile se aplica ın ordine, una dupa alta, pana cand ramane un
singur element. Se cere acest element.

Sirul va fi initializat cu numere aleatoare, iar o posibila succesiune de decizii este d={’S’,’D’,’S’,’S’}

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