Bakalárska práca Bakalár munka



Hasonló dokumentumok
6. A Pascal nyelv utasításai

Megoldott feladatok. Informatika

Programozási technikák Pál László. Sapientia EMTE, Csíkszereda, 2009/2010

Közismereti informatika 2.zh T-M szakirány

PASzSz. Dr. Kotsis Domokos

7. Strukturált típusok

hatására hátra lép x egységgel a toll

REKURZIÓK SZÁMÍTÓGÉPES PROGRAMOK SEGÍTSÉGÉVEL

Egyszerű programok készítése Kifejezések Bitszintű műveletek Relációs műveletek... 58

Algoritmizálás. Horváth Gyula Szegedi Tudományegyetem Természettudományi és Informatikai Kar

Algoritmusok - pszeudókód... 1

Szeminárium-Rekurziók

Programozás alapjai. 5. előadás

Érdekes informatika feladatok

<... < s n. Írjuk le a megoldási módszert, és adjunk meg egy megjegyzésekkel ellátott Pascal-programot. A bemeneti adatokat helyesnek tekintjük.

REKURZIÓ. Rekurzív: önmagát ismétlő valami (tevékenység, adatszerkezet stb.) Rekurzív függvény: függvény, amely meghívja saját magát.

Egyszerű programozási tételek

Programozás I. Metódusok C#-ban Egyszerű programozási tételek. Sergyán Szabolcs

I. Egydimenziós tömb elemeinek rendezése

Tartalomjegyzék Algoritmusok - pszeudókód

Programozás alapjai C nyelv 5. gyakorlat. Írjunk ki fordítva! Írjunk ki fordítva! (3)

Rendezések. A föltöltés nemcsak az r-re vonatkozik, hanem az s-re is. Ez használható föl a további rendezések

Mesterséges intelligencia 1 előadások

0.1. Mi az a standard be- és kimenet? A két mintafeladat leírása

Átkeléses feladatok 1.) 2.) 3.) 4.)

2.6. Utasítások használata

BASH SCRIPT SHELL JEGYZETEK

Összetevők. Fejlesztés és szabálykönyv: Viktor Kobilke Illusztrációk és grafika: Dennis Lohausen

C# feladatok gyűjteménye

8. Mohó algoritmusok Egy esemény-kiválasztási probléma. Az esemény-kiválasztási probléma optimális részproblémák szerkezete

Bevezetés a C++ programozásba

Algoritmizálás. Horváth Gyula Szegedi Tudományegyetem Természettudományi és Informatikai Kar

1. numere.txt n (1 n 10000) n növekvő kilenc a) Pascal/C++ Például: NUMERE.TXT

60 db Várlap A várlapkák ház. udvar speciális lapkák. osztja az utat. különböző részre az útvonalat) Bástya 2 pontozó mező is van egy téglalapon

Kétszemélyes négyes sor játék

INFORMATIKAI ALAPISMERETEK

INFORMATIKAI ALAPISMERETEK

Erdélyi Magyar TudományEgyetem (EMTE

A játékról. A játék elemei. Előkészítés és a játék elemeinek magyarázata

Algoritmizálás és adatmodellezés tanítása 1. előadás

Neumann János Tehetséggondozó Program Gráfalgoritmusok II.

Gyakorlatok. P (n) = P (n 1) + 2P (n 2) + P (n 3) ha n 4, (utolsó lépésként l, hl, u, hu-t léphetünk).

Brósch Zoltán (Debreceni Egyetem Kossuth Lajos Gyakorló Gimnáziuma) Kombinatorika

22. szakkör (Csoportelméleti alapfogalmak 1.)

A rekurzív algoritmusok tanításáról

Brósch Zoltán (Debreceni Egyetem Kossuth Lajos Gyakorló Gimnáziuma) Gráfelmélet II. Gráfok végigjárása

Számítógépes grafika

end function Az A vektorban elõforduló legnagyobb és legkisebb értékek indexeinek különbségét.. (1.5 pont) Ha üres a vektor, akkor 0-t..

INFORMATIKA javítókulcs 2016

Algoritmizálás + kódolás C++ nyelven és Pascalban

OBJEKTUMORIENTÁLT TERVEZÉS ESETTANULMÁNYOK. 2.1 A feladat

ÍRÁSBELI ÖSSZEADÁS, KIVONÁS. A MŰVELETI SORREND SZÁMÍTÁSOKBAN ÉS SZÖVEGES FELADATOK MEGOLDÁSA SORÁN. 9. modul

Felhasználói kézikönyv

Bevezetés a játékelméletbe Kétszemélyes zérusösszegű mátrixjáték, optimális stratégia

Permutáció n = 3 esetében: Eredmény: permutációk száma: P n = n! romámul: permutări, angolul: permutation

Alkalmazott modul: Programozás

Név:... Kód: LEVÉL INFORMATIKA TEHETSÉGGONDOZÁS 2011

148 feladat ) + ( > ) ( ) =?

BABEŞ BOLYAI TUDOMÁNYEGYETEM MATEMATIKA ÉS INFORMATIKA KAR BBTE Matek-Infó verseny 1. tételsor INFORMATIKA írásbeli. A versenyzők figyelmébe:

Gyakorló feladatok ZH-ra

Programozás alapjai Free Pascal

A Pascal programozási nyelvben minden programnak a következő szerkezete van:

Visszatérítő nyomaték és visszatérítő kar

Dokumentáció az 1. feladatsorhoz (egyszerű, rövidített kivitelben)

GroupWise 5.2 használói jegyzet

A programozás alapjai 1 Rekurzió

Nyitott mondatok Bennfoglalás maradékkal

Carcassonne - A frigyláda

ADATBÁZISKEZELÉS ADATBÁZIS

2. témakör: Számhalmazok

Átrendezések és leszámlálások ÚTMUTATÓ Hegedüs Pál június 30.

INFORMATIKAI ALAPISMERETEK

CAYLUS. A játéktábla. Tartalom. Egyszer volt, hol nem volt. A játék célja. Előkészületek. Nyersanyagok élelmiszer. posztó. arany. Épületek.

Analízis előadás és gyakorlat vázlat

SZÁMOLÁSTECHNIKAI ISMERETEK

1. Előadás Matlab lényeges vonásai,

3. Strukturált programok

I. ALAPALGORITMUSOK. I. Pszeudokódban beolvas n prim igaz minden i 2,gyök(n) végezd el ha n % i = 0 akkor prim hamis

Gráfokkal megoldható hétköznapi problémák

ÍRÁSBELI KIVONÁS. 31. modul. Készítette: KONRÁD ÁGNES

Makroökonómia I. segédanyag február

Legrövidebb utat kereső algoritmusok. BFS (szélességi keresés)

COBRA MUNKAÜGY ÉS BÉR PROGRAMCSOMAG ÉVI

Programozási módszertan. Dinamikus programozás: Nyomtatási feladat A leghosszabb közös részsorozat

MENNYIT ÉR PONTOSAN AZ INGATLANOM?

Scherlein Márta Dr. Hajdu Sándor Köves Gabriella Novák Lászlóné MATEMATIKA 1. A FELMÉRŐ FELADATSOROK ÉRTÉKELÉSE

Fordítóprogramok felépítése, az egyes programok feladata. A következő jelölésmódot használjuk: program(bemenet)(kimenet)

S Z A K D O L GO Z AT

MATEMATIKA JAVÍTÁSI-ÉRTÉKELÉSI ÚTMUTATÓ

2. Milyen értéket határoz meg az alábbi algoritmus, ha A egy vektor?. (2 pont)

Webprogramozás szakkör

Relációs algebra áttekintés és egy táblára vonatkozó lekérdezések

Rekurzió. Tartalomjegyzék. Készítette: Gál Tamás

Budapest, oldal

Matematikai programozás gyakorlatok

II. Halmazok. Relációk. II.1. Rövid halmazelmélet. A halmaz megadása. { } { } { } { }

A pénzről- és a közösségről

Darts: surranó nyilak, gondolkodtató problémák Kombinatorika 6. feladatcsomag

VERSENYKIÍRÁS HÉTPRÓBÁSOK BAJNOKSÁGA 2016 ORSZÁGOS EGYÉNI ÉS CSAPAT DIÁKVERSENY 2015/2016-OS TANÉV

14. Mediánok és rendezett minták

Átírás:

Univerzita J. Selyeho Selye János Egyetem Pedagogická fakulta Tanárképző Kar Katedra informatiky Informatika Tanszék Bakalárska práca Bakalár munka Názov práce: Zbierka úloh z programovania riešiteľné rekurziou a backtrackingom a ich demonštrácia pomocou interaktívnych animačných modelov A dolgozat címe: Rekurzióval és backtrackinggel megoldható programozási feladatok gyűjteménye és azok szemléltetése interaktív animációs modellek segítségével Vedúci práce: PaedDr. Ladislav Végh Témavezető: PaedDr. Ladislav Végh Autor: Méry Dávid Szerző: Méry Dávid Konzultant: PaedDr. Ladislav Végh Študijný program: M-Idb Konzulens: PaedDr. Ladislav Végh Szakpár: M-Idb Komárno 2009 Denné štúdium Nappali képzés

Čestné vyhlásenie Becsületbeli nyilatkozat Čestne vyhlasujem, že som záverečnú prácu vypracoval samostatne na základe svojich vedomostí a s použitím uvedenej literatúry, pod odborným vedením vedúceho práce. Som si vedomý, že plagiáty univerzita neakceptuje, zaväzuje ma na vypracovanie novej témy a v danom akademickom roku stratím možnosť absolvovať príslušnú časť štátnych skúšok. Alulírott Méry Dávid kijelentem, hogy a záródolgozatot önállóan dolgoztam ki, az abban feltüntetett forrásokból merítve, a témavezetőm szakmai irányításával. Tudatában vagyok annak, hogy a plagizált dolgozatot az egyetem visszautasítja, új téma kidolgozására kötelez, és az adott tanévben megfoszt az államvizsga ezen része teljesítésének lehetőségétől. Šamorin 12. 05. 2009 Somorja 2009. 05. 12. 2

ABSTRAKT Méry Dávid, Zbierka úloh z programovania riešiteľné rekurziou a backtrackingom a ich demonštrácia pomocou interaktívnych animačných modelov (Bakalárska práca) - Univerzita J. Selyeho, Pedagogická fakulta, Katedra Informatiky. Vedúci diplomovej práce: PaedDr. Ladislav Végh Stupeň odbornej kvalifikácie: Bc Komárno 2009, 49 s. Cieľom mojej bakalárskej práce bolo celoplošne predstaviť a vysvetliť metódy rekurzívneho a backtrackingového programovania. Obsah práce by som odporúčal predovšetkým stredoškolákom, ale bez ohľadu na vek môže byť zaujímavá aj ďalším užívateľom. Svoj cieľ som realizoval v náučnom programe vo formáte HTML, ktorý sa zaoberá zvlášť s obidvomi metódami. Dôkladne ich charakterizuje, a následne demonštruje ich konkrétne použitie na viacerých príkladoch. Priebeh jednotlivých úloh znázorňuje cez animácie, čo uľahčuje ich jednoduchšie porozumenie. K tomu, aby užívateľ mohol ľubovoľne vyskúšať a upraviť programy, ktoré riešia úlohy, má voľný prístup k zdrojovým kódom. Klúčové slová: metódy programovania, rekurzia, backtracking, náuční program, animácie, zdrojový kód. 3

Tartalomjegyzék BEVEZETÉS...5 1. REKURZIÓ...6 1.1. A rekurzió jellemzése...6 1.2. Feladatok...7 1.2.1. Alapműveletek...7 1.2.2. Faktoriális...10 1.2.3. Fibonacci sorozat...12 1.2.4. Szófordítás...13 1.2.5. Hanoi tornyai...13 1.2.6. Legek...15 1.2.7. QuickSort (gyorsrendezés)...16 1.2.8. Kamatozás...17 1.2.9. Számrendszerek...18 1.2.10. Pascal-háromszög...20 2. BACKTRACKING (VISSZALÉPÉSES KERESÉS)...22 2.1. A backtracking jellemzése...22 2.2. Feladatok...22 2.2.1. Ülésrend...22 2.2.2. Gyermeklánc...24 2.2.3. Térkép...27 2.2.4. Vonatozás...29 2.2.5. Szobafestés...32 2.2.6. Királynők...34 3.2.7. Labirintus...36 2.2.8. Zárójelek...40 2.2.9. Farkas kecske káposzta...41 BEFEJEZÉS...47 FELHASZNÁLT IRODALOM... 48 4

Bevezetés Munkám témájául két, a kezdő és haladó programozók körében nem túl nagy közkedveltségnek örvendő, programozási módszert választottam. Mindenki, aki a programozás tudományával bármely szinten is érintkezik, egyszer egész biztosan összetalálkozik a rekurzió és a backtracking fogalmával. Az emberek többsége idegenkedve tekint ezekre a programozási módszerekre. Nem látják át a belső összefüggéseket, így nem is érthetik meg, miként működnek. Ha lehet, inkább kerülik használatukat. Szakdolgozatommal ezen szeretnék változtatni, ugyanis e két módszer elsajátításával sokkal egyszerűbbé és könnyebbé válhat egyes problémák megoldása. Számos helyzetben időt takaríthatunk meg azzal, hogy nem az alap, megszokott programozási metódusokhoz ragaszkodunk, hanem megpróbáljuk átlátni a mögöttes összefüggéseket és felhasználni azokat. Leggyakrabban a különféle logikai feladatok megoldásában lehetnek nagy segítségünkre. Szeretném mélyrehatóan és nagy részletességgel bemutatni egy rekurzión, illetve backtrackingen alapuló program lefutását. Ezt egyes folyamatok grafikus ábrázolásával még szemléletesebbé tenném, ami fontos szerepet tölthet be a megértésben. Felsorakoztatnám az említett programozási módszerek számos felhasználhatósági formáját, és hogy milyen területeken érdemes felhasználni őket. Melyek a pozitívumaik, és melyek az ezekkel szemben álló negatívumok. Felvetnék számos feladatot, melyekhez csatolnám megoldásukat, és azok teljes mértékben érthető magyarázatát is. Ezek közt lennének természetesen e témában régi klasszikusoknak számítók, de mellettük az újszerű, érdekes problémák felvetése is teret kapna. A felhasználó megtekintheti a teljes forráskódot a részletes magyarázatával együtt, de le is töltheti azt, és így kedvére próbálgathatja, illetve módosíthatja azt. Eszmefuttatásaimat elsősorban a középiskolásoknak ajánlanám, de véleményem szerint életkortól függetlenül bárki találhat benne újdonságot. Legyen az akár abszolút kezdő, vagy egy kicsit tapasztaltabb haladó programozó. 5

1. Rekurzió 1.1. A rekurzió jellemzése Akkor beszélünk rekurzióról, ha egy függvény, vagy eljárás önmagát hívja meg. Ez végbemehet közvetlen vagy közvetett módon. Közvetlen a rekurzió, ha egy a programrész az utasításai közt meghívja saját magát, azaz a-t. Ha viszont, a szegmens meghív egy tőle eltérő b szegmenst, és utána az hívja meg az a-t akkor ez egy közvetett rekurzió. ([1], 284. oldal) Többnyire akkor használatos ez a programozási metódus, ha véges lépésben akarjuk feladatunkat önmagához hasonló kisebb feladatokra visszavezetni. Ezek megoldásához viszont még tőlük is kisebb hasonló feladatok eredményei szükségesek. Ez a felbontási folyamat addig megy végbe, míg el nem jutunk egy olyan egyszerű formájához a feladatnak, mely megoldása magától értetődik és nincs szükség hozzá más eredményre. Ha megvan ez a triviális művelet, elkezd visszafele lépegetni és fokozatosan megoldani a bonyolultabb feladatokat. Ahhoz, hogy a rekurziónk ne ismétlődjön a végtelenségig, vagy legalább amíg a memória meg nem telik és meghal a program, meg kell határoznunk egy megfelelő feltételt. Abban az esetben, ha ez nem teljesül, a rekurzió leáll és a program folytatódik tovább a többi utasítással. A rekurziós művelet folyamán többfajta adat is tárolódik a memóriában (veremben). Egy-egy szegmens hívásakor a memóriában tárolódnak a hozzájuk tartozó visszatérési címek. Ezek határozzák meg, hogy a szegmens lejátszódása után hol folytatódjon a program. Minden egyes szegmens visszatérési címe más-más helyen tárolódik, így nincs keveredés a címek közt. Az egyes szegmensek értékparamétereinek is külön helyeket foglal a gép a memóriában. Minden szegmens a saját értékparamétereinek kialakított memóriatartományról válogathat értékeket. Ennek köszönhetően nem keverednek a szegmensek egyéni értékei. További tárolásra kerülő adatok a lokális változók. ([1], 289. oldal) Mivel az előbb említett adatok közül mindhárom tárolása, feldolgozása és eltávolítása hasonló módon történik, ezért ezeket a gép egyszerre hajtja végre. Az egyes szegmensek meghívásakor a gép akkora memóriaterületet hoz létre hozzájuk, amiben elfér a saját visszatérési címük, értékparamétereik és a lokális változóik is egyaránt. 6

Így minimálisra csökken a keveredések száma. Az ilyen típusú memóriaterületeket szegmensmezőknek hívjuk. Ezek csak addig vannak érvényben, míg a hozzájuk tartozó szegmensek le nem játszódnak. Amint végbement az aktuális szegmens, a szegmensmezője felszabadul és új adatok tárolására lesz alkalmas. ([1], 289. oldal) Rekurzív feladatmegoldó módszert számos helyen felhasználhatjuk. Könnyedén megoldhatunk vele különböző matematikai feladatokat, főként ha sorozatokról van szó. Logikai problémák gyors megfejtésében is nagy segítségünkre lehet. 1.2. Feladatok 1.2.1. Alapműveletek Írjuk le a következő alap matematikai műveleteket rekurzió felhasználásával: a) Természetes számok összeadása b) Természetes számok kivonása c) Természetes számok szorzása d) Természetes számok hatványozása Megoldás: a) Vegyük észre, hogy két szám összeadását felírhatjuk úgy is, mint az egyik tag megnövelése 1-gyel annyiszor, amekkora a másik tag, például: 5+3=(((5+1)+1)+1). Így használhatjuk az inc() parancsot. ([7]) Program osszeadas; uses crt; var i,j: integer; procedure osszeg (a,n: integer); if n>0 then Inc(a); osszeg(a,n-1);end else writeln( i+j= a); {növeli az a-t 1-gyel} 7

BEGIN write( i= );readln(i); {beolvassa a két összeadandót} write( j= );readln(j); osszeg(i,j); readkey(); END. b) A kivonást is hasonló módon oldhatjuk meg, mint az összeadást, csak itt nem növeljük az egyik elemet 1-gyel, hanem csökkentjük azt annyiszor, amekkora a másik tag, például: 5-3 = ((5-1)-1)-1. Itt a dec() parancsot hasznosítjuk. program kivonas; uses crt; var i,j: integer; procedure kivon (a,n: integer); Begin If n>0 then dec(a); {csökkenti az a t 1-gyel} kivon(a,n-1); end else writeln( i-j= a); {i és j a művelet két tagja} BEGIN write( i= );readln(i); write( j= );readln(j); kivon(i,j); readkey(); END. c) Két tag szorzását felírhatjuk úgy is, mint az egyik tag önmagával való többszörös összeadása: a*n = a+a*(n-1). Itt viszont már teljesül az is, hogy a*(n-1) = a+a*(n-2) és így tovább. Ez a felbontás addig folytatódik, míg el nem jutunk az a*0 = 0 triviális, alapművelethez. ([7]) 8

program szorzas; uses crt; var i,j: integer; function szor(a,n: integer): longint; if n>0 then szor:=a+szor(a,n-1); end else szor:=0; BEGIN write( i= );readln(i); write( j= );readln(j); writeln( i*j=, szor(i,j)); readkey(); END. d) A szorzáshoz hasonlóan a hatványozást is felírhatjuk sorozat formájában. Ebben az esetben azonban az egyik elem önmagával történő szorzásáról beszélünk. Tehát a hatványozást felírhatjuk ilyen alakban: a n = a*a (n-1) = a*a*a (n-2) és így tovább. Egészen addig, míg el nem jutunk a bázisfeltételig, ahonnan nem tudunk tovább haladni: a 0 = 1. ([7]) Program hatvanyozas; uses crt; var i,j: integer; function hatv(a,n: integer): longint; if n>0 then hatv:=a*hatv(a,n-1); end else hatv:=1; 9

BEGIN write( i= );readln(i); write( j= );readln(j); writeln( i^j=, hatv(i,j)); readkey(); END. 1.2.2. Faktoriális változatát is. Számoljuk ki az n szám faktoriálisát először rekurzióval, majd írjuk le az iteratív Megoldás: Mivel már maga a faktoriális definiálása is rekurzív módon van megadva, így szinte magától értetődik, hogy megoldása rekurziót igényel. A program megírásához két dolgot kell figyelembe vennünk: a) 0! = 1 => Ez a megállító-, vagy más néven bázisfeltétel. b) n! = n*(n-1)! teljesül minden n re, ha n>0. Ez a faktoriális csökkentő feltétele. (Pl.: n = 3, 3! = 3*2!, 2! = 2*1!, 1! = 1*0!, 0! = 0). A feladat megoldásához véges lépésben el kell jutnunk a bázisfeltételhez. Ott az if feltétel ( n>0 ) már nem teljesül, így kilépünk az aktuális hívásból és folytatjuk az előzőt az else ágon, ahol n=1. Itt már ki tudjuk számolni fakt(1)-et, ami egyenlő 1*fakt(0)-val. Így történik ez fakt(2)-vel és fakt(3)-mal is. ([3], 129. oldal) Ezt a következő rekurzív programmal valósíthatjuk meg: program faktorialis; var i: integer; function fakt(n:integer): integer; if n>0 then fakt=n*fakt(n-1) else fakt=1; 10

BEGIN write( i= );readln(i); writeln( rekurzioval:,i,!=,fakt(i)); readln; END. A rekurzió megértésével és tökéletes elsajátításával sokan esnek abba a hibába, hogy a legtöbb helyen ezt a módszert alkalmazzák. Ez érthető is, mivel a hasonló feladatok többsége sokkal kényelmesebben meghatározható rekurzív módon. Azonban minden rekurziónak van egy iteratív változata is, melyben a rekurziót ciklusokkal helyettesítjük. Egyes esetekben előnyösebb inkább ezt a formáját választani a programnak. Időt spórolhatunk meg vele, főleg ha nagy számokkal dolgozunk. Ez abból adódik, hogy a rekurziónál az egyes részmegoldásokat többször is ki kell számolnia a gépnek. ([1], 300. oldal) Program faktorialis; var i: integer; procedure fakt(m:integer); var f: longint; j:=integer; f:=m; for j:=m-1 downto 1 do f:=f*j; writeln( ciklusokkal:,m,!= f); BEGIN write( i= );readln(i); fakt(i); readln; END. Az egész számok tartományában a változók definiálásánál a longint a legnagyobb típus. Értéke -2147483648-tól 2147483647-ig terjedhet. Mivel a 13! eredménye már meghaladja ezt, sajnos csak 12-ig tudunk számolni faktoriálist. 11

Ebben a tartományban azonban olyan gyorsan megkapjuk az eredményt, hogy századmásodpercekben nem is érzékelhető. Így nincs is időbeli különbség a két módszer közt. 1.2.3. Fibonacci sorozat Határozzuk meg a Fibonacci sorozat n-edik elemét rekurzív módon. Megoldás: A Fibonacci sorozat arra a kérdésre ad választ, hogy: hány nyúlpárunk lesz 3, 4, 5,, n hónap elteltével, ha egy nyúlpár kéthónapos korától kezdve havonta egy új párt hoz a világra? Tételezzük fel, hogy az új párok is ezen törvények alapján szaporodnak, és nincs elhalálozás.. ([3], 124. oldal) Ennek alapján felírhatjuk a sorozat összefüggéseit: f(1) = 1, f(2) = 1, f(3) = f(1) + f(2), f(4) = f(2) + f(3),, f(n) = f(n-1) + f(n-2) Tehát egy tetszőleges elem esetén az eredmény az előtte lévő két elemmel kapott eredmények összege. program fibonacci; var i: integer; function fib(n: integer): longint; if n>2 then fib := fib(n-1)+fib(n-2) else fib:=1; BEGIN write( i= );readln(i); writeln(i, -ik eleme:, fib(i));readln; END. 12

1.2.4. Szófordítás Készíts egy olyan programot, ami a felhasználó által tetszőlegesen beírt szavat megfordítja. ([3], 126. oldal) Megoldás: A kész program a szó betűit fokozatosan, egyenként olvassa be egy char-ként definiált változóba, és tárolja őket a memóriában. Ezt addig teszi, míg a beolvasott billentyű ASCII kódja nem lesz egyenlő az ENTER billentyűjével. Ha lenyomtuk az ENTER-t, elkezdi a beírásukkal ellentétes sorrendben, egyenként előszedni a beírt betűket a memóriából, és ki is írja őket. program szoford; procedure fordit; var betu: char; read(betu); if betu <> #13 then fordit; write(betu); BEGIN write( a szo: ); fordit; readln;readln; END. 1.2.5. Hanoi tornyai A Hanoi tornyai matematikai játék egy ősrégi legendán alapszik. A játék szabályai, hogy az első oszlopról át kell rakni az utolsó oszlopra az összes korongot. Ehhez 3 oszlop áll rendelkezésünkre. Fontos még, hogy a korongokat csak egyenként mozgathatjuk és nagyobb korongot nem helyezhetünk tőle kisebbre. 13

A legenda szerint a világ megalakulásakor Brahma szerzetesei egy 64 korongból álló feladványt kezdtek el játszani, és akkor jön el a világvége, ha befejezik azt. [8] Old meg a feladatot n magasságú toronyra rekurzió alkalmazásával. Megoldás: A feladat rekurzív módon történő megoldásának alapgondolata az, hogy ha a program a helyére tud rakni n-1 magasságú oszlopot, akkor n magasságú oszlopra is talál megoldást. Ha n=0, akkor nem csinál semmit, mivel nincs mit átrakjon. Ha n=1, akkor egyből átrakja a kiindulási oszlopról azt az egy elemet a céloszlopra. Tehát egy lépésben végrehajtja a feladatot. Ha azonban n>1, akkor már tovább tart ez a folyamat. El kell érnünk, hogy a torony felső n 1 eleme átkerüljön a kisegítő oszlopra, mivel csak ekkor rakhatjuk át a legalsó elemet a céloszlopra. Ha ez megtörtént, a kisegítőoszlopon lévő n 1 magasságú tornyot fokozatosan átrakhatjuk a céloszlopra a kiindulási oszlop segítségével. Végül az egész oszlopunk átkerül a céloszlopra. ([1], 285. oldal) Program hanoi; uses crt; var m: integer; Procedure atrak(n:integer; start, seged, cel: char); if n > 0 then atrak(n 1, start, cel, seged); writeln(n,. Elemet atrakom,start, oszloprol,cel, oszlopra ); atrak(n 1, seged, start, cel); BEGIN clrscr; write( Torony Hossza: ); readln(m); atrak(m, a, b, c );readkey; END. 14

1.2.6. Legek Olvassunk be tetszőleges mennyiségű számot, majd rekurzióval határozzuk meg melyik közülük: a) a legnagyobb, b) a legkisebb. Megoldás: Első lépésként lekérem, hogy mennyi számot fogok beolvasni, amik közül meg kell határoznom, hogy melyikük a legnagyobb (legkisebb). Utána hivatkozom a funkciómra (max, min), amely majd megadja a keresett értékeket. Fel kell ismernünk azt a tényt, hogy ha csak 1 beolvasandó számunk van, akkor nyilván az lesz a legnagyobb (legkisebb). Ez lesz a feladatunk bázisfeltétele. Tehát a funkciónk lépésenként meghívja saját magát, és minden alkalommal csökkenti egyel a beolvasandó számok mennyiségét, míg végül 1 nem lesz. Utána visszalépeget a meghívások közt és összehasonlítgatja a beolvasott számokat a max (min) elemmel. program legnangyobb; var i: integer; function max(n: integer): integer; var x: integer; readln(x); if n>1 then max:=max(n-1); if x>max then max:=x; else max:=x; BEGIN writeln( Mennyi szamot hasznalsz fel? ); readln(i); writeln( Add meg a szamokat: ); 15

writeln( A legnagyobb a:,max(i)); readln; END. Amikor a legkisebb elemet keressük, akkor is hasonlóan járunk el, csak a max változók helyére min-t írunk és a funkció összehasonlítási sorát pedig átírjuk erre: if x<min then min:=x; 1.2.7. QuickSort (gyorsrendezés) Írjunk programot, mely quicksort használatával sorba rak n darab keresztnevet. Megoldás: A quicksort rendezési módszer a rekurzión alapul. Alapelve, hogy kiválaszt a tömb közepén egy elemet, amelyet alapelemnek hívunk. Ehhez fogjuk viszonyítani az összes többi elemét a tömbnek. Először elindul a tömb elejéről és addig halad az alapelem felé, míg nem talál egy olyan elemet, ami nagyobb mint az alapelem. Itt megáll és megjegyzi ennek az elemnek a helyét. Utána elindul a tömb végéről az alapelem felé. Ha talál olyan elemet, amely kisebb mint az alapelem, akkor ott is leáll és megjegyzi a címét. A két elemet kicseréli egymással és halad tovább, először jobbról, majd balról. Ez addig folytatódik, míg végül el nem jut mindkét oldalról a vezérelemig. A következő lépésben a tömböt a kiválasztott vezérelemnél két különálló részre osztja fel. Ezeken belül ismét választ egy-egy alapelemet, és megismétli az összehasonlítási folyamatot. A tömböt addig osztja fel kisebb részekre, míg végül eljutunk olyan apró csoportokhoz, melyekben csak 1 elem van. ([1], 295. oldal) Program nevek; Uses crt; Var a,b: array [1..100] of string; i,j,m: integer; Procedure quicksort(bal, jobb: integer); var x,ve: string; {ve vezérelem, alapelem} 16

i:= bal; {a sorozat első elemének helye} j:= jobb; {az utolsó elem helye} ve:=a[(i+j) div 2]; {megkeresi a vezérelemet} while i<=j do while a[i] < ve do inc(i); {az első elemtől halad a vezérelem felé, míg annál nagyobbat nem talál} while a[j] > ve do dec(j); {az utolsó elemtől lépeget az alapelem irányába, ameddig nem talál kisebbet nála} if i<=j then x:=a[i]; {kicseréli két elem helyét} a[i]:=a[j]; a[j]:=x; inc(i); {tovább lép eggyel mindkét irányba} dec(j); if bal < j then quicksort(bal,j); {újabb részekre osztja a sort} if i < jobb then quicksort(i,jobb); BEGIN Clrscr; Write( nevek szama: );readln(m); Writeln( Kerem a neveket: ); For i:=1 to m do readln(b[i]); a:=b; quicksort(1,m); writeln( Rendezve: ); for i:=1 to m do writeln(a[i]); readkey; END. 1.2.8. Kamatozás Írjunk programot, ami kiszámítja egy bizonyos tőke és kamat mellett, hogy előre megadott év után mennyi lesz a végösszeg. ([2], 244. oldal) 17

Megoldás: Bázisfeltételnek vegyük azt az összefüggést, hogy 0 év kamatozás után a vagyonunk megegyezik a kiindulási tőkénkkel. A funkciónk minden egyes meghívásakor az évek száma eggyel csökken. Ez addig folytatódik, míg 0 nem lesz. Minden egyes évben úgy kapom meg az új összeget, hogy kiszámolom az előző évi összeg (100 + kamat)-szorosát. program kamatozas; var e: integer; k,t: real; function osszeg (ev: integer; kamat, toke: real): real; if ev>0 then osszeg:=(osszeg(ev-1, kamat, toke)/100)*(100+kamat); end else osszeg:=toke; BEGIN write( Kamatozas hossza evekben: ); readln(e); write( Evi kamat(%): ); readln(k); write( Toke: ); readln(t); writeln(e, ev utan a kamatozott vagyonunk:,osszeg(e,k,t):10:2); readln; END. 1.2.9. Számrendszerek Írj programot, mely egy 10-es számrendszerben megadott számot átalakítja a kettes számrendszertől egészen a kilencesig terjedő intervallum bármelyikébe. 18

Megoldás: Egy tízes számrendszerbéli számot úgy alakítunk át egy másikba, hogy az alapszámot elosztjuk a választott számrendszer számával. A kapott maradékot félretesszük, és az osztás eredményének egész részét újra elosztjuk. A maradékot ismét félrerakjuk, és az eredményt megint csak elosztom a számrendszerre jellemző számmal. Ezt addig ismételem, amíg az osztás összege 0 nem lesz. Ha elértük ezt, akkor az új számrendszerbéli számot úgy kapjuk, hogy a félretett maradékokat fordított sorrendben összeolvassuk. ([3], 135. oldal) program szamrendszerek; var i,n: integer; procedure atvalt(szam, szamrend: integer); if szam > 0 then atvalt(szam div szamrend, szamrend); write(szam mod szamrend); end BEGIN write( Szam 10 es szamrendszerben: ); readln(i); write( Szamrendszer(2-9): ); readln(n); if (n>1) and(n<10) then write(i, a,n, szamredszerben: ); atvalt(i,n); end else writeln( Hibas a megadas ); readln; END. 19

1.2.10. Pascal-háromszög Szerkessz programot, ami kiírja a Pascal-háromszög általad kiválasztott sorának az elemeit. ([3], 153. oldal) Megoldás: A Pascal-háromszög fontos jellemzői: a. minden sor annyi elemet tartalmaz, ahányadik sorról van szó b. egy elemet úgy kapunk meg, hogy az előző sor vele megegyező sorszámú elemét összeadjuk annak eggyel előtte lévő elemével c. minden sor első és utolsó (azaz a sor számával megegyező sorszámú) eleme 1 d. a háromszög első, illetve második sorának elemei mind 1-esek Ezeket a szabályszerűségeket felhasználva rekurzióba önthetjük a feladatot. program pascal; uses crt; var i,n: integer; a: array [1..100,1..100] of integer; procedure pascal(sor: integer); var j: integer; if sor>2 then pascal(sor-1); a[sor,1]:=1; for j:=2 to sor-1 do a[sor,j]:=a[sor-1,j-1]+a[sor-1,j]; a[sor,sor]:=1; end else for j:=1 to sor do a[sor,j]:=1; 20

BEGIN clrscr; write( Hanyadik sort iratod ki? ); readln(n); pascal(n); for i:=1 to n do write(a[n,i], ); readln; END. A háromszög elemeit egy kétdimenziós tömbbe olvasom be (Ez az a). Először a megadott sorra hívom meg az eljárásom. Mivel ahhoz, hogy ennek az elemeit ki tudjam számolni, szükségem van az előző sor elemeire, így meghívom azt rekurzívan. Ez addig folytatódik, míg el nem jutunk a második sorig, ahol ugyanis mindkét elem 1-gyel egyenlő. Ennek segítségével már ki tudom számolni a 3. sor elemeit, amiket felhasználok a 4. sor feltöltéséhez és így tovább. Végül visszajutunk a kért sorhoz. Ha sikeresen megkaptuk az elemeit, akkor már csak ki kell íratnunk egy egyszerű for ciklussal. 21

2. Backtracking (visszalépéses keresés) 2.1. A backtracking jellemzése A backtracking, más szóval visszalépéses keresés programozási módszer, rekurzív módon keresi a megoldást. Lépésenként végigmegy az összes lehetséges útvonalon a megoldás felé haladva, és egyenként ellenőrzi, hogy vajon ezek megfelelnek-e az előre kitűzött feltételeknek vagy sem. Ha egy bizonyos helyzetben nem teljesülnek ezek a feltételek, akkor visszalépeget a legutóbbi lehetséges elágazásig, ahonnan egy újabb útvonalon folytathatja tovább útját a végcél felé. Minden egyes alkalommal ellenőrzi, hogy nem vagyunk-e már a megoldásnál. Ha igen, akkor kiírja azt. A program befejeztével egy egydimenziós tömböt kapunk. Ez tartalmazza az egyes elemek értékeit, melyek a cél felé vezető útvonalaknak felelnek meg. Az aktuális elemhez csak abban az esetben rendel értéket, ha teljesülnek rá a belső feltételek, és ha az előtte lévő összes elem is már rendelkezik értékkel. Mivel ezek az értékek veremben tárolódnak, mindig az aktuális elem értéke van legfelül. Ebből adódóan visszaléptetéskor a felső értékeket távolítjuk el lefelé haladva egészen addig, míg a legközelebbi elágazásnak megfelelő elemig el nem jutunk. ([4], 182. oldal) Többnyire olyan logikai feladatoknál használják ezt a programozási módszert, ahol a megoldáshoz szükség van az összes lehetséges helyzet megvizsgálására. Mivel ez a módszer tulajdonképpen bejárja az egész megoldásteret és lépésenként ellenőrzi azt, meglehetősen hosszadalmas a lefutási ideje. 2.2. Feladatok 2.2.1. Ülésrend Egy hosszú padra n darab ember ült le. Írjunk programot, amely backtracking segítségével meghatározza az összes lehetséges ülésrendet. 22

Megoldás: Tulajdonképpen a feladat magja egy n számra teljesülő permutáció. A program egyenként kiválasztja az összes lehetséges ülőhelyet. Kipróbálgatja, hogy mely emberek ülhetnek az egyes helyekre úgy, hogy csak egyszer forduljanak elő az ülésrendben. Mivel a végén az összes emberünk helyet foglal, ebből adódóan n helyünk lesz. Addig próbálgatja elrendezni a személyeket, míg az összes hely be nem telik. Ha az utolsó ülőhelyet is elfoglalták, a program kiírja ezt a lehetséges ülésrendet, majd visszaléptetéssel új elrendezések után kutat. A végeredményünk egy tömb, ami változó sorrendben tartalmazza az n darab elemünket. program ultetos; uses crt; var a: array [1..20] of integer; n,h: integer; function jo(m: integer): boolean; {megállapítja, hogy egy bizonyos hely szabad-e vagy sem} var i: integer; jo:=true; for i:=m-1 downto 1 do if a[i]=a[m] then jo:=false; procedure kiir; {kiírja az egyes lehetséges ülésrendeket} var i: integer; h:=h+1; writeln(h,'. lehetoseg: '); for i:=1 to n do write(a[i],' '); 23

procedure megold(k: integer); a[k]:=0; if k<n+1 then repeat if a[k]<n then a[k]:=a[k]+1; if jo(k) then if k=n then kiir; readln; megold(k+1); until a[k]=n; BEGIN clrscr; h:=0; write('hany embert akarsz egymas melle ultetni? '); readln(n); megold(1); if h=0 then writeln('nincs megoldasa') else writeln('ennyi'); readln; end. 2.2.2. Gyermeklánc Egy n létszámú gyerekcsoport, hogy biztosan együtt maradjon, egymás kezét fogva távozik az előadásról. Így tulajdonképpen egy élőláncot alkot. Írjunk programot, amely meghatározza az összes lehetséges változatát ennek az élőláncnak úgy, hogy egy gyerek csak olyan gyerekek kezét foghatja, akik azelőtt nem voltak a szomszédjai. 24

Megoldás: Ezt a feladatot hasonlóan oldjuk meg, mint az előzőt (2.2.1.). Itt azonban már jócskán lecsökken a megoldások száma, ugyanis az egyes sorozatoknak egy plusz feltételnek is meg kell felelniük. Minden egyes megoldás felfedezése után az elemek kapnak egy-egy halmazt, amiben a mellette lévő elemek tárolódnak. Mielőtt egy helyre leraknánk egy bizonyos elemet, előtte megnézzük, hogy nem szerepel-e már a sorban, illetve, hogy a szomszédjai nincsenek-e benne a hozzá rendelt halmazban. program lanc; uses crt; type elemek=set of 1..30; var a: array [1..30] of integer; b: array [1..30] of elemek; {ez tartalmazza az elemekhez rendelt halmazokat} n,h,g : integer; function jo(m: integer): boolean; var i: integer; jo:= true; for i:=m-1 downto 1 do if a[i]=a[m] then jo:=false; {megnézi, hogy az aktuális elem előtti tag nincs-e benne a hozzá rendelt halmazban} if (m>1) and (a[m-1] in b[a[m]]) then jo:=false; procedure kiir; var i: integer; h:=h+1; writeln(h,'. megoldas: '); for i:=1 to n do write(a[i],' '); 25

procedure megold(k: integer); var i,j: integer; a[k]:=0; if k<n+1 then repeat if a[k]<n then a[k]:=a[k]+1; if jo(k) then {ha az elem jó helyen van és sorrendben az utolsó is, akkor van egy megoldásunk} if k=n then kiir; for j:=2 to k do b[a[j]]:=b[a[j]]+[a[j-1]]; for j:=1 to k-1 do b[a[j]]:=b[a[j]]+[a[j+1]]; readln; megold(k+1); end until a[k]=n; BEGIN clrscr; h:=0; write('add meg a gyerekek szamat: '); readln(n); megold(1); if h=0 then writeln('nincs megoldas') else writeln('ennyi'); readln; end. 26

2.2.3. Térkép Egy térképen van n darab országunk. Fessük ki, legtöbb 4 színnel úgy, hogy a szomszédos országok nem lehetnek megegyező színűek. ([4], 213. oldal) Megoldás: A program alapjául egy kétdimenziós tömb fog szolgálni, melybe az induláskor bekérjük az egyes országok egymással való szomszédsági kapcsolatát. Minden egyes ország színezésénél először kikeresi ebben a tömbben a hozzá tartozó sort, majd meghatározza, hogy a vele szomszédos országok nem ugyan olyan színűek-e. Ha igen, akkor egy következő színt próbál le az aktuális országon. Ha azonban a feltételnek megfelel a szín, továbbléphetünk egy következő országra. Ez addig folytatódik, míg az összes országot ki nem színeztük valamilyen színnel. Az egyes lehetséges eredmények tömb formájában jelennek meg. Sorban leírják minden egyes ország hányas színnel van kiszínezve. program terkepes; uses crt; var a: array [1..20] of integer; b: array [1..20,1..20] of integer; n,h: integer; function jo(m: integer): boolean; {megállapítja, hogy a szomszédos országok tőle eltérő színűek-e} var i: integer; jo:=true; if m>1 then for i:=m-1 downto 1 do if (b[m,i]=1) and (a[m]=a[i]) then jo:=false; procedure kiir; {kiírja az egyes lehetséges színezéseket} var i: integer; 27

h:=h+1; writeln(h,'. lehetoseg: '); for i:=1 to n do write(a[i],' '); readln; procedure megold(k: integer); a[k]:=0; if k<n+1 then repeat if a[k]<4 then a[k]:=a[k]+1; if jo(k) then if k=n then kiir; megold(k+1); until a[k]=4; procedure beolvas; {egyenként beolvassa az egyes országok közti kapcsolatokat} var i,j: integer; writeln('adja meg az egyes orszagok kolcsonos helyzetet:'); writeln('ha ket orszag szomszedos, irjon be 1-est, ha nem 0-t'); for i:=1 to n do for j:=1 to n do if i=j then b[i,j]:=0 else write(i,' orszag kapcsolata ',j,' -vel: '); readln(b[i,j]); 28

BEGIN clrscr; h:=0; write('hany orszag van a terkepen? '); readln(n); beolvas; megold(1); if h=0 then writeln('nincs megoldasa') else writeln('ennyi'); readln; end. 2.2.4. Vonatozás Pár kalandor elhatározza, hogy körbeutazza vonattal az összes, n számú környező várost. Hányféle útvonalon tehetik ezt meg úgy, hogy az 1-es számú városból indulnak el és az út végén ugyan oda is érkeznek meg. A kirándulás során minden városon csak egyszer mehetnek át. Megoldás: A feladatban tulajdonképpen egy gráf Hamilton-körét kell megtalálnunk. A gráf minden pontját tartalmazó kört nevezzük Hamilton-körnek. ([4], 228. oldal) A program alapja egy kétdimenziós tömb. Induláskor bekérjük, hogy mely városokat köti össze vasútvonal. Kiindulási pontként az 1-es számú város szolgál. Ez mindig stabil, mert nem kell leellenőrizni, hogy megy-e ebbe vasút. A következő állomás meghatározására sorba veszi az összes többi várost. Megvizsgálja, hogy az aktuális város és az előtte lévő közt van e vasútvonal. Ha van, akkor keresi a következő uticélt. Akkor talál kész útitervet, ha az utolsó városból megy vasút az első városkába. A végeredmény egy tömb lesz, amely felsorakoztatja az egymást követő megállóhelyeit az útitervnek. 29

program vonatozos; uses crt; var a: array [1..20] of integer; b: array [1..20,1..20] of integer; n,h: integer; function jo(m: integer): boolean; {megkeresi a tömbben, hogy az aktuális város előtti állomáshelyről jön-e ide vonat} var i: integer; jo:=true; if m>1 then if b[a[m-1],a[m]]=0 then jo:=false else for i:=1 to m-1 do if a[m]=a[i] then jo:=false; procedure kiir; var i: integer; h:=h+1; a[n+1]:=1; writeln(h,'. lehetoseg: '); for i:=1 to n+1 do write(a[i],' '); readln; procedure megold(k: integer); a[k]:=0; if k<n+1 then repeat if a[k]<n then a[k]:=a[k]+1; if jo(k) then 30

if k=n then {az utolsó város össze van kötve az elsővel} if b[a[n],a[1]]=1 then kiir; megold(k+1); until a[k]=n; procedure beolvas; var i,j: integer; {beolvassa a városok közti útvonalakat} writeln('adja meg az egyes varosok kozti utvonalakat:'); writeln('ha ket varost osszekot vasutvonal, irjon be 1-est, ha nem 0 -t'); for i:=1 to n do for j:=1 to n do if i=j then b[i,j]:=0 else write(i,' varost osszekoto utvonal ',j,' -vel: '); readln(b[i,j]); BEGIN clrscr; h:=0; write('hany varost kivannak bejarni? '); readln(n); beolvas; a[1]:=1; megold(2); if h=0 then writeln('nincs ilyen utvonal') else writeln('ennyi'); readln; end. 31

2.2.5. Szobafestés Van egy f falú (plafonnal együtt) termünk, amit szeretnénk kifesteni. Erre rendelkezésünkre áll sz fajta szín. Hányféle képpen festhetjük ki a termet úgy, hogy csak a szemben lévő falak lehetnek ugyanolyan színűek, és a plafon nem egyezhet meg egyik fal színével sem. Megoldás: Fontos, hogy a falak száma egy páratlan szám legyen. Abból egy a plafon, így páros számú falunk lesz. Ellenkező esetben nem lesznek szemben lévő falak. Vesszük sorba a falakat, és mindegyikre kipróbáljuk, hogy milyen színűre lehet befesteni. Úgy kapjuk meg, hogy egy fallal melyik fal van szemben, hogy az aktuális fal sorszámából kivonjuk az összes fal számának a felét. Megvizsgáljuk, hogy a már befestett falak egyike (kivétel a vele szemben lévő) nem egyezik-e meg az aktuális falunk színezésével. Ha befestettük mind az f-1 falunkat, keresünk a plafonnak is egy színt, amit még nem használtunk fel az előző falak festésénél. Az eredmény egy tömb, mely leírja az egymás után következő falak színezésének a számát. program szobafest; uses crt; var a: array [1..1000] of integer; f,sz,h: integer; function jo(m: integer): boolean; var i,j: integer; jo:=true; if m>1 then if m<f then for i:=m-1 downto 1 do if i<>m-(f div 2) then if a[i]=a[m] then jo:=false; end 32

else for j:=m-1 downto 1 do if a[j]=a[m] then jo:=false; procedure kiir; var i: integer; h:=h+1; writeln(h,'. lehetoseg: '); for i:=1 to f do write(a[i],' '); readln; procedure megold(k: integer); a[k]:=0; if k<f+1 then repeat if a[k]<sz then a[k]:=a[k]+1; if jo(k) then if k=f then kiir; megold(k+1); until a[k]=sz; BEGIN clrscr; h:=0; writeln('hany fala van a teremnek a plafonnal egyutt?(paratlan szam): '); readln(f); writeln('hany szinunk van? '); readln(sz); 33

if (f mod 2) <> 0 then megold(1) else writeln('paratlan szamot kerek'); if h=0 then writeln('nincs megoldasa') else writeln('ennyi'); readln; end. 2.2.6. Királynők Többnyire ezzel a feladattal szokták bevezetni a backtracking módszerét a köztudatba. Szemléltetésével könnyedén és érthetően figyelemmel kísérhetjük a visszaléptetéses keresés folyamatának lépésenként történő lefolyását. Van egy n*n-es sakktáblánk. Ezen kellene elhelyeznünk n darab királynőt úgy, hogy azok ne üssék ki egymást. Határozzuk meg a figurák összes ilyen felállítási módját. ([4], 183. oldal) Megoldás: Végeredményként egy tömböt kapunk. A tömb elemeinek sorszáma meghatározza, hogy az egyes bábuk melyik sorban vannak. Az elemek értékei pedig az oszlopszámozásnak felelnek meg. Egy figura akkor van jó helyen, ha a már lerakott bábuk közül egyik sem üti átlósan, illetve egyikkel sincs közös oszlopban. Mivel a bábukat soronként rakjuk le, ezért arra már nem kell ügyelnünk, hogy két bábu nehogy egy sorban legyen. A program lefutása közben fokozatosan halad végig a sorokon, és azon belül az oszlopokon. Minden egyes lépésnél megvizsgálja, hogy az aktuális mezőre lerakott bábu megfelel-e a kitűzött feltételeknek. Ha egy sor egyik mezőjére sem helyezhetünk bábut, akkor a program visszalép az előző sor aktuális mezőjéhez és onnan folytatja a lépegetést. Az egyes megoldások szemléletes kiíratásához feltöltök egy kétdimenziós tömböt a bábuk helyzetével, majd azt iratom ki mátrix formájában. 34

program kiralyno; uses crt; var a: array [1..100] of integer; n,h : integer; function jo(m: integer): boolean; {megállapítja, hogy az aktuális bábut nem üti-e egy már fentlévő bábu} var i: integer; jo:= true; i:=m-1; if m>1 then repeat{átlósan egyik irányba} if a[i]+i=a[m]+m then jo:=false; {átlósan a másikba} if a[i]-i=a[m]-m then jo:=false; {oszlopot ellenőrzi} if a[i]=a[m] then jo:= false; i:=i-1; until (not jo) or (i=0); procedure kiir; var b: array [1..20,1..20] of integer; i,j: integer; h:=h+1; writeln(h,'. megoldas: '); for i:=1 to n do {a bábuk elhelyezéseit bemásolom egy kétdimenziós tömbbe} for j:=1 to n do if a[i]=j then b[i,j]:=1 else b[i,j]:=0; for i:=1 to n do {kiiratom a tömböt} for j:=1 to n do write(' ',b[i,j]); 35

procedure megold(k: integer); {megadja, hogy az adott sorban hova tehetek bábut} var i,j: integer; a[k]:=0; if k<n+1 then repeat if a[k]<n then a[k]:=a[k]+1; if jo(k) then if k=n then kiir; readln; megold(k+1); end until a[k]=n; BEGIN clrscr; h:=0; write('add meg a sakktabla meretet: '); readln(n); megold(1); if h=0 then writeln('nincs megoldas') else writeln('ennyi'); readln; end. 3.2.7. Labirintus Segítsük át a kisegeret a labirintuson, hogy eljusson a sajthoz. A labirintus méreteit és vázlatát az utveszto.txt szövegállományból olvassuk be. [6] 36

Megoldás: A labirintus alapformája egy n*m méretű mátrix, ezért egy kétdimenziós tömbbe olvassuk be. A mátrixon belül mindennek megvan a saját számozása: 0 lehetséges útvonal 3 az egér kiindulási helye 5 a sajt helye 9 a falak Először megkeressük, hogy hol lesz a kiindulási pont a tömbön belül. Ha ez megvan, lépésenként elkezdünk haladni előre. Minden egyes lépés előtt leellenőrizzük, hogy a közvetlen szomszédságában lévő elemek melyike 0, vagyis üres hely ahova léphetünk. Ha talál ilyet, akkor az aktuális helyzetét megjelöli 1-gyel és átlép. Így tudni fogja, merre járt már azelőtt. Ha zsákutcában találja magát, kénytelen visszalépegetni a már bejárt útvonalon. Ilyenkor ezeket átírja 2-re, feltüntetvén, hogy rossz az útvonal. A lépegetés egészen addig ismétlődik, míg olyan elemhez érünk, mely közvetlen szomszédjának értéke 5 nem lesz, azaz a sajt. [6] program labirint; uses crt; var lab:array [1..100,1..100] of integer; k,l,n,m: integer; kesz: boolean; f: text; procedure beolvas; {beolvassa az utveszto.txt állományban definiált labirintust a lab tömbbe} var i,j: integer; assign(f,'utveszto.txt'); reset(f); while not eof(f) do read(f,n);read(f,m); for i:=1 to n do for j:=1 to m do read(f,lab[i,j]); 37

close(f); procedure kiiras; var i,j:integer; if kesz then writeln('megoldas:'); for i:=1 to n do for j:=1 to m do case lab[i,j] of 9: {fal} textcolor(lightgray); write(#219); { #219 = befestett négyzet karakter } 0: write(' '); { üres vagy célpont } 1: { helyes útvonal } textcolor(lightgreen); write('x'); 2: {bejárt, de rossz útvonal } textcolor(red); write('o'); 3: {kiindulási pozíció} textcolor(yellow); write('e'); 5: {célpont} textcolor(yellow); write('s'); 38

procedure lepes(x,y:integer); { nem értünk be a célba? } if lab[x,y]<>5 then {lépés előre...} if lab[x,y]<>3 then {a kiindulási pontot nem írja át} lab[x,y]:=1; {van felfelé bejáratlan útvonal (üres vagy célpont)?} if (x>1) and (lab[x-1,y] in [0,5]) then lepes(x-1,y); {van jobbra bejáratlan útvonal (üres vagy célpont)?} if (y<m) and (lab[x,y+1] in [0,5]) then lepes(x,y+1); {van balra bejáratlan útvonal (üres vagy célpont)?} if (y>1) and (lab[x,y-1] in [0,5]) then lepes(x,y-1); {van lefelé bejáratlan útvonal (üres vagy célpont)?} if (x<n) and (lab[x+1,y] in [0,5]) then lepes(x+1,y); {lepés vissza... megjelölés bejárt, de rossz útvonalnak} lab[x,y]:=2;end else {megtalált útvonal kirajzolása} kesz:=true; kiiras; lab[x,y]:=2; BEGIN clrscr; kesz:=false; beolvas; writeln('jussunk el E -bol S -be'); kiiras; readln; for k:=1 to n do {megkeresi a kiindulási pontot és onnan indul útra} for l:=1 to m do if lab[k,l]=3 then lepes(k,l); if not kesz then writeln('nincs ilyen utvonal'); readln; end. 39

2.2.8. Zárójelek Adott n darab zárójel. Határozzuk meg az összes lehetséges elhelyezésüket úgy, hogy szabályosan nyíljanak és záródjanak. ([5], 81. oldal) Megoldás: Minden zárójelnek megvan a maga értéke. A nyitottaké 1, a zártaké pedig 2. Egyenként rakja le a zárójeleket. Minden egyes lerakás előtt megvizsgálja, hogy a már lerakott zárójelek közt az egyik fajtából se legyen több, mint az összes zárójel számának a fele. Így elérjük azt, hogy ugyan annyi nyitott zárójelünk lesz, mint zárt. Ez a folyamat addig folytatódik, míg le nem rakjuk az utolsó zárójelet is. ([4], 235. oldal) program zarojel; uses crt; var a: array [1..20] of integer; n: integer; procedure kiir(k: integer); var i: integer; for i:=1 to k do if a[i]=1 then write('(') else write(')'); procedure megold(k,ny,cs: integer); if k=n+1 then kiir(k-1) else {ne legyen több nyitott, mint zárt} if ny< n div 2 then a[k]:=1; {lerak egy nyitott zárójelet} megold(k+1,ny+1,cs); 40

{ellenőrzi, hogy a zárt zárójelek száma ne legyen több a nyitottakénál} if cs<ny then a[k]:=2; {lerak egy zárt zárójelet} megold(k+1,ny,cs+1); BEGIN clrscr; repeat write('kerem a zarojelek szamat (paros legyen): '); readln(n); until n mod 2 =0; megold(1,0,0); readln; end. 2.2.9. Farkas kecske káposzta Egy révészlegény vett a vásárban egy farkast, egy kecskét és egy káposztát. Hazafele át kell vinnie ezeket a folyón. Hányféle képpen teheti ezt meg úgy, hogy: a. A kecskét nem hagyhatja kettesben a káposztával, az egyik parton, mert megdézsmálja. b. A farkast sem hagyhatja ott a parton a kecskével, mert megeszi azt. c. A csónakban egyszerre csak egy valamit vihet át a túloldalra. Megoldás: Ez a fajta feladat a logikai fejtörők régi nagy klasszikusai közé tartozik. Ahhoz, hogy eljussunk a végeredményhez, egyenként meg kell vizsgálnunk az összes lehetséges szituációt és azok következményeit. A program először is feltölt egy 2*3 kiterjedésű kétdimenziós tömböt a 4 szereplőnk kiindulási helyzetével. A tömb két sora a folyó két partjának felel meg. 41

Az első oszlop a káposztát, a második a kecskét, a harmadik a farkast és végül a negyedik pedig a révészt jelenti. A szereplőink ott tartózkodnak, ahol a tömbben az értékük 1. Az eredményünk egy egydimenziós tömb lesz, ami tartalmazza az egymás után lecserélt szereplők számát. Először mindig az első szereplővel kezdi a cserét. Átviszi a túloldalra, majd megnézi, hogy a másik oldalon hagyott két elem megfelel-e a kitűzött feltételeknek. Ha nem, akkor visszaviszi az aktuális elemet, és a sorrendben a következőt viszi át. Ezen is lepróbálja, hogy a feltételeknek megfelelően van-e minden. Ha jónak talál egy cserét, akkor az egydimenziós tömbben egy szinttel feljebb lép és elkezdi keresni, hogy melyik tag lesz a következő, amit át tud vinni. Ezt az első elemnél kezdi újra. A cserék előtt azt is meghatározza, hogy a lecserélendő szereplővel megegyező parton helyezkedik-e el a révész. Ha nem, akkor át kell érte mennie. Az átszállítás akkor ér véget, ha már csak a kecske vár átvitelre a túloldalon. Ilyenkor átvisszük a kecskét és van egy lehetséges sorrendünk a cserék terén. Azért ezt a helyzetet tekintem a megoldásnak, mert ha itt nem a kecskét visszük át, hanem bármelyik más szereplőt, végül vagy zsákutcába ütközünk, vagy visszakerülünk egy olyan helyzetbe, ahol már voltunk, és körbe-körbe lépegetnénk. Ha van egy kész sorrendünk, akkor a kétdimenziós tömbünket újra feltöltjük a kezdeti pozíciókkal, és egyenként lejátszatjuk rajta a helyes cserék sorát. Minden csere után kirajzoljuk a szereplők aktuális helyzetét, így nyomon követhetjük mi is történik valójában. program fakekap; uses crt; type matrix=array [1..2,1..4] of integer; sor=array [1..100] of integer; var a: sor; b: matrix; n : integer; procedure feltolt; {feltöltöm a mátrixom a szereplők jelenlegi pozícióival} var i,j: integer; {első oszlop a káposzta} 42

{második a kecske, harmadik a farkas, negyedik a révész} for i:=1 to 2 do for j:=1 to 4 do b[i,j]:=2-i; function jo(m: integer): boolean; {leellenőrzi, hogy nincs-e két zavaró tényező egy helyen a révész jelenléte nélkül} var i: integer; jo:= true; for i:=1 to 2 do if b[i,4]=0 then if b[i,1]=1 then if (b[i,2]=1) and (b[i,3]=0) then jo:=false;end else if (b[i,2]=1) and (b[i,3]=1) then jo:=false; {megakadályozza az oda-vissza cserét} if (m>1) and (a[m]=a[m-1]) then jo:=false; procedure kiirat; {kiíratom a szereplők pillanatnyi helyét} if b[1,1]=1 then writeln('kaposzta'); if b[1,2]=1 then writeln('kecske'); if b[1,3]=1 then writeln('farkas'); writeln('------------------'); if b[1,4]=1 then writeln('revesz'); end else writeln('revesz'); writeln('------------------'); if b[2,1]=1 then writeln('kaposzta'); if b[2,2]=1 then writeln('kecske'); if b[2,3]=1 then writeln('farkas'); 43

readln; procedure csere(m: integer); var x: integer; {átvisz a révész valamit a másik partra} x:=b[1,m]; b[1,m]:=b[2,m]; b[2,m]:=x; if b[1,m]=0 then b[1,4]:=0; b[2,4]:=1; end else b[1,4]:=1; b[2,4]:=0; {a kész eredményt a cserék sorrendjének megfelelően újra lejátsza és szemlélteti} procedure lejatszas(m: integer); var h,i: integer; i:=1; feltolt; writeln(i,'.'); kiirat; for h:=1 to m do csere(a[h]); i:=i+1; writeln(i,'.'); kiirat; 44

function kesz: boolean; {kész ha már csak a kecskét kell átvinni} var i: integer; kesz:=false; if b[1,2]=1 then if (b[2,1]=1) and (b[2,3]=1) then kesz:=true; {egyenként hordja át őket és ellenőrzi, mikor jó a helyzet} procedure megold(k: integer;c: matrix;u:sor); a[k]:=0; if not kesz then repeat c:=b; if a[k]<3 then a[k]:=a[k]+1; if b[1,a[k]]<>b[1,4] then csere(4); if jo(k) then csere(a[k]); if not jo(k) then csere(a[k]) else u:=a; megold(k+1,c,u); {visszaléptetéskor az előző szituációhoz tér vissza} b:=c; a:=u; end else csere(4); until (a[k]=3) or kesz; end else a[k]:=2; writeln('megoldas: '); lejatszas(k); readln; 45

BEGIN clrscr; feltolt; kiirat; megold(1,b,a); writeln('ennyi'); readln; end. 46

Befejezés Munkám eredményeképp létrehoztam egy HTML formátumban lévő oktatószoftvert. Futtatására bármely egyszerű internetböngésző alkalmas. Leginkább az Internet Explorert ajánlanám, ugyanis más böngészők használatakor előfordulhatnak kisebb eltérések. A program kezelhetősége meglehetősen egyszerű és áttekinthető. Egy flash menü segítségével lépegethetünk az egyes pontok közt. Több lehetőség közül is választhatunk munkánk során. A program részletesen bemutatja a rekurzív és a visszalépéses keresés programozási módszerek működését, felhasználhatóságát, előnyeit és hátrányait. Mindkét metódusra több feladatot sorakoztat fel, melyek közt természetesen szerepelnek a témában már jól megszokott klasszikusok (pl.: Hanoi tornyai, 8 királynő probléma), de mellettük megoldásra vár még pár újfajta, érdekes probléma is. A feladatok mellett ismerteti a megoldások lényegét. Hogy is fogjunk hozzá egy feladathoz, milyen feltételekre kellene odafigyelnünk, és milyen változókat érdemes használnunk. A programok lefutásainak folyamatát animációkon mutatja be, melyek segítségével könnyedebbé válik megértésük. Minden animációt beépített gombok segítségével tudunk navigálni. Elindíthatjuk, bármely pillanatban megállíthatjuk, majd innen indíthatjuk tovább. Kedvünk szerint léptethetjük előre vagy hátra, de akár teljesen le is állíthatjuk. Tartalmaz még egy csúszkát, mely mozgatásával az animáció lejátszási sebességét módosíthatjuk, a hozzá tartozó kijelző pedig kimutatja azt. Ahhoz, hogy le tudjuk játszani ezeket az animációkat, szükségünk van Adobe Flash Player telepítésére. Abból is a legújabbat (Adobe Flash Player 10) ajánlanám használatra. A feladatok megoldásai közt áttekinthető formában megtaláljuk a forráskódok leírásait, rövid magyarázatokkal kiegészítve. Ezeket a forráskódokat.rar formátumban le lehet tölteni, a felhasználó kipróbálhatja működésüket, és akár még módosíthat is rajtuk. Így teljes mértékben megvizsgálhatja, hogy a forráskódok melyik része mire szolgál. 47

Felhasznált irodalom [1] PONGOR, Gy.: Szabványos Pascal Programozás és algoritmusok. Budapest: Műszaki Könyvkiadó, 2002. ISBN 963-16-2573-7 [2] BENKŐ, T.: Programozási feladatok és algoritmusok TURBO PASCAL nyelven. (BENKŐ, L., MESZÉNA, Zs. eds.) Budapest: Computerbooks, 2001. ISBN 963-618-268-X [3] AVORNICULUI, M.: Informatika. Egyszerűen programozás: Informatika intenzív. (BUZOGÁNY, L., LUKÁCS, S.) Kolozsvár: Ábel Kiadó, 2005. ISBN 973-7741-70-6 [4] IGNÁT, J., A.: Informatika. Informatika-Tankönyv a XI. osztály számára intenzív. (INCZE, K., JAKAB, I., T. eds.) Kolozsvár: Ábel Kiadó, 2006. ISBN 973-114-009-3 [5] KÁTAI Z.: Algoritmusok felülnézetből. Kolozsvár: Scientia Kiadó, 2007. ISBN 978-973-7953-74-2 [6] VÉGH, L.: Backtracking [online]: Programozás. [letöltve. 2009. 3. 4.] < http://prog.ide.sk/pas2.php?s=40 > [7] LUTTER, A.: III. A rekurzió [online]: Haladó programozás, 1999. [letöltve. 2009. 2. 15.] < http://w3.enternet.hu/infokt/internet/szakkor/f3.htm > [8] Hanoi torony [online]: Wikipédia a szabad enciklopédia [letöltve: 2009. 2. 1.] < http://hu.wikipedia.org/wiki/hanoi_torony > 48

Formulár Autor: Názov práce v slovenskom jazyku: Názov práce v jazyku vypracovania: Jazyk práce: Typ práce: Dávid Méry Zbierka úloh z programovania riešiteľné rekurziou a backtrackingom a ich demonštrácia pomocou interaktívnych animačných modelov Rekurzióval és backtrackinggel megoldható programozási feladatok gyűjteménye és azok szemléltetése interaktív animációs modellek segítségével maďarský Bakalárska práca Počet strán: 49 Univerzita: Fakulta: Katedra: Študijný odbor: Študijný program: Sídlo univerzity: Vedúci záverečnej práce: Konzultanti záverečnej práce: Univerzita J. Selyeho Pedagogická fakulta Katedra informatiky 1.1.1 Učiteľstvo akademických predmetov Matematika Informatika Komárno Dátum odovzdania: 19.05.2009 Kľúčové slová v slovenskom jazyku: Kľúčové slová v jazyku vypracovania: PaedDr. Ladislav Végh PaedDr. Ladislav Végh metódy programovania, rekurzia, backtracking, náuční program, animácie, zdrojový kód programozási módszerek,rekurzió, backtracking, oktatószoftver, animációk, forráskód 49