11. príklady


Posledná zmena: 29.10.2002

Banner Text rekurzia

rekurzívne číselné funkcie

  • všetky tieto rekurzívne funkcie by sa dali zapísať aj bez rekurzie, napr. pomocou cyklov - našou úlohou je nacvičiť sa riešiť tieto úlohy rekurzívne, hoci mnohokrát sú to veľmi neefektívne riešenia

k-ta mocnina čísla n

triviálny prípad: n0 = 1

inak:  nk = n*nk-1

function NnaK(n,k:integer):integer;
begin
  if k=0 then Result:=1
  else Result:=n*NnaK(n,k-1);
end;

fibonacciho postupnosť

triviálny prípad:  f(0) = f(1) = 1

inak:  f(n) = f(n-1) + f(n-2)

úlohu riešte takýmto postupom:
- najprv zapíšte pomocou while-cyklu
- prepíšte while do chvostovej rekurzie (opačným postupom, ako sa chvostová rekurzia prepíše do while-cyklu)

  f1:=1; f2:=1;
  while n>1 do begin
    i:=f1; f1:=f2; f2:=i+f2;
    dec(n);
  end;
  Result:=f2;

function fib(n:integer):integer;
  function fib1(n,f1,f2:integer);
  begin
    if n<=1 then Result:=f2
    else Result:=fib1(n-1,f2,f1+f2);
  end;
begin
  Result:=fib1(n,1,1);
end;

kombinačné číslo

pripomeňme si n nad k:

triviálny prípad:  C(n,0)=C(n,n)=1

inak:  C(n,k)=C(n-1,k-1)+C(n-1,k)

function C(n,k:integer):integer;
begin
  if (k=0) or (k=n) then Result:=1
  else Result:=C(n-1,k-1)+C(n-1,k);
end;

Ďalšie úlohy

    • rekurzívne násobenie dvoch celých čísel pomocou súčtu
    • v rekurzívnom algoritme k-tej mocniny minimalizujte počet násobení, napr. pre výpočet 59 stačia 4 násobenia, lebo x2 sa dá urobiť na 1 násobenie a teda 59 = 5*((52)2)2
    • podobne sa dá optimalizovať aj súčet
    • rekurzívne (euklidovým algoritmom) vypočítajte NSD - najväčší spoločný deliteľ

rekurzia a znakové reťazce

  • s reťazcami sa veľmi zriedka pracuje pomocou rekurzie - väčšinou v praxi sa používajú nerekurzívne algoritmy (pomocou cyklov)

palindorm

rekurzívna funkcia zistí, či je reťazec palindrom

triviálny prípad: prázdny alebo jednoznakový reťazec

inak: prvý znak sa rovná poslednému a zvyšok je tiež palindrom

function palindrom(s:string):boolean;
begin
  if Length(s)<=1 then Result:=true
  else Result:=(s[1]=s[Length(s)]) and
               palindrom(copy(s,2,Length(s)-2));
end;

prevrátenie reťazca

rekurzívna funkcia prevráti reťazec

triviálny prípad: prázdny alebo jednoznakový reťazec

inak: prevrátenie reťazca bez prvého a posledného znaku a k tomu sa pridá posledný (na začiatok) a prvý znak (na koniec)

function prevrat(s:string):string;
begin
  if Length(s)<=1 then Result:=s
  else Result:=s[Length(s)]+
               prevrat(copy(s,2,Length(s)-2))+s[1];
end;

prevrátenie poradia riadkov v súbore

triviálny prípad: súbor je prázdny

inak: prečítame 1. riadok, prevrátime zvyšok súboru (rekurzívne), zapíšeme zapamätaný prvý riadok (na koniec)

premyslite, čo by sa stalo, keby lokálna premenná s nebola lokálnou v prevrat1, ale o úroveň vyššie - bola by lokálnou v prevratSubor

procedure prevratSubor(meno:string);
var
  t1,t2:TextFile;

  procedure prevrat1;
  var
    s:string;
  begin
    if eof(t1) then // nič
    else begin
      readln(t1,s);
      prevrat1;
      writeln(t2,s);
    end;
  end;

begin
  AssignFile(t1,meno); Reset(t1);
  AssignFile(t2,'x'+meno); Rewrite(t2);
  prevrat1;
  CloseFile(t1); CloseFile(t2);
end;

prevrátenie obsahu súboru 1.verzia

v predchádzajúcom príklade budeme meniť len vnorenú procedúru prevrat1

1. verzia riešenia je veľmi podobná prevracaniu poradia riadkov - len sa číta a zapisuje po jednom znaku - problém vzniká s dvojicou znakov #13#10

procedure prevrat1;
var
  z:char;
begin
  if eof(t1) then // nič
  else begin
    read(t1,z);
    prevrat1;
    if z=#13 then writeln(t1)
    else if z<>#10 then write(t2,z);
  end;
end;

prevrátenie obsahu súboru 2.verzia

2. verzia využíva reťazcovú funkciu prevrat, ktorá prevracia znakový reťazec

procedure prevrat1;
var
  s:string;
begin
  if eof(t1) then // nič
  else begin
    readln(t1,s);
    prevrat1;
    writeln(t2,prevrat(s));
  end;
end;

Ďalšie úlohy

    • napíšte rekurzívnu funkciu, ktorá vygeneruje znakový reťazec 2n hviezdičiek, napr. hv(3)='********';
    • napíšte rekurzívnu funkciu, ktorá pre dané n vráti znakový reťazec zložený z toľkých hviezdičiek, ako je n-té fibonacciho číslo - vo funkcii nepoužívajte iné číslené premenné okrem formálneho parametra n

rekurzívne krivky

  • všetky krivky kreslíme pomocou korytnačky, ktorá je zadefinovaná v globálnej premennej k a je vytvorená niekde v strede plochy
  • pri riešení úloh sa snažte v útvare úrovne n nájsť útvar úrovne n-1 a tiež postup, ako to využijete

trojuholníky

triviálny prípad: pre n=0 je nič

procedure trojuhol1(n:integer; s:real);
var
  i:integer;
begin
  if n=0 then   // nič
  else
    for i:=1 to 3 do begin
      trojuhol1(n-1,s/2);
      k.Dopredu(s);
      k.Vlavo(120);
    end;
end;

vpísané trojuholníky

triviálny prípad: pre n=0 je nič

procedure trojuhol2(n:integer; s:real);
begin
  if n=0 then   // nič

  else begin
    k.Dopredu(s/2);
    k.Vlavo(60);
    trojuhol2(n-1,s/2);
    k.Vpravo(60);
    k.Dopredu(s/2);
    k.Vlavo(120);
    k.Dopredu(s);
    k.Vlavo(120);
    k.Dopredu(s);
    k.Vlavo(120);
  end;
end;

trojuholníková pyramída

 

uvedomte si, že tento obrázok vznikol jedným ťahom

ako by ste mohli využiť tento algoritmus na nakreslenie rekurzívneho obrázka trojuhol1 jedným ťahom?

procedure trojuhol3(n:integer; s:real);
var
  i:integer;
begin
  if n=0 then k.Dopredu(s)
  else begin
    k.Dopredu(s/2);
    k.Vlavo(120);
    for i:=1 to 3 do begin
      trojuhol3(n-1,s/2);
      k.Vpravo(120);
    end;
    k.Vpravo(120);
    k.Dopredu(s/2);
  end;
end;

trojuholníková pyramída

rovnaký útvar trochu iným postupom

procedure trojuhol3(n:integer; s:real);
begin
  if n=0 then k.Dopredu(s)
  else begin
    trojuhol3a(n-1,s/2);
    k.Vlavo(120);
    k.Dopredu(s/2);
    k.Vpravo(120);
    trojuhol3(n-1,s/2);
    k.Vpravo(120);
    k.Dopredu(s/2);
    k.Vlavo(120);
    trojuhol3a(n-1,s/2);
  end;
end;

snehová vločka

triviálny prípad: pre n=0 je nič

procedure vlocka(n:integer; s:real);
var
  i:integer;
begin
  if n=0 then   // nič

  else
    for i:=1 to 6 do begin
      k.Dopredu(s);
      vlocka(n-1,s/3);
      k.Dopredu(-s);
      k.Vpravo(60);
    end;
end;

Ďalšie úlohy

    • pokúste sa niektorú z týchto úloh vyriešiť bez korytnačky - len štandardnými príkazmi grafickej plochy (MoveTo a LineTo)
    • nájdite postup pre

    • nasledujúci rekurzívny obrázok vznikol rovnakým postupom, len namiesto zatočenia vľavo, resp. vpravo o 90 stupňov, sa najprv otočí o 45, potom urobí malý krok a potom opäť o 45:


© 2002 AB, KVI
blaho@fmph.uniba.sk