Rekurzivní funkce
Rekurzivní funkce je pojmenování pro funkce, které volají samy sebe. Je dobré o nich vědět, protože můžou někdy výrazně zjednodušit řešení problému.
Ukázka rekurzivní funkce je
void rekurze() {
rekurze();
}
Kdybychom takovou funkci zavolali, tak se nedočkáme úspěšného konce. Můžeme si to demonstrovat na následujícím programu
#include <stdio.h>
void rekurze() {
rekurze();
}
int main()
{
rekurze();
return 0;
}
Takový program poběží teoreticky nekonečně dlouho. Ve skutečnosti ale programu dojde paměť v počítači a program se ukončí s chybou stack overflow
. Proto je důležité u každé rekurzivní funkce mít podmínkou, kdy program skončí (ukončující podmínku).
Příkladem rekurzivní funkce může být výpočet faktoriálu. Pro zopakování faktoriálu např. zde.
Pro faktoriál platí
- pokud n = 0, pak faktoriál se rovná 1
- jinak platí, že n faktoriál se rovná n krát n - 1 faktoriál (n! = n*(n-1)!)
Pokud se výpočet faktoriálu pokusíme naprogramovat, tak si nejdříve můžeme udělat ukončující podmínku
#include <stdio.h>
int faktorial(int n) {
if (n == 0) {
return 1;
}
return ...;
}
int main()
{
int vysledek = faktorial(5);
return 0;
}
To je náše podmínka, která nám zastaví nekonečnou rekurzi. V každé rekurzivní funkci musí být nějaká podmínka, která výpočet zastaví a již znova nezavolá danou funkci.
Nyní můžeme naprogramovat druhou část funkce. Pokud n není 0, tak se má vrátít n * (n - 1)!
.
To můžeme naprogramovat následovně
#include <stdio.h>
int faktorial(int n) {
if (n == 0) {
return 1;
}
return n * faktorial(n - 1);
}
int main()
{
int vysledek = faktorial(5);
printf("Vysledek %i\n", vysledek);
return 0;
}
Jak výpočet probíhá?
Volání | Návratová hodnota | Výsledek |
---|---|---|
faktorial(5) | return 5 * faktorial(4) | 120 (5 * 24) |
faktorial(4) | return 4 * faktorial(3) | 24 (4 * 6) |
faktorial(3) | return 3 * faktorial(2) | 6 (3 * 2) |
faktorial(2) | return 2 * faktorial(1) | 2 (2 * 1) |
faktorial(1) | return 1 * faktorial(0) | 1 (1 * 1) |
faktorial(0) | return 1 | 1 |
Pokud si to odkrokujeme, tak dostáváme
- Zavolá se funkce
faktorial(5)
- Zkontroluje se podmínka - je 5 rovno 0? Ne, IF se nevyhodnotí
- zavolá se
5 * faktorial(4)
- Zkontroluje se podmínka - je 4 rovno 0? Ne, IF se nevyhodnotí
- zavolá se
4 * faktorial(3)
- Zkontroluje se podmínka - je 3 rovno 0? Ne, IF se nevyhodnotí
- zavolá se
3 * faktorial(2)
- Zkontroluje se podmínka - je 2 rovno 0? Ne, IF se nevyhodnotí
- zavolá se
2 * faktorial(1)
- Zkontroluje se podmínka - je 1 rovno 0? Ne, IF se nevyhodnotí
- zavolá se
1 * faktorial(0)
- Zkontroluje se podmínka - je 0 rovno 0? Ano, IF se vyhodnotí
- Vrátí se hodnota 1 jako výsledek
faktorial(0)
1 * faktorial(0)
se nahradí za1 * 1
- Vrátí se hodnota 1 jako výsledek
faktorial(1)
2 * faktorial(1)
se nahradí za2 * 1
- Vrátí se hodnota 2 jako výsledek
faktorial(2)
3 * faktorial(2)
se nahradí za3 * 2
- Vrátí se hodnota 6 jako výsledek
faktorial(3)
4 * faktorial(3)
se nahradí za4 * 6
- Vrátí se hodnota 24 jako výsledek
faktorial(4)
5 * faktorial(4)
se nahradí za5 * 24
- Vrátí se hodnota 120 jako výsledek
faktorial(5)
- Hodnota 120 se uloží do proměnné
vysledek
ve funkcimain
- Hodnota
vysledek
se vytiskne - Program se ukončí pomocí
return 0
Obecný výpočet rekurzivní funkce probíhá vždy tak, že se nejdříve výpočet zanořuje směrem dolů (volá se rekurzivní funkce dále a dále). Výpočet se zanořuje tak dlouho, než narazí na ukončovací podmínku (v našem případě pro faktoriál n == 0
). Jakmile se narazí na ukončovací podmínku, tak se výpočet začne zase vynořovat (volání rekurzivní funkce se nahrazují za hodnoty).
Na diagramu by to vypadalo následovně
Vztah rekurze a iterace
Obecně se programy s rekurzí dají přepsat na programy bez rekurze se zachováním funkčnosti. Náš program na výpočet faktoriálu by šlo napsat bez použití rekurze následovně
#include <stdio.h>
int faktorial(int n) {
int vysledek = 1;
while (n > 1) {
vysledek = vysledek * n;
n = n - 1;
}
return vysledek;
}
int main()
{
int vysledek = faktorial(5);
printf("Vysledek %i\n", vysledek);
return 0;
}
Některé programy je však mnohem jednodušší napsat pomocí rekurze a bylo by mnohem náročnější je napsat bez rekurze.
Je nutné si dát pozor, že použití rekurze může mít negativní vliv na výkon programu. Velké rekurze si berou hodně zdrojů počítače a pro velké množství zanoření nemusí takový program ani nikdy doběhnout.
Odkazy
Následující kapitola: Čistý kód