Warning
This page is located in archive.

9 - Rekurze

Cílem tohoto cvičení je osvojit si programování rekurzivních funkcí. Na nich si ukážeme výhody krokování programu v debugovacím režimu.

Výchozí program v Javě

Zdrojové soubory, ze kterých budeme během tohoto cvičení vycházet, si stáhněte z archívu: pr1-lab09.zip . Stejně jako v minulých cvičeních si v Netbeans vytvořte projekt pr1-lab09 a přidejte do něj adresář src se zdrojovými soubory. Jako např. v 7. cvičení i zde je hlavní třída Start, která volá metodu Start() třídy Lab09. Jednotlivé části programu (part1(), part2(),…) lze volit přímým zadáním prvního argumentu nebo pomocí konstanty DEFAULT_PART. Domácí úkol se pak spouští změnou konstanty HOMEWORK ve třídě Start.

Informace k procvičovanému tématu

Rekurze

Rekurzivní funkce je funkce, která volá samu sebe. To by samo o sobě znamenalo nekonečné zacyklení. Proto je potřeba ukončovací podmínka, která včas zastaví další volání funkce. Ukončovací podmínka typicky kontroluje stav parametru, který se mění s každým dalším voláním rekurzivní funkce. Užitečný kód (v příkladu do something) pak může být vykonáván před či po rekurzivním volání funkce v závislosti na účelu dané funkce.

public void funkce(parametr) {
    if (ukončovací podmínka) return;
    else {
        // do something...
        funkce(změněný parametr);
        // do something...
    }
}

For cyklus se také dá zapsat jako rekurzivní funkce. Porovnejte následující dva příklady.

public static void main(String[] args) {
    for (int i = 0; i < 5; i++) {
        // do something...
    }
}

private static void rekurze(int i) {
    if (i<5) {
        // do something...
        i++;
        rekurze(i);
    }
}
 
public static void main(String[] args) {
    rekurze(0);
}

Krokování programu

Pokud si nejste jisti, co vlastně program dělá, nebo máte pocit, že by měl dělat něco jiného, můžete použít ladící (debugovací) režim, kde lze běh programu pozastavit a krokovat řádek po řádku.

Jednou z možností je označit si v kódu ty řádky, na kterých se má program zastavit (breakpointy). To lze udělat jednoduše kliknutím na číslo dané řádky. Objeví se zde červený čtvereček a celý řádek se zvýrazní. Program je pak nutno spustit tlačítkem Debug Main Project (Ctrl+F5) jako na obrázku níže. Při běžném spuštění (zeleným trojúhelníkem) jsou breakpointy ignorovány.

Další možností je kliknout v debug toolbaru (obr. níže) na Step Into (F7) (program se zastaví hned na prvním řádku kódu), nebo Run To Cursor (F4) (program se zastaví až v místě, kde je kurzor - jako by tam byl breakpoint). Pokud debug toolbar nevidíte, lze jej zobrazit přes menu View→Toolbars→Debug.

Pro krokování programu jsou k dispozici intuitivní tlačítka v debug toolbaru. Zelený řádek označuje místo, kde je program zastaven. Fialové řádky jsou místa, kde se program zanořil do dalších funkcí.

Během debugování (krokování) programu máme k dispozici další přehledy. Například Call stack (volání funkcí). Tento přehled je dostupný na dvou místech přes menu Window→Debugging→Call Stack / Debugging. Když se v programu zavolá funkce, přidá se na vrch call stacku. Po skončení funkce z call stacku zmizí. Call stack tedy znázorňuje, jak a odkud (číslo řádku) byly funkce volány. Můžeme se tak dopátrat, kudy se program dostal do stavu, ve kterém jsme ho pozastavili.

Nebo přehled proměnných (dostupné přes Window→Debugging→Variables). Aktuální hodnotu proměnné lze získat i po najetí myší na název dané proměnné (funguje pouze pro lokálně dostupné proměnné).

Průběh cvičení

Část 1

Výpočet faktoriálu je ideální pro názorné použití rekurzivní funkce. Ze známé vlastnosti faktoriálu n!=n*(n-1)! vidíme, že k výpočtu faktoriálu můžeme použít hodnotu jiného faktoriálu. Jelikož víme, že 0!=1, máme i požadovanou ukončovací podmínku.

Na příkladu výpočtu faktoriálu si vyzkoušejte krokování programu. Například najetím myší na danou proměnou se ukáže její hodnota (program musí být zastaven ve stejné funkci). Aktuálně dostupné proměnné a jejich hodnoty lze najít i v okně Variables ve spodní části obrazovky (menu Window→Debugging→Variables).

Volání rekurzivní funkce ale není zadarmo. Každé další vnořené volání funkce zabírá určité místo v paměti (zásobníku). Zásobník není neomezený a může přetéct. Zkuste zvětšit proměnnou LIMIT a docílit přetečení zásobníku.

Část 2

Napište rekurzivní funkci, která vypíše na obrazovku zadaný počet hvězdiček. Lze to napsat i for-cyklem, ale zde použijte rekurzi.

Část 3

Implementujte rekurzivní funkci, která vypíše prvních n členů Fibonacciho posloupnosti. Viz http://cs.wikipedia.org/wiki/Fibonacciho_posloupnost.

Rekurzivní funkce může mít i více ukončovacích podmínek.
Ačkoli rekurzivní zápis algoritmu může vypadat jednodušeji a přímočařeji, nemusí být vždy tou nejlepší volbou. Porovnejte rekurzivní výpočet s iterativním. Pomocí systémových funkcí porovnejte čas běhu dané funkce.

Část 4

Implementujte rekurzivní funkci na testování palindromů. Využijte dekompozici po vzoru faktoriálu. (Nápověda : Co se skrývá uvnitř palindromu?)

Část 5

Osvěžte si znalosti z přednášky a zkuste implementovat rekurzivní algoritmus k řešení hlavolamu Hanojských věží.

Část 6

Implementujte výpočet postupné aproximace zlatého řezu, vyjádřeného pomocí nekonečného rozvoje. Ukončovací podmínka zde bude vyjadřovat požadovanou přesnost výpočtu (počet zanoření).

Výpočet eulerova čísla lze také výjádřit nekonečnou řadou. To lze implementovat dokonce pomocí dvojnásobné rekurze (jedna pro faktoriál, druhá pro postupnou aproximaci).

Část 7

Implementujte převodník čísel, který vypíše na obrazovku zadané číslo z desítkové soustavy převedené do libovolné jiné soustavy (max. je 35 znaková soustava 0-9A-Z).

Část 8

Představte si klasickou hru Hledání min (minesweeper). Jak byste program realizovali? Pro inspiraci můžete zkusit on-line verzi hry http://minesweeperonline.com.

Pro tuto úlohu máte předpřipraveny dvě samostatné třídy v souborech Minesweeper.java a MineField.java.

Třída MineField je hotová a zakrývá (zaobaluje) práci s vnitřní reprezentací minového pole. Třída poskytuje několik public funkcí, pomocí kterých se dá k minovému poli přistupovat zvenčí (např. ze třídy Minesweeper). Všechny funkce mají dvojí rozhraní, kdy se souřadnice v minovém poli dají vyjádřit buď pomocí dvou celých čísel (int x, int y) nebo pomocí jednoho objektu, který tyto souřadnice obsahuje (Point p). Minové pole je čtvercové a má zadanou velikost SIZE x SIZE. Souřadnice políček se pohybují v intervalu <1,SIZE>.

Třída Minesweeper hotová není a obsahuje jenom nástřel možné implementace. Je tu několik metod, které je ještě potřeba implementovat:

  • náhodné rozmístění min - randomizeField
  • chování programu po kliknutí na políčko - clickOn
  • odkrývání políček - reveal
  • vyhledání souřadnic okolních políček - getAdjacentSpots
  • kontrola jestli jsou souřadnice stále uvnitř pole - isInsideField
  • kontrola jestli je hra vyhraná - jsou odkryta všechna políčka, kde nejsou miny - checkWin

Domácí úkol

courses/a0b36pr1/labs/lab09.txt · Last modified: 2015/11/30 09:38 by hrstkon1