Вы находитесь на странице: 1из 13

ROMNIA

MINISTERUL EDUCAIEI, CERCETRII I INOVRII


INSPECTORATUL COLAR AL JUDEULUI SLAJ

GRUP COLAR INDUSTRIAL SRMAG


Ro-457330-Srmag-Slaj, str. Primverii, nr. 15,tel: 0260 655384, 0260 655385
tel/fax: 0260 655026, www.gsis.ro,e-mail:lisarmasag@yahoo.com;

Metoda Divide et impera


I. Descrierea metodei.
Metoda Divide et impera (desparte i stpnete) este o
metod general de elaborare a algoritmilor. Ea const n mprirea
repetat a unei probleme de dimensiune mare n dou sau mai multe
subprobleme de acelai tip, urmat de combinarea soluiilor
subproblemelor rezolvate pentru a obine soluia problemei propuse.
Pentru clarificare s presupunem c se d un vector A
(a , a ,..., a ) i c trebuie efectuat o prelucrare oarecare asupra
elementelor sale. Mai mult, presupunem c pentru orice p, q numere
naturale cu 1 p q n exist m astfel nct p m q i prelucrarea
secvenei a p ,..., aq se poate face prelucrnd secvenele a p ,..., am i
am1 ,..., aq i apoi combinnd rezultatele pentru a obine prelucrarea
dorit a ntregii secvene a p ,..., aq . n continuare vom face o prim
descriere a metodei Divide et impera folosind un algoritm recursiv
ce transcrie cele de mai sus. Procedura se va numi DIV_IMP(p, q, r)
unde:
- p indic nceputul secvenei ce trebuie prelucrat;
- q indic sfritul secvenei ce trebuie prelucrat;
- r indic rezultatul prelucrrii.
Procedura se va apela prin call DIV_IMP(1, n; r).
1

procedure DIV_IMP(p, q; r)
if p q < EPS
then
call PREL(p, q; r)
else
call DIV(p, q; m)

call DIV_IMP(p, m; r1 )
call DIV_IMP(m+1, q; r2 )
call COMB( r1 , r2 ; r )
end if
return
end
n algoritmul anterior sau folosit notaiile:
EPS reprezint lungimea minim a unei secvene a p ,..., aq ,
notat prescurtat (p,q), pentru care prelucrarea se poate face direct,
fr a mai fi necesar mprirea n subprobleme;
Procedura PREL realizeaz prelucrarea secvenelor de acest
tip furniznd rezultatul n r.
Procedura DIV determin subsecvenele (p,m) i (m+1,q) n
care se mparte secvena (p,q) i furnizeaz pe m.
Procedura COMB realizeaz combinarea rezultatelor r1 i r2
ale prelucrrii a dou secvene vecine (p,m) i (m+1,q), obinnd
rezultatul m al prelucrrii secvenei (p,q).
Un exemplu tipic l constituie sortarea prin interclasare.
Trebuie sortat (ordonat cresctor) un vector A (a , a ,..., a ) .
1

Procedura DIV va furniza valoarea

p q
2

. Vom lua EPS=1

deoarece este uor de scris o procedur PREL care s ordoneze un


subir de tipul ai , ai 1 . Procedura COMB va interclasa subirurile
deja sortate a p ,..., a m i a m1 ,..., a q furniznd rezultatul subirului
a p ,..., aq .
II. Algoritm iterativ pentru metoda Divide et impera
n cele ce urmeaz vom prezenta un algoritm nerecursiv pentru
transpunerea metodei Divide et impera, algoritm constituit de
procedura H.
n procedura H, REZP este o stiv n care se pstreaz
rezultatele pariale, iar PQR este o stiv n care se pstreaz limitele
p, q ale secvenelor ce urmeaz a fi prelucrate, precum i numrul
natural a crui semnificaie ca fi precizat mai jos.
1

procedure H(n, EPS)

2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
18
19

stack REZP, PQR


p1; qn; REZP; PQR(n+1, +, 0)
do
while q p > EPS
call DIV(p, q; m)
(m+1, q, 0) PQR; q m
repeat
call PREL(p, q, )
(p, q, r) PQR
for i=1,r:
REZP; call COMB(, , )
repeat
REZP
if PQR = 0 then return
else p1 , q1 , r1 PQR; p1 , q1 , r1 1 PQR
endif
repeat
end
nainte de a demonstra valabilitatea algoritmului, vom explica

modul n care el lucreaz i anume pentru n = 16 i

p q
2

Secvenele deja prelucrate n stiva REZP vor fi reprezentate printr-o


pereche constnd din indicii primului i ultimului element din
secven. Vom lua EPS=1. Prezentm n continuare valoarea ultimei
secvene prelucrate i coninuturile stivelor REZP i PQR imediat
dup execuia instruciunii de pe linia 9.
Metoda Divide et impera se poate reprezenta sub forma unui
arbore binar strict n care rdcina este etichetat cu (1, +),
descendenii si cu (1, N), (N+1, +), iar fiecare vrf corespunztor
secvenei a p ,..., a q prin (p, q).
REZP
PQR

(1,2)
(3,4,0), (5,8,0), (9,16,0), (17,+,0)
(3,4)
(1,2)
(5,8,1), (9,16,0), (17,+,0)
(5,6)
(1,4)
(7,8,0), (9,16,1), (17,+,0)
(7,8)
(5,6), (1,4)
(9,16,2), (17,+,0)

(9,10)

(1,8)
(11,12,0), (13,16,0), (17,+,0)
(11,12)
(9,10), (1,8)
(13,16,1), (17,+,0)
(13,14)
(9,12), (1,8)
(15,16,0), (17,+,0)
(15,16)
(13,14), (9,12), (1,8)
(17,+,0)
Descendenii unui vrf etichetat cu (p, q) sunt etichetai cu (p,
m) i (m+1, q) pentru un anume m. Vrfurile terminale sunt etichetate
cu (p, q) unde p q EPS. Pentru exemplu considerat, arborele
ataat este urmtorul:
(1, +)
(1, 16)

(17, +)
(9, 16)

(1, 8)

(1, 4)

(5, 8)

(1, 2) (3, 4) (5, 6)

(9,12)

(13,16)

(7, 8) (9,10) (11,12) (13,14) (15,16)

Valabilitatea algoritmului rezult din faptul c imediat dup


fiecare execuie a instruciunii de pe linia 9 se ajunge n urmtoarea
situaie (dac stiva PQR conine cel puin dou elemente):
1) suntem ntr-un vrf final care succede n postordine
vrfului final la care s-a ajuns dup precedenta execuie a
instruciunii de pe linia 9.
2) perechile (p, q) din stiva REZP (n ordine de la baz la vrf)
din i din stiva PQR (n ordine de la vrf la baz) corespund unei
partiii
1,..., q1 , p2 ,..., q2 , ...., p s ,..., N , N 1,...
a lui N, cu p q 1 pentru k 1, ..., s 1.
3) stiva REZP conine n ordine (n ordine de la vrf la baz)
fraii stngi ai vrfurilor aflate pe drumul ce leag vrful terminal
de rdcin. n plus perechile (p, q) din REZP coincid cu secvenele
a p ,..., aq deja prelucrate.
k 1

4) tripletele (p, q, r) din stiva PQR (n ordine de la vrf la baz)


conin n (p,q) fraii drepi ai vrfurilor aflate pe drumul ce leag
vrful terminal de rdcin. Fie (p,q) fratele drept al unui vrf
(p,q) de pe acest drum; atunci se memoreaz (p, q, r) unde r este
numrul predecesorilor fr frate drept ai lui (p,q).
III. Exemple de probleme rezolvate cu ajutorul
metodei Divide et Impera
1. Minimul unui vector.
Se citete un vector cu n componente numere ntregi. Se cere s
se tipreasc cel mai mic element al su.
O astfel de problem poate fi rezolvat utiliznd un algoritm
clasic de calcul al minimului unui vector, ns pentru a scurta timpul
de gsire a minimului este recomandat a se folosi metode rapide cum
este i metoda "divide et impera".
program minim;
const eps=3;
type vector=array[1..100] of integer;
var a:vector;
i,j,m,n,p,q,alfa,beta,gama:integer;
procedure citeste;
begin
write('Dati nr. de componente ale vectorului
n=');readln(n);
writeln('Introduceti componentele vectorului:');
for i:=1 to n do
begin
write('a[',i,']=');readln(a[i]);
end;
end;
procedure prel(p,q:integer;var alfa:integer);
begin
if a[p]<a[q] then alfa:=a[p] else alfa:=a[q];
end;
procedure Divide(p,q:integer;var m:integer);
begin
m:=(p+q) div 2;
end;

procedure Combina(beta,gama:integer;var
alfa:integer);
begin
if beta<gama then alfa:=beta else alfa:=gama;
end;
procedure DivEtImp(p,q:integer; var alfa:integer);
begin
if q-p<eps then prel(p,q, alfa)
else
begin
Divide(p,q,m);
DivEtImp(p,m,beta);DivEtImp(m+1,q,gama);
Combina(beta,gama,alfa);
end;
end;
begin
citeste;
p:=1;q:=n;
DivEtImp(p,q,alfa);
writeln(alfa);
readln
end.

2. Cutare binar
Se citesc n numere ntregi sortate cresctor. De asemenea se
citete un numr ntreg nr. Se cere s se decid dac nr. se gsete n
irul celor n numere citite.
Cutarea se efectueaz ntre numerele reinute de componentele
de indice ntre valorile reinute de dou variabile li i ls (iniial li = 1
i ls = n).
Fiind date li i ls procedm astfel:
se calculeaz indicele componentei din mijloc, n cazul n care n
este impar, sau a uneia din cele dou plasate n mijloc, n cazul n
care n este par (k = (li+ls) div 2);
apar trei posibiliti:
valoarea reinut de componenta de indice calculat este egal cu
nr (caz n care cutarea se termin cu succes);

valoarea reinut de componenta de indice calculat este mai


mic dect nr (caz n care numrul va fi cutat ntre
componentele de indice li = k+1 i ls);
valoarea reinut de componenta de indice calculat este mai
mare dect nr (caz n care numrul va fi cutat ntre
componentele de indice li i ls = k -1 ).
Cutarea se termin cnd numrul a fost identificat sau cnd li > ls
(cutare cu succes).
program cautare;
type vector=array[1..100] of integer;
var a:vector;
n,i,li,ls,k,nr:integer;
gasit:boolean;
begin
write('n=');readln(n);
for i:=1 to n do
begin
write('a[',i,']=');
readln(a[i]);
end;
write('nr=');readln(nr);
li:=1; ls:=n; gasit:=false;
repeat
k:=(li+ls) div 2;
if a[k]=nr
then
begin
writeln('gasit pe pozitia ',k);
gasit:=true;
end
else
if a[k]<nr
then li:=k+1
else ls:=k-1
until (li>ls) or gasit;
if li>ls then writeln('negasit');
readln
end.

3. Sortarea prin interclasare.


Algoritmul de sortare prin intercasare constituie un exemplu
reprezentativ pentru folosirea metodei "divide et impera" n
programare. Astfel, dac avem de sortat un vector, atunci l mprim
n dou, sortm la fel - cele dou pri ale vectorului, apoi le
interclasm. Dac i vectorii rezultai dup mprire sunt destul de
mari (mai mult dect un singur element), atunci procedm la
mprirea i a acestui vectori i tot aa.
Astfel, vom scrie o procedur SortInterclas(nceput,
sfarsit: Integer); care va sorta vectorul A ntre poziia
nceput i poziia sfarsit. Procedura va determina poziia din
mijloc i se va autoapela pentru nceput i mijloc, apoi pentru
mijloc+1 i sfrit, dup care vor interclasa cele dou pri ale
vectorului.
program SortarePrinInterclasare;
const max=10;
var a: array[1..max] of integer;i,n: 1..max;
procedure Interclaseaza(start,mijloc,finis:integer);
var b: array[1..max] of integer; i,j,k:integer;
begin
k:=start ; i:=start;j:=mijloc+1;
while (i<=mijloc) and (j<=finis) do
if a[i]<a[j] then begin b[k]:=a[i]; i:=i+1;
k:=k+1 end
else begin b[k]:=a[j]; j:=j+1; k:=k+1 end;
if i<=mijloc then for j:=i to mijloc do begin
b[k]:=a[j];
k:=k+1 end
else for i:=j to finis do begin b[k]:=a[i]; k:=k+1
end;
for i:=start to finis do a[i]:=b[i]
end;
procedure SortInterclas(inceput,sfarsit: Integer);
var centru:Integer;
begin
if inceput < sfarsit then
begin
centru:=(inceput+sfarsit) div 2;
SortInterclas(inceput,centru);

SortInterclas (centru+1, sfarsit );


Interclaseaza (inceput,centru,sfarsit)
end
end;
begin
Write('n='); readln(n);
for i:=1 to n do begin write('a[',i,']=');
readln(a[i]) end;
SortInterclas(1,n); for i:=1 to n do
write(a[i],','); readln
end.

4. Sortarea rapid "quick - sort"


Un alt exemplu l constituie acest algoritm avansat de sortare,
care folosete o procedur Poziioneaz. Aceast procedur se ocup
de o anumit parte din vectorul de sortat A, cuprins ntre indicii start
i finis. Ea poziioneaz componenta de pe poziia start pe o anumit
poziie k, ntre start i finis, poziie pe care respectivul element va
rmne pn la final, astfel nct toate elementele de pe poziii ntre
start i k-1 s fie mai mici sau egale cu A[k], iar toate elementele de
pe poziii ntre k+1 i finis s fie mai mari sau egale cu A[k].
n procedura de sortare, Quick, dup ce s-a realizat
poziionarea, n conformitate cu tehnica "divide et impera", se va
autoapela aceast procedur pentru cele dou pri rmase nesortate,
dinainte i de dup elementul poziionat A[k].
n procedura Poziioneaz se compar, n mod repetat, dou
elemente, cele de pe poziiile i i resectiv j din vector. Iniial i este
start, iar j este finis. La fiecare pas, dac A[i] > A[j] se interschimb
A[i] cu A[j] i se trece fie la mrirea lui i cu o unitate, fie la
micorarea lui j cu o unitate. Astfel, pn cnd i devine egal cu j,
poziiile i i j se apropie, una de alta, odat cu eventualele
interschimbri necesitate de relaia existent ntre elementele de pe
cele dou poziii. Modificrile lui i i j se realizeaz cu ajutorul
variabilei d, care ia pe rnd valorile 0 i 1, iar asupra lui i se execut o
incrementare cu d, iar asupra lui j o decrementare cu d.
program QuickSort;
const max=10;

type vector=array[1..max] of integer; var a: vector;


i,n: integer;
procedure Pozitioneaza(start,finis:integer; var k
:integer;var a:vector);
procedure Schimba (var x,y: integer);
var aux: integer; begin aux:=x; x:=y; y:=aux end;
var i,j,d: integer;
begin
d :=0; i:=start; j:= finis;
while i< j do
begin
if a[i]>a[j] then begin
Schimba(a[i],a[j]); d:=1-d end;
Inc(i,d); Dec(j,1-d)
end;
k:=i
end;
procedure Quick(inceput,sfarsit: integer; var a :
vector);
var k : integer ;
begin
if inceput <sfarsit then
begin
pozitioneaza (inceput, sfarsit,k,a);
Quick(inceput,k-1,a); Quick(k+1,sfarsit,a)
end
end;
begin
writeln('Quick-sort'); write('dati n='); readln(n);
for i:=1 to n do begin write ('a[',i,']=');
readln(a[i]) end;
Quick(1,n,a);
writeln('Vectorul sortat este:');
for i:=1 to n do write(a[i],',');
readln
end.

5. Turnurile din Hanoi


Se spune c n Vietnamul antic, n Hanoi, erau trei turnuri, pe unul
din ele fiind puse, n ordinea descresctoare a diametrelor lor, mai
multe (opt) discuri de aur. Din motive obiective, nite clugri, care
le aveau ngrij, trebuiau s aeze discurile pe cel de-al doilea turn, n

aceeai ordine. Ei puteau s foloseasc, eventual, turnul al treilea,


deoarece, altfel discurile nu ar fi avut stabilitate. Discurile puteau fi
mutate unul cte unul. De asemenea, se renuna din start la ideea de a
aeza un disc mai mare peste unul mai mic, deoarece exista riscul ca
s se strice discurile, din cauza diferenei mari de mas.
Dei, aparent simpl, dup cteva ncercri, cu un prototip n
fa, se va constata c problema nu e banal. ns este posibil o
rezolvare optim a ei (cu numai 2 n 1 mutri, deci 255, pentru n=8),
folosind tehnica recursiv "divide et impera".
Problema este de a muta n discuri de la turnul 1 la turnul 2.
Pentru a o rezolva, s vedem cum se mut, n general, m discuri de la
un turn p la un turn q. Se mut primele m+1 discuri de pe r, r fiind
turnul auxiliar, apoi singurul disc rmas pe p (discul cel mare) se
mut de pe p pe q, dup care cele m-1 discuri sunt mutate de pe r pe
q. Firete, mutarea celor m-1 discuri este corect, deoarece existena
discurilor de diametre mai mari, la bazele celor trei turnuri nu
afecteaz cu nimic mutrile discurilor mai mici.
n cazul limit m=1, avem doar o mutare a discului din vrful
turnului p spre q.
Programul de mai jos soluioneaz (cu animaie) problema
descris, pentru n=8. Procedura de baz este Han, iar celelalte
proceduri sunt pentru micarea discului curent. Exist i dou
proceduri cu structur inedit. Ele sunt scrise n limbaj de asamblare.
Folosind ntreruperea 10h, realizeaz ascunderea, respectiv reafiarea
cursorului text.
program TurnurileDinHanoi;
uses crt;
const pauza=10; forma=#219;
varf:array[1..3] of byte=(13,22,22);
procedure HideCursor; assembler; {ascunde cursorul
palpaitor, in modul text}
asm MOV AX, $0100; MOV CX, $2607; INT $10 end;
procedure ShowCursor; assembler; {reafiseaza
cursorul}
asm MOV AX, $0100; MOV CX, $0506; INT $10 end;
function ColTija(tija:byte):byte;{stabileste coloana
unei tije}
begin ColTija:=24*tija-8 end;
procedure MutaDreapta(disc, tija1, tija2: byte);

var i,k:byte;
begin
for i:=ColTija(tija1)-disc to Pred(ColTija(tija2)disc) do
begin
Delay(Pauza); if KeyPressed then Halt(1);
gotoxy(i,3); for k:=0 to disc do Write(' ');
gotoxy(i+1,3); for k:=0 to 2*disc do
Write(forma);
end
end;
procedure MutaStanga(disc, tija1, tija2 :byte);
var i,k:byte;
begin
for i:=ColTija(tija1)-disc downto
succ(ColTija(tija2)-disc) do
begin
Delay(Pauza); if KeyPressed then Halt(1);
gotoxy(i,3); for k:=0 to disc do Write(' ');
gotoxy(i-1,3); for k:=0 to 2*disc do
Write(forma);
end
end;
procedure Coboara(disc, tija:byte);
var i,k:byte;
begin
for i:=3 to Pred(Varf[tija]-1) do
begin
Delay(Pauza); if KeyPressed then Halt(1);
gotoxy(ColTija(tija)-disc,i);
for k:=0 to disc do Write(' ');
gotoxy(ColTija(tija)-disc,i+1);
for k:=0 to 2*disc do Write(forma);
end;
Dec(Varf[tija])
end;
procedure Ridica(disc, tija:byte);
var i,k:byte;
begin
for i:=Varf[tija] downto 4 do
begin
Delay(Pauza); if KeyPressed then Halt(1);
gotoxy(ColTija(tija)-disc,i);
for k:=0 to disc do Write(' ');

gotoxy(ColTija(tija)-disc,i-1);
for k:=0 to 2*disc do Write(forma);

end;
Inc(Varf[tija])
end;
procedure Muta(disc, tija1, tija2:byte);
begin
Ridica(disc,tija1);
if (tija1<tija2) then
MutaDreapta(disc,tija1,tija2);
Coboara(disc, tija2)
end;
procedure Han(n, tija1, tija2, tija3:byte);
begin
if (n=1) then Muta(1,tija1,tija2)
else
begin
Han(n-1, tija1, tija3, tija2);
Muta(n, tija1,tija2);
Han(n-1, tija3, tija2,tija1)
end
end;
procedure Initializari;
var k,disc:byte;
begin
HideCursor; Clrscr;
for disc:=1 to 9 do
begin
gotoxy(ColTija(1)-disc,varf[1]+disc-1);
for k:=0 to 2*disc do
write(forma);
end
end;
begin
Initializari; gotoxy(28,1); writeln('- Turnurile din
Hanoi -');
Han(8,1,2,3); ShowCursor;
readln
end.