PR2ET11: Pinu ja rekursioon

Labori materjal

Laboriülesanded

Selles laboris on kaks baasülesannet, üks iseseisev edasijõudnute ülesanne rekursioonile ning üks edasijõudnute laiendusülesanne pinule.

Ülesanne 1: Rekursiooni ülesannete komplekt

Esimese ülesande raames tuleb sul luua kolm rekursiivset programmi (osa 1, 2 ja 3). Ülesanne saab esitada ainult siis, kui kõik kolm on valmis!

NB! Tegu on äärmiselt lihtsate ülesannetega, väldi internetist lahenduste otsimist!

Tunni alguses luuakse näidislahendus faktoriaali rekursiivsest lahendamisest!

Nõuded
  • Võid laheduse luua ühe failina kus on kolm rekursiooni korraga või kolme erineva programmina. Näited on tehtud kolme erineva rakendusena.
  • Kõigil juhtudel on kohustuslik toetada sisendina positiivset täisarvu. Negatiivse sisendi korral on kas lahenduses märgitud eraldi vastus (baasjuht) või tuleb kuvada viga.
  • Sisendi võid küsida rakenduse sees või lugeda käsurea argumendina.
  • Iteratiivseid lahendusi kasutada ei tohi, kõik kolm osa peavad olema lahendatud rekursiivselt.
  • Esitada saab ülesannet vaid siis, kui kõik kolm osa on valmis.
Osa 1: summa

Loo rakendus, mis leiab kõigi positiivsete täisarvude summa kuni kasutaja sisestatud arvuni. Negatiivse argumendi korral rekursiivsele summa funktsioonile on summa 0 (positiivsed arvud puuduvad, baasjuht).

Osa 2: astendamine

Loo rakendus, mis lubab astendada xy. math.h teegi ja sisse ehitatud astmefunktsioonide kasutamine ei ole lubatud.

NB! Kuigi nõue on vaid rakendus luua positiivsetele täisarvudele, siis võid proovida ka laiendada lahendust näiteks negatiivsetele arvudele.

Osa 3: Fibonacci arvud.

Loo rakendus, mis leiab soovitud Fibonacci arvu. n-indaks fibonacci arvuks on selle kahe eelneva Fibonacci arvu summa ehk siis fibx = fibx – 1 + fibx – 2. Erinevalt eelnevatest rekursioonidest on Fibonacci jadal kaks seetõttu ka 2 baasjuhtu.

Fibonacci arvud on  0, 1, 1, 2, 3, 5, 8, 13, 21, …,

Märkasid kui kaua aega läks viimase leidmiseks? See on ootuspärane laboriülesande lahendamisel ning parandada vaja ei ole, kuid mõtle kindlasti miks nii juhtus!

Kõik 3 valmis, näita nüüd ülesanne ette!

Programmi ajakulu saad mõõta kasutades time  programmi. Siin on võrdluseks kaks erinevat rekursiivset lahendust samast programmist. Teisel juhul on kasutatud lähenemist mida kutsutakse tail-recursion’iks ehk sabarekursiooniks. Selle kohta võid vajadusel uurida omal käel.

Ülesanne 2: pinu

Selles ülesandes loome menüüprogrammi, mille sisuks on demonstreerida pinu funktsioone. Tunnis lahendatav ülesanne on demonstratiivne ning toetab varasemalt õpitud dünaamilist mälu – selline lahendus ei too oma keerukuse tõttu välja pinu eeliseid kiiruses!

Tunni alguses luuakse osaline näidis, kus lahendame ära Push() funktsiooni!

Nõuded
  • Loo oma rakendusele menüü struktuur – kasutaja peab saama valida millist operatsiooni teostada
  • Loo Pop() ja Peek() funktsioonid
    • Pop() eemaldab pealmise elemendi pinust ja tagastab selle väärtuse.
    • Peek() tagastab pealmise elemendi, kuid ei eemalda seda
  • Loodavad funktsioonid ise elementide väärtusi kuvada ei tohi! Ekraanile kuvatakse väärtused pärast menüüsse tagastamist!
  • NB! Baasversioonis on lubatud reserveerida üks väärtust tähistamaks pinu alatäitumist, et väljastuses illegaalset väärtust ei kuvataks. Edasijõudnute versioonis see lubatud ei ole.
  • Vastavalt funktsioonile veendu, peavad olema realiseeritud kas ala- või ületäitumise kontrollid.
  • Pööra tähelepanu olukorrale kus pinust eemaldatakse kõik elemendid – reallociga 0 baiti küsida on ohtlik, tuleks kasutada free funktsiooni!
Pinu silumisfunktsioon

Selleks, et saaksid paremat aimu pinu sisust pakun sulle ka silumisfunktsiooni. Tegelikus olukorras sellist funktsiooni sageli rakendada ei saaks kuna see võib olla vastuolus pinu piirangute ning ka konkreetse pinu realisatsiooniga!.

Testimine

Testimisel pööra tähelepanu järgnevale:

  • pinu ületäitumine
  • pinu alatäitumine
  • pinu andmete vabastamine pärast viimase elemendi kustutamist (valgrind!)
  • Mälulekke ohule kui programm suletakse kui pinus on veel andmeid (valgrind!)
Ava mind väljundi nägemiseks

Edasijõudnute ülesanne 1: rekursiooni praktiline kasutusjuht

See edasijõudnute ülesanne on sulle antud ingliskeelse algoritmi kirjeldusena. Tegu on täiesti eraldiseisva ülesandega, mis ei ole seotud eelnevalt tehtud tunnitöö lahendusega!

NB! Olulised muutujad on antud matemaatikutele omases kirjapildis ehk üksikute tähtedena – soovitus oma programmis kasuta pikemaid ja selgitavamaid nimesid nagu on programmeerimises omane.

Given search key K  and an array A  of n  integers A0, A1, … , An – 1 sorted such that A0 ≤ A1≤ … ≤ An – 1 use the following algorithm to find if K ∈ A

  1. Set lowIndex L  to   and highIndex H  to n − 1 .
  2. Call the recursive function using 4 arguments – A, L, H, K .
    • If L > H , the search terminates as unsuccessful – return 0
    • Set m  (the position of the middle element) to the floor of (L + H)  /  2
    • If Am < K, set L  to m + 1 , call recursion again
    • If Am > K, set H  to m − 1 , call recursion again
    • If Am = K, the search was successful – return 1

Ülesanne valmis, valmista enne esitamist ette vastused järgmistele küsimustele. Kui vastused olemas, anna märku!

  • Ajalike keerukus – võrdle antud algoritmi ja tavalist lineaarset otsingut. Mitu võrdlust läheks vaja, et võrrelda massiivi kus on
    • 16 liiget
    • 256 liiget
    • 100 000 liiget
  • Anna hinnang algoritmi keerukusele  – nt konstantne, logaritmiline, lineaarne, eksponentsiaalne (https://www.bigocheatsheet.com)
  • Kui massiiv poleks sorteeritud, kas see algoritm töötaks?

Edasijõudnute ülesanne 2: pinu laiendus

Selles ülesandes loome paar lisavõimalust oma pinule ning teeme seda veidi universaalsemaks.

Nõuded
  • Lisa oma pinule järgmised funktsioonid
    • Duplicate()
    • Swap()
  • Need funktsioonid tohivad kasutada ainult Push() ja Pop() funktsioonide väljakutseid!
  • Muuda oma Pop() ja Peek() funktsioonide kujusid sedasi, et kehtiksid järgmised nõuded
    • Pinu peab toetama kõiki arve (ei tohi olla reserveeritud mitmetähenduslike väärtusi).
    • Juhul kui tekib pinu alatäitumine, siis kuvatakse vaid veateade, väärtust ennast ei kuvata
  • Märkus: Lisafunktsioonide jaoks pead suure tõenäousega muutma ka Push() funktsiooni!
Testimine

Olulised kitsaskohad mida testida (lisaks tavaolukordadele)

  • Pop(), kui pinu on tühi (ei tohi numbrit väljastada
  • Duplicate(), kui andmeid pinus pole
  • Duplicate(), kui pinu on täis
  • Swap(), kui pinus andmeid pole
  • Swap(), kui pinus on vaid 1 liige

Pärast seda tundi peaksid

  • Teadma mida tähendab rekursioon, sh rekursiivne funktsioon ja rekursiivne andmetüüp.
  • Oskama kirjutada lihtsaid rekursioone
  • Teadma, et eksisteerib ka sabaga rekursioon
  • Teadma rekursioonide headest omadustest ja probleemidest
  • Teadma rekursioonide kasutusvaldkondi
  • Teadma mida tähendab abstraktne andmestruktuur
  • Teadma mis on pinu ning millised on selle omadused
  • Teadma pinu kasutusvaldkondi
  • Oskama luua pinu ning sellele kohustuslike funktsioone

Täiendav materjal