Rekurzió Dr. Iványi Péter 1
Függvényhívás void f3(int a3) { printf( %d,a3); } void f2(int a2) { f3(a2); a2 = (a2+1); } void f1() { int a1 = 1; int b1; b1 = f2(a1); } 2
Függvényhívás void f3(int a3) { printf( %d,a3); } a1 b1 1 akármi void f2(int a2) { f3(a2); a2 = (a2+1); } void f1() { int a1 = 1; int b1; b1 = f2(a1); } 3
Függvényhívás void f3(int a3) { printf( %d,a3); } void f2(int a2) { f3(a2); a2 = (a2+1); } a1 b1 1 akármi 1 (argumentum) void f1() { int a1 = 1; int b1; b1 = f2(a1); } 4
Függvényhívás void f3(int a3) { printf( %d,a3); } a1 b1 1 akármi void f2(int a2) { f3(a2); a2 = (a2+1); } a2 1 void f1() { int a1 = 1; int b1; b1 = f2(a1); } 5
Függvényhívás void f3(int a3) { printf( %d,a3); } a1 b1 1 akármi void f2(int a2) { f3(a2); a2 = (a2+1); } a2 1 1 (argumentum) void f1() { int a1 = 1; int b1; b1 = f2(a1); } 6
Függvényhívás void f3(int a3) { printf( %d,a3); } a1 b1 1 akármi void f2(int a2) { f3(a2); a2 = (a2+1); } a2 a3 1 1 void f1() { int a1 = 1; int b1; b1 = f2(a1); } 7
Függvényhívás void f3(int a3) { printf( %d,a3); } void f2(int a2) { f3(a2); a2 = (a2+1); } void f1() { int a1 = 1; int b1; b1 = f2(a1); } a1 b1 a2 a3 1 akármi 1 Ekkor is függvényhívás történik, de most ignoráljuk. 1 8
Függvényhívás void f3(int a3) { printf( %d,a3); } a1 b1 1 akármi void f2(int a2) { f3(a2); a2 = (a2+1); } a2 a3 1 1 void f1() { int a1 = 1; int b1; b1 = f2(a1); } 9
Függvényhívás void f3(int a3) { printf( %d,a3); } a1 b1 1 akármi void f2(int a2) { f3(a2); a2 = (a2+1); } a2 1 void f1() { int a1 = 1; int b1; b1 = f2(a1); } 10
Függvényhívás void f3(int a3) { printf( %d,a3); } a1 b1 1 akármi void f2(int a2) { f3(a2); a2 = (a2+1); } a2 2 void f1() { int a1 = 1; int b1; b1 = f2(a1); } 11
Függvényhívás void f3(int a3) { printf( %d,a3); } a1 b1 1 akármi void f2(int a2) { f3(a2); a2 = (a2+1); } a2 2 void f1() { int a1 = 1; int b1; b1 = f2(a1); } 12
Függvényhívás void f3(int a3) { printf( %d,a3); } a1 b1 1 akármi void f2(int a2) { f3(a2); a2 = (a2+1); } void f1() { int a1 = 1; int b1; b1 = f2(a1); } 13
Függvényhívás void f3(int a3) { printf( %d,a3); } a1 b1 1 akármi void f2(int a2) { f3(a2); a2 = (a2+1); } void f1() { int a1 = 1; int b1; b1 = f2(a1); } 14
Rekurzió, Definíció Egy értéket vagy egy állapotot úgy definiálunk, hogy definiáljuk a kezdőállapotot, majd általában egy állapotot az előző véges számú állapot segítségével határozzunk meg. 15
Arekurzió célja A feladat visszavezetése egy még egyszerűbb feladatra egészen addig amíg a feladat olyan egyszerű nem lesz, hogy már megoldható. Más szóval: a feladat megfogalmazása mindig ugyanaz, így a függvény önmagát hívja de egyre egyszerűbb argumentummal Előnye: az elegancia. Néhány sorban könnyen érthető kódot írhatunk Hátránya: Akkor is használjuk ha kevéssé hatékony, sőt pazarló 16
L rendszerek, kitérő A. Lindenmayer (1925-1989) vezette be mint matematikai eszközt több cellás organizmusok modellezésére 17
Definíció Az L-rendszerrel leírt modelek dinamikusak abban az értelemben, hogy az eredmény egy térbeli és időbeli folyamat eredménye. A fejlődést úgynevezett újraírással lehet megadni. 18
Újraírás Az újraírás, olyan technika complex objektumok definiálására, ahol egy egyszerű kezdeti objektum részeit egy szabály szerint helyettesítjük újabb objektumokkal. Újraírási szabály adja meg a hogyant 19
Hópehely Von Koch 1905 20
Nyelvtanok Legjobban tanulmányozott újraírási rendszerek karakterekre épülnek Az első ilyen rendszert Thue adta meg 1950-es évektől Chomsky munkája meghatározó a formális nyelvtanok területén Az L-rendszereket Lindenmayer 1968-ban vezette be 21
Különbség A Chomsky nyelvtan szekvenciálisan, egymás után sorban, alkalmazza az újraírást Az L-rendszerben az újraírás parallel, egyszerre történik, ezzel is egy celuláris organizmus osztódását szimulálva 22
Példa 1. Vegyük azokat a szavakat melyek az a és a b betűkből állnak Minden betűhöz egy újraírási szabály is adva van a b ab a Az újraírás egy alap szóval kezdődik, egy axiómával 23
Példa 2. 24
Grafikus értelmezés A karaktersorozatok (szavak) teknős grafikai parancsok sorozataként is értelmezhetők Például: F : mozdulj előre + : fordulj balra - : fordulj jobbra 25
Példa Axióma: F-F-F-F Szabály: F F-F+F+FF-F-F+F 26
Növények 1. 27
Növények 2. 28
Növények 3. 29
Nyomtatás 1. FÜGGVÉNY nyomtat(n) print n HA n!= 0 nyomtat(n-1) nyomtat(3) 3210 30
Kiértékelés folyamata Belépés (nyomtat 3) -> 3 Belépés (nyomtat 2) -> 2 Belépés (nyomtat 1) -> 1 Belépés (nyomtat 0) -> 0 Kilépés Kilépés Kilépés Kilépés 31
Megjegyzések a rekurzióról Orosz baba analógia Minden alkalommal egy új függvény kerül meghívásra Lényegében egymásba ágyazott függvények meghívásáról van szó Csak éppen a függvény mindig ugyanaz Szekvenciális végrehajtásból következik, hogy az elöző függvény végrehajtása felfüggesztődik amíg az aktuális függvény végrehajtódik 32
Megvalósítás A program aktuális állapotát egy veremben (stack) mentjük el Mivel a verem véges mindig biztosítani kell egy végfeltételt, amely a rekurziót leállítja Ha nincs végfeltétel a program a végtelenségig futna, de mivel a verem véges és hamar megtelik a program hibával leáll. 33
Nyomtatás 2. FÜGGVÉNY nyomtat(n) HA n!= 0 nyomtat(n-1) print n nyomtat(3) Mit fog nyomtatni? 34
Kiértékelés folyamata Belépés (nyomtat 3) Belépés (nyomtat 2) Belépés (nyomtat 1) Belépés (nyomtat 0) Kilépés -> 0 Kilépés -> 1 Kilépés -> 2 Kilépés -> 3 nyomtat(3) 0123 35
Rekurzió Nyomtatás 1: jobb rekurzió Rekurzív hívás a műveletek után Nyomtatás 2: bal rekurzió Rekurzív hívás a műveletek előtt 36
Általános eset, jobb rekurzió FÜGGVÉNY rekurzív(paraméter) számítás a paraméterrel a paraméter módosítása... HA feltétel(paraméter) == igaz AKKOR eredmény = kezdőérték KÜLÖNBEN eredmény = rekurzív(paraméter) visszatérési érték: eredmény 37
Mechanizmus 1. A függvény hívásakor a program a pillanatnyi futási címet a verembe menti, illetve a paramétereket is a veremre teszi. A meghívott függvény a veremből kiveszi a paramétereket és felhasználja (cím marad). Amikor vége a függvénynek a visszatérés hatására kiveszi a veremből az elmentett futási címet, majd a mutatott utasítás utáni címen folytatja a program végrehajtását. 38
Mechanizmus 2. Minden változó lokális, ezért amikor a függvény meghívja önmagát a változóknak egy új példánya jön létre, függetlenül a többitől. 39
Függvényhívás FÜGGVÉNY nyomtat(n) print n HA n!= 0 nyomtat(n-1) 40
Függvényhívás FÜGGVÉNY nyomtat(n) print n HA n!= 0 nyomtat(n-1) nyomtat(3) 3 41
Függvényhívás FÜGGVÉNY nyomtat(n 1 =3) print 3 HA 3!= 0 nyomtat(2) n 1 3 42
Függvényhívás FÜGGVÉNY nyomtat(n 1 =3) print 3 HA 3!= 0 nyomtat(2) n 1 3 43
Függvényhívás FÜGGVÉNY nyomtat(n 1 =3) print 3 HA 3!= 0 nyomtat(2) n 1 3 2 44
Függvényhívás FÜGGVÉNY nyomtat(n 2 =2) print 2 HA 2!= 0 nyomtat(1) n 1 3 n 2 2 45
Függvényhívás FÜGGVÉNY nyomtat(n 2 =2) print 2 HA 2!= 0 nyomtat(1) n 1 3 n 2 2 46
Függvényhívás FÜGGVÉNY nyomtat(n 2 =2) print 2 HA 2!= 0 nyomtat(1) n 1 3 n 2 2 1 47
Függvényhívás FÜGGVÉNY nyomtat(n 3 =1) print 1 HA 1!= 0 nyomtat(0) n 1 3 n 2 n 3 2 1 48
Függvényhívás FÜGGVÉNY nyomtat(n 3 =1) print 1 HA 1!= 0 nyomtat(0) n 1 3 n 2 n 3 2 1 49
Függvényhívás FÜGGVÉNY nyomtat(n 3 =1) print 1 HA 1!= 0 nyomtat(0) n 1 3 n 2 n 3 2 1 0 50
Függvényhívás FÜGGVÉNY nyomtat(n 4 =0) print 0 HA 0!= 0 nyomtat(n) n 1 3 n 2 n 3 2 1 n 4 0 51
Függvényhívás FÜGGVÉNY nyomtat(n 4 =0) print 0 HA 0!= 0 nyomtat(n) n 1 3 n 2 n 3 2 1 n 4 0 52
Függvényhívás FÜGGVÉNY nyomtat(n 4 =0) print 0 HA 0!= 0 nyomtat(n) n 1 3 n 2 n 3 2 1 n 4 0 53
Függvényhívás FÜGGVÉNY nyomtat(n 4 =0) print 0 HA 0!= 0 nyomtat(n) n 1 3 n 2 n 3 2 1 0 54
Függvényhívás FÜGGVÉNY nyomtat(n 4 =0) print 0 HA 0!= 0 nyomtat(n) n 1 3 n 2 n 3 2 1 0 55
Függvényhívás FÜGGVÉNY nyomtat(n 3 =1) print 1 HA 1!= 0 nyomtat(1) n 1 3 n 2 n 3 2 1 0 56
Függvényhívás FÜGGVÉNY nyomtat(n 3 =1) print 1 HA 1!= 0 nyomtat(1) n 1 3 n 2 2 1 0 57
Függvényhívás FÜGGVÉNY nyomtat(n 3 =1) print 1 HA 1!= 0 nyomtat(1) n 1 3 n 2 2 1 0 58
Függvényhívás FÜGGVÉNY nyomtat(n 2 =2) print 2 HA 2!= 0 nyomtat(2) n 1 3 n 2 2 1 0 59
Függvényhívás FÜGGVÉNY nyomtat(n 2 =2) print 2 HA 2!= 0 nyomtat(2) n 1 3 2 1 0 60
Függvényhívás FÜGGVÉNY nyomtat(n 2 =2) print 2 HA 2!= 0 nyomtat(2) n 1 3 2 1 0 61
Függvényhívás FÜGGVÉNY nyomtat(n 1 =3) print 3 HA 3!= 0 nyomtat(3) n 1 3 2 1 0 62
Függvényhívás FÜGGVÉNY nyomtat(n 1 =3) print 3 HA 3!= 0 nyomtat(3) 3 2 1 0 63
Függvényhívás FÜGGVÉNY nyomtat(n 1 =3) print 3 HA 3!= 0 nyomtat(3) 3 2 1 0 64
Mikor ne használjuk? Ha az eredmény zárt alakban is megadható Pl. számtani sorozat n-edik eleme Ha ciklussal is könnyen megoldható a feladat 65
Rekurzió és ciklus Minden ciklus megvalósítható rekurzióval Minden rekurzió megvalósítható ciklussal és segédváltozókkal. 66
Lista hossza Alapeset: Az üres lista hossza: 0 Rekurzív eset: Egy lista hossza eggyel nagyobb mint a nála eggyel kevesebb elemet tartalmazó lista FÜGGVÉNY hossz(lista) HA lista == üres KÜLÖNBEN visszaadott érték: 0 visszaadott érték: (1 + hossz(lista következő)) 67
a = hossz(listafej) FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista listafej 1.elem 2.elem NIL 68
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista listafej 1.elem 2.elem NIL 69
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista visszaadott: cím lista listafej 1.elem 2.elem NIL 70
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista visszaadott: cím lista listafej 1.elem 2.elem NIL 71
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista visszaadott: cím lista visszaadott: cím lista listafej 1.elem 2.elem NIL 72
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista visszaadott: cím lista visszaadott: cím lista listafej 1.elem 2.elem NIL 73
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista visszaadott: cím lista visszaadott: 0 cím lista listafej 1.elem 2.elem NIL 74
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista visszaadott: cím lista visszaadott: 0 cím lista listafej 1.elem 2.elem NIL 75
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista visszaadott: cím lista visszaadott: 0 cím lista listafej 1.elem 2.elem NIL 76
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista visszaadott: 1 cím lista visszaadott: 0 cím lista listafej 1.elem 2.elem NIL 77
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista visszaadott: 1 cím lista visszaadott: 0 cím lista listafej 1.elem 2.elem NIL 78
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: cím lista visszaadott: 1 cím lista visszaadott: 0 cím lista listafej 1.elem 2.elem NIL 79
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: 2 cím lista visszaadott: 1 cím lista visszaadott: 0 cím lista listafej 1.elem 2.elem NIL 80
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) visszaadott: 2 cím lista visszaadott: 1 cím lista visszaadott: 0 cím lista listafej 1.elem 2.elem NIL 81
FÜGGVÉNY hossz(lista) HA lista == üres visszaadott érték: 0 KÜLÖNBEN visszaadott érték: (1 + hossz(lista következő)) a = 2 visszaadott: 2 cím lista visszaadott: 1 cím lista visszaadott: 0 cím lista listafej 1.elem 2.elem NIL 82
Rekurzió többszörös alapesettel Elem keresése egy listában Alapeset 1: Ha üres a lista, az elem nincs benne Alapeset 2: Ha az elem egyenlő a lista adat részével akkor megvan Rekurzív eset: Egyébként keressük a lista maradék részében 83
Elem keresése egy listában FÜGGVÉNY keres(obj, lista) HA lista == üres visszaadott érték: HAMIS KÜLÖNBEN HA lista adat == obj visszaadott érték: IGAZ KÜLÖNBEN visszaadott érték: keres(obj, lista következő) 84
Többszörös rekurzív eset Elem minden előfordulását töröljük egy listából Alapeset: Ha üres a lista, az elem nem törölhető Rekurzió 1: Ha az elem egyenlő a lista adat részével akkor csak maradék résszel kell folytatni Rekurzív 2: Egyébként olyan listát kell visszaadni ami tartalmazza az adat részt és a listát amiből még törlünk 85
Elem törlése listából FÜGGVÉNY torol(obj, lista) HA lista == üres KÜLÖNBEN visszaadott érték: NIL HA lista adat == obj visszaadott érték: KÜLÖNBEN torol(obj, lista következő) visszaadott érték: lista kovetkezo = torol(obj, lista következő) 86