Rekurzia
V programovacom
jazyku Pascal je možné vyvolávať procedúry a funkcie rekurzívne. Pod rekurzívnym vyvolaním
rozumieme také vyvolanie, ktoré nastáva skôr, než bolo predchádzajúce vyvolanie
ukončené.
Procedúry
a funkcie, ktoré obsahujú rekurzívne vyvolanie, sa nazývajú rekurzívne. Rozlíšujeme pritom dva
druhy rekurzie, a to
·
rekurzia priama – ktorá nastáva u tej procedúry alebo funkcie, ktorá má vo svojom
tele príkaz vyvolania samej seba (u procedúry) alebo zápis samej seba (u
funkcie). K rekurzívnemu vyvolaniu prichádza tak, že procedúra alebo
funkcia vyvolá samu seba.
·
rekurzia nepriama (vzájomná), ktorá nastáva vtedy, keď dochádza
k vzájomnému vyvolávaniu sa procedúr alebo funkcií. Prvá procedúra vyvolá
druhú, druhá tretiu, atď, (n-1)–vá vyvolá n–tú a n–tá vyvolá prvú.
Príklad: Hanojské veže
Tento
klasický matematický problém môže byť sformulovaný nasledovne:
Máme
k dispozícii tri ihly a určitý počet diskov s rôznym priemerom
a s otvorom uprostred. Na začiatku sú všetky disky napichnuté na
jednej z ihiel tak, že tvoria vežu, smerom nahor sa zúžujúcu . Je potrebné
premiestniť všetky disky na druhú ihlu pomocou tretej ihly, pričom musia byť
dodržané nasledujúce pravidlá:
·
v každom kroku je možné premiestniť len jeden
disk z ihly na ihlu (disky nemôžu byť odložené vedľa ihly),
·
nie je možné položiť väčší disk na menší.
Riešenie:
Na konkrétnom príklade najprv ukážeme postup pri
riešení.
Predpokladajme, že počet diskov je rovný 3.
Pomocou zápisu i –> j vyjadríme, že disk je
presúvaný z ihly i na ihlu
j . Celý postup pri troch diskoch je vyjadriteľný takto:
1
-->2
1
-->3
2
-->3 {Na treťom disku je veža
tvorená 2 diskami}
1
-->2 {Posledný disk sa dostáva na
svoju pozíciu}
3
-->1
3
-->2
1
-->2 {Všetky tri disky sú na druhom
disku a tvoria vežu}
Celý postup by mohol byť popísaný pomocou
príkazov
1. PRENES_DISK z ihly i
na ihlu j
2. PRENES_VEZU
(n, i, j, k)
ktorý interpretujeme takto:
prenes n diskov z ihly i na ihlu j použitím ihly k.
V prípade, že n>0, je možné tento príkaz
vyjadriť pomocou troch jednoduchších príkazov, a to
PRENES_VEZU (n-1, i, k, j) ,
PRENES_DISK (i, j) ,
PRENES_VEZU (n-1, k, j, i) .
V prípade, že n=0, niet čo prenášať.
Uvedený
rozklad predstavuje rekurzívnu definíciu algoritmu riešenia a na jeho
základe je možné vytvoriť rekurzívnu procedúru PRENES_VEZU. Procedúra PRENES_DISK bude vnorená
v procedúre PRENES_VEZU a bude vypisovať príslušný prenos.
program
HANOJ;
var
pocet_diskov: integer;
procedure
PRENES_VEZU (vyska, odkial, kam, pomocou: integer);
procedure}
PRENES_DISK(odk, kam: integer);
begin
writeln(odk:3,'\Longrightarrow ',kam:3)
end; {PRENES_DISK}
begin
{PRENES_VEZU}
if vyska>0 then
begin
PRENES_VEZU(vyska-1, odkial, pomocou,
kam);
PRENES_DISK(odkial, kam);
PRENES_VEZU(vyska-1, pomocou, kam,
odkial)
end
end;
{PRENES_VEZU}
begin
{Hlavný program HANOJ}
writeln('Zadajte, prosim, pocet diskov');
read(pocet_diskov);
PRENES_VEZU(pocet_diskov, 1, 2, 3)
end.
Podobne
ako procedúry je možné vytvárať aj rekurzívne funkcie. Pomocou nich je možné
naprogramovať mnoho matematických funkcií, ktorých definície majú rekurzívny
charakter.
Jednoduchým
príkladom je, napríklad, známa funkcia faktoriál, ktorá je definovaná takto:
Z tejto definície priamo možno napísať
rekurzívnu funkciu
function
FACT (N: integer): integer;
begin
if N<=0
then FACT:=1 else FACT:=
N*FACT(N-1)
end};
{FACT}
Iným typickým príkladom je najväčší spoločný deliteľ dvoch kladných
celých čísel
-- stručne
NSD. Z vlastností NSD vieme, že
platí:
ak
A=B, tak NSD(A,B)=A=B,
ak
A>B, tak NSD(A,B)= NSD(A-B,B),
ak
B>A, tak NSD(A,B)= NSD(A,B-A).
Toto je rekurzívna definícia, ktorá môže byť
zapísaná v tvare rekurzívnej funkcie nasledujúcim spôsobom:
function
NSD(A, B: integer): integer;
{Vypočíta najväčší spoločný deliteľ dvoch celých
kladných čísel}
begin
IF A = B THEN NSD:= A ELSE
IF A>B
THEN NSD:= NSD(A-B,B) ELSE NSD:= NSD(A,B-A)
end; {NSD}
Rekurzívne
definície sú obyčajne veľmi dobré popísané a sú založené na jednoduchých
ideách.
Pri
ich prepisovaní do pascalovskej procedúry alebo funkcie je potrebné dodržiavať
nasledujúce zásady, aby sme sa vyhli chybám:
·
Príkazová
časť rekurzívnej procedúry (funkcie) musí obsahovať vetvu pre triviálny prípad niektorého
základného parametra alebo vstupnej hodnoty. Táto vetva nebude obsahovať
(rekurzívne) volanie procedúry (funkcie), ktorú práve definujeme.
·
Ľubovoľné volanie procedúry (funkcie) vo vnútri
jej príkazovej časti musí mať vhodne redukovaný argument (alebo globálnu
hodnotu). Pri tejto redukcii sa očakáva, že raz dosiahne triviálny prípad po
konečnom počte opakovaní.