ALGORITMIZÁLÁS ALAPJAI. Tömösközi Péter



Hasonló dokumentumok
Változók. Mennyiség, érték (v. objektum) szimbolikus jelölése, jelentése Tulajdonságai (attribútumai):

Változók. Mennyiség, érték (v. objektum) szimbolikus jelölése, jelentése Tulajdonságai (attribútumai):

Java programozási nyelv

A C# programozási nyelv alapjai

Programozás alapjai (ANSI C)

Brósch Zoltán (Debreceni Egyetem Kossuth Lajos Gyakorló Gimnáziuma) Számelmélet I.

Webprogramozás szakkör

Segédanyagok. Formális nyelvek a gyakorlatban. Szintaktikai helyesség. Fordítóprogramok. Formális nyelvek, 1. gyakorlat

sallang avagy Fordítótervezés dióhéjban Sallai Gyula

Algoritmus fogalma. Mi az algoritmus? HF: Al Khwarizmi. Egy adott probléma megoldásának leírása elemi lépések sorozatával

Számítógép architektúra

Programozás I. 1. előadás: Algoritmusok alapjai. Sergyán Szabolcs

AZ ALGORITMUS. az eredményt szolgáltatja

Bánsághi Anna 2014 Bánsághi Anna 1 of 68

Algoritmusok. Dr. Iványi Péter

Készítette: Nagy Tibor István Felhasznált irodalom: Kotsis Domokos: OOP diasor Zsakó L., Szlávi P.: Mikrológia 19.

9. előadás. Programozás-elmélet. Programozási tételek Elemi prog. Sorozatszámítás Eldöntés Kiválasztás Lin. keresés Megszámolás Maximum.

Algoritmusok Tervezése. 6. Előadás Algoritmusok 101 Dr. Bécsi Tamás

Bevezetés a programozásba

2018, Diszkrét matematika

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

Programozás I. Sergyán Szabolcs Óbudai Egyetem Neumann János Informatikai Kar szeptember 10.

Programozási alapismeretek 1. előadás

Bevezetés az informatikába

1. Jelölje meg az összes igaz állítást a következők közül!

Webprogramozás szakkör

Programozási segédlet

Vezérlési szerkezetek

Formális nyelvek és automaták

Occam 1. Készítette: Szabó Éva

Tömbök kezelése. Példa: Vonalkód ellenőrzőjegyének kiszámítása

Pásztor Attila. Algoritmizálás és programozás tankönyv az emeltszintű érettségihez

ELEMI PROGRAMOZÁSI TÉTELEK

3 A C programozási nyelv szintaktikai egységei

Algoritmusok, adatszerkezetek, objektumok

Operációs rendszerek. 11. gyakorlat. AWK - szintaxis, vezérlési szerkezetek UNIVERSITAS SCIENTIARUM SZEGEDIENSIS UNIVERSITY OF SZEGED

B I T M A N B I v: T M A N

Adattípusok, vezérlési szerkezetek. Informatika Szabó Adrienn szeptember 14.

ALGORITMIKUS SZERKEZETEK ELÁGAZÁSOK, CIKLUSOK, FÜGGVÉNYEK

Információk. Ismétlés II. Ismétlés. Ismétlés III. A PROGRAMOZÁS ALAPJAI 2. Készítette: Vénné Meskó Katalin. Algoritmus. Algoritmus ábrázolása

Az algoritmusok alapelemei

C programozás. { Márton Gyöngyvér, 2009 } { Sapientia, Erdélyi Magyar Tudományegyetem }

OOP I. Egyszerő algoritmusok és leírásuk. Készítette: Dr. Kotsis Domokos

A félév során előkerülő témakörök

Programozás I. Sergyán Szabolcs Óbudai Egyetem Neumann János Informatikai Kar szeptember 10.

Egyirányban láncolt lista

Programozás alapjai. (GKxB_INTM023) Dr. Hatwágner F. Miklós augusztus 29. Széchenyi István Egyetem, Gy r

1. előadás. Lineáris algebra numerikus módszerei. Hibaszámítás Számábrázolás Kerekítés, levágás Klasszikus hibaanalízis Abszolút hiba Relatív hiba

Java II. I A Java programozási nyelv alapelemei

A C programozási nyelv I. Bevezetés

A C programozási nyelv I. Bevezetés

Adatbázis rendszerek. dr. Siki Zoltán

Informatika tagozat osztályozóvizsga követelményei

Programozási tételek. Dr. Iványi Péter

Java II. I A Java programozási nyelv alapelemei

Rekurzió. Dr. Iványi Péter

Egyszerű programozási tételek

Algoritmusok tervezése

Követelmény az 5. évfolyamon félévkor matematikából

A programozás alapjai előadás. Amiről szólesz: A tárgy címe: A programozás alapjai

ALGORITMUSOK, ALGORITMUS-LEÍRÓ ESZKÖZÖK

BASH script programozás II. Vezérlési szerkezetek

Adatszerkezetek Tömb, sor, verem. Dr. Iványi Péter

Nézzük tovább a lexikai egységeket!

Felvételi tematika INFORMATIKA

Oktatási segédlet 2014

5. előadás. Programozás-elmélet. Programozás-elmélet 5. előadás

C programozás. 1 óra Bevezetés

1. Alapok. #!/bin/bash

Struktúra nélküli adatszerkezetek

Követelmény a 7. évfolyamon félévkor matematikából

Objektumorientált Programozás III.

Előfeltétel: legalább elégséges jegy Diszkrét matematika II. (GEMAK122B) tárgyból

Programozási alapismeretek. 1. előadás. A problémamegoldás lépései. A programkészítés folyamata. Az algoritmus fogalma. Nyelvi szintek.

Adatszerkezetek Adatszerkezet fogalma. Az értékhalmaz struktúrája

Felvételi vizsga mintatételsor Informatika írásbeli vizsga

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

26. MINIMÁLIS KÖLTSÉGŰ UTAK MINDEN CSÚCSPÁRRA

2. Fejezet : Számrendszerek

Kifejezések. Kozsik Tamás. December 11, 2016

A programozás alapjai

Funkcionális és logikai programozás. { Márton Gyöngyvér, 2012} { Sapientia, Erdélyi Magyar Tudományegyetem }

A PROGRAMOZÁS ALAPJAI 3. Készítette: Vénné Meskó Katalin

Web-programozó Web-programozó

1. Alapfogalmak Algoritmus Számítási probléma Specifikáció Algoritmusok futási ideje

Szoftvertervezés és -fejlesztés I.

Matematika. 1. évfolyam. I. félév

Karakterkészlet. A kis- és nagybetűk nem különböznek, a sztringliterálok belsejét leszámítva!

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

Függvények. Programozás alapjai C nyelv 7. gyakorlat. LNKO függvény. Függvények(2) LNKO függvény (2) LNKO függvény (3)

Programozás alapjai C nyelv 7. gyakorlat. Függvények. Függvények(2)

SZÁMÍTÁSOK A TÁBLÁZATBAN

Kinek szól a könyv? A könyv témája A könyv felépítése Mire van szükség a könyv használatához? A könyvben használt jelölések. 1. Mi a programozás?

1.1. A forrásprogramok felépítése Nevek és kulcsszavak Alapvető típusok. C programozás 3

Feladat. Bemenő adatok. Bemenő adatfájlok elvárt formája. Berezvai Dániel 1. beadandó/4. feladat április 13. Például (bemenet/pelda.

Adatok ábrázolása, adattípusok

Amortizációs költségelemzés

KOVÁCS BÉLA, MATEMATIKA I.

Osztályozóvizsga követelményei

Kifejezések. Kozsik Tamás. December 11, 2016

Átírás:

ALGORITMIZÁLÁS ALAPJAI Tömösközi Péter

MÉDIAINFORMATIKAI KIADVÁNYOK

ALGORITMIZÁLÁS ALAPJAI Tömösközi Péter Eger, 2011

Lektorálta: CleverBoard Interaktív Eszközöket és Megoldásokat Forgalmazó és Szolgáltató Kft. A projekt az Európai Unió támogatásával, az Európai Szociális Alap társfinanszírozásával valósul meg. Felelős kiadó: dr. Kis-Tóth Lajos Készült: az Eszterházy Károly Főiskola nyomdájában, Egerben Vezető: Kérészy László Műszaki szerkesztő: Nagy Sándorné Kurzusmegosztás elvén (OCW) alapuló informatikai curriculum és SCORM kompatibilis tananyagfejlesztés Informatikus könyvtáros BA, MA lineáris képzésszerkezetben TÁMOP-4.1.2-08/1/A-2009-0005

Tartalom 1. Bevezetés... 9 1.1 Célkitűzés... 9 1.2 A tananyag tartalma... 10 1.3 A tananyag tömör kifejtése... 10 1.4 Kompetenciák és követelmények... 10 1.5 Tanulási tanácsok, tudnivalók... 10 2. Az algoritmusok jellemzői és leírásuk... 11 2.1 Célkitűzés... 11 2.2 Tartalom... 11 2.3 A tananyag kifejtése... 11 2.3.1 Az algoritmus... 11 2.3.2 Mit jelent az, hogy elemi lépés?... 13 2.3.3 Az algoritmusok jellemzői... 15 2.3.4 Algoritmusok megadása... 16 2.4 Összefoglalás... 31 2.5 Önellenőrző kérdések... 31 3. Adatok, adatszerkezetek... 32 3.1 Célkitűzés... 32 3.2 Tartalom... 32 3.3 A tananyag kifejtése... 32 3.3.1 Adat... 32 3.3.2 A literálok... 33 3.3.3 A változók... 34 3.3.4 Összetett adatok, adatszerkezetek... 37 3.3.5 A tömb... 38 3.3.6 További homogén adatszerkezetek... 42 3.3.7 A rekord... 46 3.3.8 A fájl... 47 3.4 Összefoglalás... 47 3.5 Önellenőrző kérdések... 47 4. Adatsorhoz egy adatot rendelő eljárások 1.... 48 4.1 Célkitűzés... 48 4.2 Tartalom... 48 4.3 A tananyag kifejtése... 48 4.3.1 A tömbfeltöltés... 48 4.3.2 Az összegzés... 50 4.3.3 Az eldöntés... 52 4.3.4 A megszámlálás... 54 4.3.5 A kiválasztás... 57 4.3.6 A szélsőérték-kiválasztás... 57 5

4.4 Összefoglalás... 59 4.5 Önellenőrző kérdések... 60 5. Adatsorhoz egy adatot rendelő eljárások 1. Keresések... 61 5.1 Célkitűzés... 61 5.2 Tartalom... 61 5.3 A tananyag kifejtése... 61 5.3.1 A teljes keresés... 61 5.3.2 A strázsás keresés... 62 5.3.3 A lineáris keresés... 63 5.3.4 A bináris (logaritmikus) keresés... 63 5.3.5 Sztring keresése sztringben mintaillesztés... 66 5.4 Összefoglalás... 68 5.5 Önellenőrző kérdések... 68 6. Adatsorhoz adatsort rendelő eljárások 1.... 69 6.1 Célkitűzés... 69 6.2 Tartalom... 69 6.3 A tananyag kifejtése... 69 6.3.1 Kiválogatás... 69 6.3.2 Az összefuttatás... 72 6.4 Összefoglalás... 74 6.5 Önellenőrző kérdések... 75 7. Adatsorhoz adatsort rendelő eljárások 2. Rendező eljárások... 76 7.1 Célkitűzés... 76 7.2 Tartalom... 76 7.3 A tananyag kifejtése... 77 7.3.1 Két elem cseréje egy vektorban... 77 7.3.2 Rendezés közvetlen kiválasztással... 77 7.3.3 A buborékrendezés... 78 7.3.4 Rendezés szélsőérték-kiválasztással... 79 7.3.5 A beszúró rendezés... 81 7.3.6 Shell módszere... 82 7.3.7 A gyorsrendezés... 83 7.4 Összefoglalás... 87 7.5 Önellenőrző kérdések... 87 8. Példák az eljárások alkalmazására... 89 8.1 Célkitűzés... 89 8.2 Tartalom... 89 8.2.1 Példák a tételek alkalmazására... 89 8.3 Összefoglalás... 117 8.4 Önellenőrző kérdések... 117 9. Algoritmusok hatékonysága... 118 9.1 Célkitűzés... 118 6

9.2 Tartalom... 118 9.3 A tananyag kifejtése... 118 9.3.1 Algoritmusok műveletigénye... 118 9.3.2 Aszimptotikus jelölések... 119 9.4 Összefoglalás... 121 9.5 Önellenőrző kérdések... 121 10. Alprogramok... 122 10.1 Célkitűzés... 122 10.2 Tartalom... 122 10.3 A tananyag kifejtése... 122 10.3.1 Az alprogramok célja, jellemzői, fajtái... 122 10.3.2 Formális és aktuális paraméterek... 125 10.3.3 A rekurzió... 126 10.3.4 Hatókör, élettartam... 127 10.4 Összefoglalás... 128 10.5 Önellenőrző kérdések... 128 11. Dinamikus adatszerkezetek... 129 11.1 Célkitűzés... 129 11.2 Tartalom... 130 11.3 A tananyag kifejtése... 130 11.3.1 A halmaz... 130 11.3.2 A lista... 136 11.3.3 A verem... 138 11.3.4 Sor... 139 11.3.5 Fa... 139 11.3.6 A háló... 142 11.4 Összefoglalás... 143 11.5 Önellenőrző kérdések... 143 12. Összefoglalás... 144 12.1 A kurzusban kitűzött célok összefoglalása... 144 12.2 Tartalmi összefoglalás... 144 12.3 A tananyagban tanultak részletes összefoglalása... 144 12.3.1 Bevezetés... 144 12.3.2 Az algoritmusok jellemzői és leírásuk... 144 12.3.3 Adatok, adatszerkezetek... 145 12.3.4 Adatsorhoz egy adatot rendelő eljárások 1.... 145 12.3.5 Adatsorhoz egy adatot rendelő eljárások 2. Keresések... 145 12.3.6 Adatsorhoz adatsort rendelő eljárások 1.... 145 12.3.7 Adatsorhoz adatsort rendelő eljárások 2. Rendező eljárások... 145 12.3.8 Példák az eljárások alkalmazására... 145 12.3.9 Az algoritmusok hatékonysága... 145 12.3.10 Alprogramok... 146 12.3.11 Dinamikus adatszerkezetek... 146 12.4 Zárás... 146 7

12.5 Egyéb... 146 13. Kiegészítések... 147 13.1 Irodalomjegyzék... 147 14. Ábrajegyzék... 148 15. Médiaelemek... 150 16. Tesztek... 151 16.1 Próbateszt... 151 16.2 Záróteszt A.... 155 16.3 Záróteszt B.... 158 16.4 Záróteszt C.... 162 8

1. BEVEZETÉS Az algoritmizálás alapjai című kurzusban elemi algoritmusokkal, azok jellemzőivel és lehetséges felhasználási területeikkel ismerkedünk meg. Algoritmusok segítségével egyszerűbb és bonyolultabb problémákat oldhatunk meg. Hétköznapi szavakkal az algoritmus valamilyen probléma vagy feladat megoldásának lépéseit határozza meg. Ha ezeket a lépéseket a megfelelő sorrendben hajtjuk végre, azzal megadjuk egy probléma vagy feladat egy lehetséges megoldását. Az algoritmus kifejezést értelmezhetjük úgy is, hogy egy olyan számítási eljárást, amely valamilyen bemenetből (bemenő adatból vagy bemenő adatok sorozatából) a lépések végrehajtásával valamilyen kimenetet állít elő. Sokan tévesen hiszik azt, hogy az algoritmizálás és a programozás kifejezések lényegében azonos tevékenységeket jelölnek. Kétségtelen, hogy helyes programok készítéséhez általában ismernünk kell különféle algoritmusokat. Azonban egyrészt algoritmusokat konkrét programozási nyelv ismerete nélkül is megadhatunk. Gondoljunk elemi matematikai feladatokra: pl. ismerjük egy síkidom oldalainak hosszát, és szeretnénk kiszámítani a kerületét és területét. Bemenő adatok az oldalak hosszúságai, kimenő adatok a síkidom kerülete és területe. A megoldáshoz bizonyos síkidomok esetében egyszerű képleteket alkalmazhatunk, néha azonban, például a terület kiszámítása csak több lépésben valósítható meg (pl. egy sokszöget először háromszögekre bontunk, és a háromszögek területének összegét kiszámítva kapjuk meg a síkidom területét). Másrészt léteznek olyan programozási nyelvek is, amelyek használatakor a programozónak nem kell ismernie a feladat megoldásának lépéseit, vagyis a megoldás algoritmusát. Ezekben a nyelvekben a megoldást maga a programozási nyelv keresi meg. Az ilyen nyelveket deklaratív programozási nyelveknek nevezzük. Deklaratív nyelveket használva a programozónak azt kell megadnia, hogy mit szeretne megoldani, és nem azt, hogy hogyan. Ezzel szemben az imperatív nyelvekben a programozó a feladat megoldásának algoritmusát ülteti át az adott programozási nyelvbe, figyelembe véve a nyelv szintaxisát és eszközrendszerét. A programozó változókat használ. Ezek segítségével eléri és módosítani tudja a memóriában tárolt adatokat, a probléma megoldásának algoritmusát ő adja meg. Az imperatív nyelveket szokás procedurális nyelveknek is nevezni. Az algoritmus tehát többféle módon értelmezhető és megadható: absztrakt módon (pl. matematikai eszközökkel), számítógépes programként stb. Aki algoritmusokkal foglalkozik, az egyben adatokkal, adatszerkezetekkel is foglakozik. Ez az algoritmus kifejezés fenti értelmezéséből is adódik, hiszen az algoritmus a fentiek szerint egy olyan számítási eljárás, amely bemenő adatokból kimenő adatokat állít elő. A kurzus másik célja, hogy a különböző problémák megadásához használható adatok és adatszerkezetek jellemzőit is bemutassa. Kijelenthetjük tehát, hogy ha valaki programozással, vagy az informatika sok más területével (pl. adatbázis-kezelés, számítógép-hálózatok, térinformatika, kriptográfia stb.) kíván foglalkozni, szüksége lesz alapvető algoritmizálási ismeretekre is. 1.1 CÉLKITŰZÉS A kurzus célja az algoritmusokkal, illetve adatokkal, adatszerkezetekkel kapcsolatos alapvető ismeretek bemutatása, és felhasználási lehetőségeik áttekintése. 9

Az algoritmizálás önálló tudomány. A jegyzet azonban olyan hallgatók számára készült, akik csak érintőlegesen foglalkoznak ezzel a tárggyal, ezért a jegyzet elkészítésekor nem volt célunk, hogy algoritmuselméleti és -helyességi kérdésekkel is foglalkozzunk. A jegyzet csak elemi algoritmusok bemutatását és felhasználásuk lehetséges módjait tárgyalja. 1.2 A TANANYAG TARTALMA 1. Bevezetés 2. Az algoritmusok jellemzői, az algoritmusokkal szemben támasztott követelmények. A Böhm Jacopini-tétel, vezérlési szerkezetek az algoritmusokban 3. Algoritmusok megadásának eszközei 4. Adatok és adatszerkezetek jellemzői 5. Adatsorhoz egy adatot rendelő algoritmusok 6. Adatsorhoz adatsort rendelő algoritmusok 7. Rendező algoritmusok 8. Alprogramok 9. Dinamikus adatszerkezetek 10. Feladatok és megoldásaik 1.3 A TANANYAG TÖMÖR KIFEJTÉSE A kurzusban elemi algoritmusokkal, az ún. programozási tételekkel ismerkedünk meg. Ahogyan a bevezetőből már kiderült, az algoritmusok megadása és a programozás kifejezések különböző tevékenységeket jelölnek, de az algoritmusok legfőbb alkalmazási területe az informatikában a programozás. Emiatt szükséges, hogy az elemi algoritmusokon túl megismerkedjünk néhány programozási nyelv alapvető jellemzőivel is, hogy a különböző eszközökkel felírt algoritmusokat a gyakorlatban is ki tudjuk próbálni. Ehhez nyújt segítséget a 10. fejezet. Hangsúlyozzuk azonban, hogy nem célunk a programozási nyelvek teljes eszközrendszerének bemutatása, hiszen ez nem egy programozás tankönyv. 1.4 KOMPETENCIÁK ÉS KÖVETELMÉNYEK A kurzus teljesítéséhez előfeltételként alapvető informatikai ismeretek szükségesek. A tananyag megértéséhez ismerni kell a Neumann-elv alapvető pontjait, a processzor és a memória szerepét, illetve az adattárolás alapjait. 1.5 TANULÁSI TANÁCSOK, TUDNIVALÓK A tananyag megértéséhez feltétlenül szükséges a definíciók pontos ismerete. Nagyon fontos, hogy megértse a második és harmadik fejezet minden részét, és csak ezután kezdjen a konkrét algoritmusok tanulmányozásához. 10

2. AZ ALGORITMUSOK JELLEMZŐI ÉS LEÍRÁSUK 2.1 CÉLKITŰZÉS Ebben a fejezetben az algoritmusokkal és az algoritmizálással, algoritmuskészítéssel kapcsolatos alapvető fogalmakkal ismerkedünk meg. Megismerjük az algoritmus szó értelmezését, az algoritmusokkal szemben támasztott követelményeket, és az algoritmusok pontos megadásának különböző eszközeit. 2.2 TARTALOM Az algoritmus kifejezés értelmezése Algoritmusok jellemzői és algoritmusokkal szemben támasztott követelmények Algoritmusleíró eszközök és jellemzőik 2.3 A TANANYAG KIFEJTÉSE Ebben a fejezetben az algoritmizálás alapjainak tárgyalásához szükséges fogalmakkal és eszközökkel ismerkedünk meg. 2.3.1 Az algoritmus A köznyelvben az algoritmus szó valamilyen műveletsort, tevékenységet jelent, amelylyel egy probléma megoldását adjuk meg. Lényegében minden tevékenységünkkel algoritmusokat követünk: minden mozdulatsorunk, de még a gondolkodásunk is algoritmizálható. Ahhoz, hogy valamit megoldjunk, algoritmust használunk, legyen az munka, játék, hobbi vagy sport. Algoritmust követünk egy étel készítésekor (recept), egy lapraszerelt bútor összerakásakor (útmutató rajz), vagy ha szeretnénk hibát bejelenteni vagy információhoz jutni valamilyen közüzemi szolgáltatónál ( kérem, telefonja billentyűzete segítségével válasszon az alábbi menüből ), hogy csak néhány példát említsünk. A továbbiakban az algoritmust olyan számítási műveletként értelmezzük, amely valamilyen bemeneti (input) adatból vagy adatsorból egy vagy több kimeneti (output) adatot állít elő. Egy algoritmus elemi lépések meghatározott sorrendű véges sorozata. Ez a megfogalmazás matematikai szempontból nem kellően pontos. Megjegyzésként említjük meg, hogy egy absztraktabb értelmezés szerint az algoritmus egy Turing-gépre írt program. A Turing-gép egy absztrakt automata, tehát nem egy valós, fizikai gépre kell gondolnunk, hanem egy matematikai módszerre, a valós digitális számítógépek egy nagyon leegyszerűsített modelljére. 11

1. kép A Turing-gép vázlata A Turing-gép egy olyan gép, amelyhez egy végtelen hosszúságú papírszalag tartozik. Ezen a szalagon cellákban jeleket találunk, minden cellában egy-egy jelet. A Turing-gép nyilvántartja az aktuális cella pozícióját. Minden lépésben lehetőség van beolvasni az aktuális cella tartalmát, lehetőség van az előző és a következő pozícióra léptetni az olvasófejet, lehetőség van az aktuális cella tartalmát egy másik jelre változtatni. A gép minden lépésnél valamilyen belső állapotban van (a belső állapotok száma véges sok), az egyes cellák értékei ezeket a belső állapotokat megváltoztathatják. A gép tehát minden lépésben beolvassa az aktuális cella tartalmát, megváltoztat(hat)ja belső állapotát, megváltoztat(hat)ja a jelet a cellában, aztán elmozdul(hat). Ez a folyamat kétféle kimenetet eredményezhet: a gép terminális (leálló) belső állapotba kerül és megáll (ez a szabályos lefutás), sosem kerül stop állapotba, végtelen ideig fut. A belső állapotok megváltoztatása a gép vezérlőegységének feladata. Az, hogy egy állapotból milyen állapotba válthat a gép, egyrészt függ a szalagon található jelektől, másrészt attól, hogy az automata működése milyen utasításokkal van leírva. A Turing-gép fogalmának bevezetésével absztrakt definíció adható az algoritmusra: Egy probléma megoldására adott utasítások sorozata akkor algoritmus, ha létezik olyan vele ekvivalens Turing-gép, amely minden megoldható bemenet esetén megáll. A Turing-gép megalkotása Alan Turing (1912 1954) brit matematikus nevéhez fűződik. A gép fogalmát egy 1936-ban megjelent cikkében vezeti be. Turing bebizonyította, hogy minden speciális automata leírható véges hosszúságú utasítássorozattal. Ez az eredmény nagy hatással volt Neumann János (1903 1957) munkásságára, illetve a mai korszerű digitális számítógépek fejlődésére. 12

2. kép Alan Turing 2.3.2 Mit jelent az, hogy elemi lépés? 3. kép Neumann János Erre a kérdésre többféle dolog határozhatja meg a választ. Ha abból indulunk ki, hogy azért adunk meg egy feladathoz algoritmust, hogy abból azután számítógépes programot készítsünk, az elemi lépések nagyságát meghatározhatja az adott programozási nyelv eszközrendszere. Köztudott, hogy a futtatott program utasításait a memória tárolja, a futáshoz szükséges adatokkal együtt. A program utasításait a processzor hajtja végre. A processzor csak egyféle programot tud végrehajtani, olyat, amelyet az ő saját utasításkészletén írtak. Minden processzortípusnak egyedi jellemzői vannak, így egyedi a különböző processzorok utasításkészlete is. A processzorok saját utasításkészletén írott programokat nevezzük gépi kódnak. A gépi kód lényegében bináris számjegyek sorozataként írható fel, egy-egy utasítás egy 8, 16, 24 stb. jegyű bináris jelsorozat. Egy ilyen gépi kódú utasítás önmagában általában nem túl látványos hatást eredményez (pl. beír egy memóriaterületre egy 1 bájtos számot, vagy növeli/csökkenti egy memóriacellában található szám értékét stb.) főleg, ha ún. magasszintű programozási nyelvek utasításaihoz hasonlítjuk őket. A gépi kódú prog- 13

ramozás tehát azért nehéz, mert nagyon elemi szintű utasítások állnak csak rendelkezésünkre, másrészt pedig az emberi szem számára lényegében olvashatatlan egy ilyen kód: kevés olyan ember van, aki számára egy több ezer bináris számjegyből álló sorozatot gépi kódként azonnal meg tud érteni. A számítástechnika hőskorában azonban ez volt az egyetlen lehetséges programozási módszer. Az első eszköz, amely kicsit barátságosabbá tette a programozást, az assembly nyelv volt. Ez lényegében a gépi kód egy olvashatóbb jelölésmódját jelenti, amelyben az utasításokat bináris számjegyek helyett 2-3 betűs szavak, rövidítések, ún. mnemonikok jelölik. Lássunk egy példát: gépi kód 10110000 01100001 assembly MOV AL, 061h Az utasítás jelentése: tedd a hexadecimális 61 értéket a processzor AL regiszterébe! A processzorok azonban közvetlenül az assembly nyelvet sem értik, ahhoz tehát, hogy egy ilyen nyelven írott programot futtatni tudjunk, az assembly nyelvű kódot gépi kódra kell fordítani. Az ilyen fordítóprogramokat nevezzük assemblernek. Az 1950-es évektől kezdtek megjelenni az ún. magas szintű programozási nyelvek 1. Ezek közös jellemzője, hogy közelebb állnak az ember beszélte (általában angol) nyelvhez, mint a gépi kódhoz. Magas szintű programozási nyelven való programozáshoz nem (feltétlenül) szükséges ismernünk a számítógép hardverének (elsősorban a processzor és a memória) felépítését, sőt, sok magas szintű programozási nyelvben nincs is eszközünk arra, hogy közvetlenül nyúljunk a memóriához vagy a processzorhoz. A magas szintű programozási nyelvekben különböző adattípusokat és adatszerkezeteket, illetve összetett utasításokat használhatunk. (A nagyságrend érzékeltetéséhez: gépi kódban egy utasítással beírhatunk egy adatot egy bizonyos memóriacímre, míg egy erre alkalmas magas szintű programozási nyelvben például egyetlen utasítás segítségével a képernyő felbontásához igazítva és a videokártya sajátosságait figyelembe véve lejátszhatunk egy videoállományt.) Kissé pontatlanul az alábbi megállapításokat tehetjük: mivel a magas szintű programozási nyelvek sokkal több kész eszközt adnak a programozó kezébe, a magas szintű programozási nyelveken egyszerűbb a programozás; mivel gépi kódban a programozó közvetlenül programozza a processzort, sokkal hatékonyabb kódok írhatók gépi kódban, mint amilyen gépi kódot a magas szintű programozási nyelvek fordítóprogramjai állítanak elő. 1 Az első magas szintű nyelv a FORTRAN volt, melynek leírását 1954-ben tette közzé az IBM. Az első gyakorlatban használt programozási nyelv tíz évvel korábban készült, Konrad Zuse készítette 1944-ben Plankalkül néven a Z4 jelű számítógépéhez. 14

Mindkét állításban van igazság, de ennél azért lényegesen összetettebb kérdésről van szó. A laikus számára egy magas szintű programozási nyelven írott kód is lehet éppen anynyira értelmezhetetlen, mint egy gépi kódú, másrészt a programozási nyelvek fordítóprogramjai egyre fejlettebbek, és az új fordítók képesek lehetnek közel olyan hatékonyságú gépi kód előállítására, mint amilyet egy gépi kódban jártas programozó készít. Összefoglalva: az elemi lépés pontos meghatározása többféle tényezőtől függhet. Általában elmondhatjuk, hogy elvárjuk az elemi lépésektől, hogy függetlenek legyenek, vagyis egy elemi lépés ne legyen felírható más elemi lépések sorozataként, a probléma szempontjából legyenek fontosak, célszerűek és hatékonyak (relevancia). 2.3.3 Az algoritmusok jellemzői A 2.3.1 fejezetben kijelentettük, hogy az algoritmus elemi lépések meghatározott sorrendű véges sorozataként értelmezhető. Az elemi lépések nagysága az előző részben írottak alapján függhet például attól, hogy milyen programozási nyelven akarjuk implementálni a kódot. (Az implementáció kifejezés azt jelenti, hogy az algoritmust egy konkrét programozási nyelv utasításkészletére írjuk át, más szavakkal konkrét programozási nyelvi megvalósítást értünk alatta.) Lássunk egy példát: a tankönyvekből unásig ismert hétköznapi algoritmus a teafőzés algoritmusa. Ha a teafőzés algoritmusát gépi kódra akarjuk alkalmazni, képletesen szükségünk lesz két hidrogén- és egy oxigénatomra, amelyekből némi munkával vizet hozhatunk létre. Hasonlóképp szükségünk lesz másféle atomokra és molekulákra, amelyekből teafüvet, cukrot, citromlevet stb. készítünk. Az összetevőkből azután energia közlésével (melegítés) és keveréssel előállítható a kívánt ital. Ezzel szemben a mai modern magas szintű, pl. objektumorientált programozási nyelvek mindegyikében (képletesen) létezik tea objektum, amelyet a programozónak nem kell létrehoznia, mert készen kapja azt, ha szüksége van rá. (A teafőzés telefonálás, levél feladása postán, bevásárlás stb. algoritmusának megadása a legtöbb, kezdőknek szóló algoritmizálás-tankönyv kedvelt bevezető példája: Írjuk le egy hétköznapi tevékenység pl. teafőzés, telefonálás stb. algoritmusát!. Az ilyen feladatokra azonban nem lehet algoritmust adni, ugyanis nem egyértelműek. Nem tudjuk, hogy otthon a konyhában vagy a Mount Everesten állunk-e éppen. Nem tudjuk, hogy milyen eszközök állnak rendelkezésünkre a teafőzéshez, illetve van-e nálunk mobiltelefon, vagy fülkét kell keresnünk, és ha igen, merre induljunk. Nem tudjuk, hány főnek készül a tea, és így tovább. Összefoglalva: nyitott feladatok megoldására nem lehet algoritmust adni. Az egyértelműség elengedhetetlen: amíg nem tudjuk pontosan megfogalmazni, mi a feladat, addig a megoldást sem tudjuk megadni.) Az elemi lépések meghatározott sorrendjére vonatkozó követelmény nem igényel magyarázatot. Egy feladatot csak úgy tudunk megoldani, ha a lépéseket a megfelelő sorrendben hajtjuk végre. A végesség is nyilvánvaló követelmény. Az általunk tárgyalt algoritmusok lépéseinek elemszáma mindig véges. (Megjegyzés: a végesség többféle módon értelmezhető. Jegyzetünkben a végesség lépések leírására vonatkozik ez az ún. statikus végesség, és nem a végrehajtásukra. Ha egy algoritmus öt egymás utáni utasításból áll, és az ötödik utasítás az, 15

hogy ugorj vissza az 1. utasításra, akkor az algoritmus véges számú lépésből áll, tehát statikusan véges, hiszen öt lépést definiáltunk, a végrehajtás viszont végtelen.) Egy algoritmust determináltnak nevezzük, ha ugyanazon bemenetek esetén ugyanazt a kimenetet állítja elő (azonos kezdőállapotok mellett). 2.3.4 Algoritmusok megadása 2.3.4.1 LNKO Tekintsünk egy egyszerű példát, a legnagyobb közös osztó (LNKO) kiszámításának egy lehetséges algoritmusát. Általános iskolában úgy tanultuk, hogy az a és b természetes számok legnagyobb közös osztója az a c természetes szám, amely osztója a-nak és b-nek is, és az ilyen számok közül a legnagyobb. A c nem feltétlenül különbözik a-tól és/vagy b-től: LNKO(6;12) = 6, LNKO(25;25) = 25. Az LNKO kiszámítására az iskolában azt a módszert tanultuk, hogy megkeressük a és b prímtényezőit, majd azokat a prímtényezőket, amelyek mindkét szám felbontásában megtalálhatók, a legkisebb hatványon összeszorozzuk. Ennél a módszernél van egy lényegesen egyszerűbb, bár kevésbé hatékony megoldás. Ehhez csak a kivonás műveletét kell ismernünk, és meg kell tudnunk határozni két szám közötti relációkat (<, =). Az eljárás lényege az, hogy a két szám közül a kisebbiket addig vonjuk ki nagyobbikból, míg végül a két szám egyenlő nem lesz 2. Lássunk egy példát: Legyen a = 42, b = 30. Készítsünk egy táblázatot, melyben nyomon követhető a műveletsorozat! a b művelet 42 30 a = a b (mivel a a nagyobb, azt csökkentjük b-vel) 12 30 b = b a 12 18 b = b a 12 6 a = a b 6 6 A műveletsorozat végén a = b = 6 lett az eredmény, ezért LNKO(42;30) = 6. Próbáljuk meg leírni, mit csináltunk! 1. Add meg a két számot! 2. Ha a egyenlő b-vel, akkor megvan az LNKO, megállhatsz. 3. Különben, ha a nagyobb, mint b, akkor csökkentsd a értékét b-vel. Ha pedig b nagyobb, mint a, akkor b értékét csökkentsd a-val! 4. Menj vissza a 2. pontra. 2 Ennél hatékonyabb eljárás az ún. euklideszi algoritmus. Vegyük észre, hogy egy egész számból egy nála kisebb egész szám többszöri kivonásának műveletsorozata helyettesíthető egyetlen egészosztás művelettel. Pl.: 23 4 4 4 4 4 = 3, a 23-ból 5-ször vontuk ki a 4-et, maradt a 3, vagyis 23 = 5 4 + 3. Egyszerűbben leírva: 23 : 4 = 5, maradék: 3. Az euklideszi algoritmusban az a és b számok különbsége helyett osztásuk maradékát képezzük (a nagyobbat elosztjuk a kisebbel és a kapott maradékkal számolunk tovább). Ugyanazt az eredményt fogjuk kapni, mint a kivonások sorozatával, de sokkal kevesebb lépésben. 16

Ez a leírás nagy vonalakban megfelelő, de azért vannak vele problémák. Egyrészt terjengős, másrészt csak azok értik, akik beszélnek magyarul, és nem is teljesen egyértelmű. (A 3. pontban a Ha pedig érthető úgy is, hogy abban az esetben, ha 3. pont első mondata nem volt igaz, de úgy is, ha igaz volt.) Adjuk meg ennél pontosabban az algoritmust! 2.3.4.2 A Böhm Jacopini-tétel Figyeljük meg a fenti példában, hogy az eljárásban három különböző strukturális (más szóval vezérlési) szerkezet található. a) Egyrészt az a és b értékét felváltva, egymás után változtatjuk. Annak nincs jelentősége, hogy az a csökkentése után a b, vagy ismét az a értékét kell-e csökkenteni, de a kivonások mindig egymás után és nem egyszerre történnek. (Csak azután hajtom végre pl. a harmadik kivonást, ha a másodikat már befejeztem.) Azt mondjuk, hogy ezek a műveletek szekvenciát alkotnak. b) Előfordulhat olyan eset, hogy úgy ér véget az algoritmus, hogy az eredeti értékeken egyáltalán nem kell változtatnunk. Ha a két eredeti, bemenő számadat egyenlő, akkor nincs semmi tennivalónk, kiválasztjuk az egyiket és az lesz az LNKO. Így a 2. pont azonnal egy döntést igényel tőlünk: kell-e bármit is csinálnunk, vagy készen vagyunk? Mivel a végrehajtás során többször is sorra kerül a 2. tevékenység, ez lesz az a pont, amely végül megállítja a végrehajtást. (Bármely két természetes számnak van legnagyobb közös osztója. Legrosszabb esetben ez a szám az 1, ha a két szám relatív prím.) Azt mondjuk, hogy a 2. művelet egy elágazás vagy szelekció. Az ilyen esetekben mindig két vagy több lehetséges végrehajtási lépés (végrehajtási ág) közül választunk ki egyet és azt hajtjuk végre. A 3. pontban is elágazásokat látunk. c) Az egész eljárás egy műveletsorozat ismétlése. Addig kell ismételve kivonnunk a nagyobbik számból a kisebbiket, amíg egyenlők nem lesznek. Az ilyen ismétlő szerkezet neve ismétlés vagy iteráció. (A programozási nyelvekben az ismétlés neve ciklus, és ciklusutasításokkal valósítható meg. Minden magas szintű programozási nyelv ismer legalább egyféle ciklusszerkezetet, de általában többet is.) Az 1966-ban két olasz matematikus, Corrado Böhm (1923 ) és Giuseppe Jacopini cikke 3 mutatja be azokat a kutatási eredményeket, amelyeket azóta az algoritmusok strukturált leírásának egyik alaptételeként használunk. A tételt Böhm Jacopini-tételnek nevezik: Bármely algoritmust leírhatunk három alapstruktúra segítségével: szekvenciával, iterációval és szelekcióval. 3 C. Böhm, G. Jacopini: Flow diagrams, Turing Machines and Languages with only Two Formation Rules, Comm. of the ACM, 9(5). 1966, pp. 366 371. 17

4. kép Az eredeti cikk első oldala 18

5. kép Corrado Böhm A következőkben az algoritmusok pontos megadásának és leírásának néhány eszközét ismerjük meg. 2.3.4.3 Mondatszerű leírónyelv, pszeudonyelv A mondatszerű leírónyelv (pszeudonyelv, pszeudokód) a magyar (angol stb.) nyelv szavainak, kifejezéseinek segítségével, illetve szimbólumok (, stb.) használatával írja le az algoritmus vezérlési szerkezeteit, végrehajtandó műveleteit. Jól definiált, kizárólagos szabvány nem létezik rá. A hazai és nemzetközi szakirodalom gyakorlatában kialakult egyfajta szintaxis kisebb-nagyobb eltéréseket mutat a különböző szerzők, kiadók műveiben. Jegyzetünkben minden algoritmust ezzel a leíró eszközzel mutatunk be. Kötetünkben a vezérlési szerkezetek jelölésére is magyar szavakat, kifejezéseket fogunk használni, de ebben a fejezetben bemutatjuk a további elterjedt jelölésmódokat is. Bevezetésként lássunk egy konkrét példát. Tekintsük a korábban leírt LNKO kiszámításának egy lehetséges, mondatszerű leírónyelven megadott algoritmusát. eljárás LNKO be: A, B ciklus, amíg A B ha A > B, akkor A = A B különben B = B A // harmadik eset nincs ki: A Figyeljük meg a vezérlési szerkezeteket! Az eljárás lényegében három fő részből áll. Az első rész az input (be: A, B), a második rész a számítást végzi, a harmadik rész pedig az output (ki: A). Az eljárás kimenete természetesen lehetne a B változó is, hiszen a ciklus lefutása után A és B egyenlők. 19

A középső rész ciklusa addig fut, amíg a két változó értéke egyenlő nem lesz. Ha a bemenő értékük eredetileg is egyenlő (pl. A = B = 17), nincs tennivalónk, hiszen az LNKO is ez a szám lesz. A ciklus belsejében egy szelekciót látunk. Ha A nagyobb B-nél, akkor A értékét csökkentjük B-vel, ha nem nagyobb, akkor a B értékét csökkentjük A-val. Egyenlők nem lehetnek, mert ha egyenlők lettek volna, már kiléptünk volna a ciklusból. Ezt jelöltük is az algoritmusban egy kommenttel. A komment (megjegyzés) a programozónak és nem a fordítóprogramnak szól. A kommentek jelölésére a különböző programozási nyelvek másmás módszert alkalmaznak. Jegyzetünkben mi a kommenteket // (per-per) jel mögé fogjuk írni és minden esetben kurzívan szedjük. Az A = A B utasítás egy értékadás, amely az egyik legfontosabb típusa a végrehajtható utasításoknak az algoritmusokban és az imperatív nyelven írott programokban. A változó értéket kap, vagyis a változóhoz rendelt memóriaterületre a változó típusának megfelelő reprezentációban eltároljuk a változó értékéhez rendelhető bitkombinációt. Az értékadás bal oldalán mindig egy változó áll, a jobb oldalon pedig egy kifejezés, amelynek kiértékelhető értéke van. A kiértékelhetőség azt jelenti, hogy az algoritmus adott pontján a kifejezésben szereplő összes változó deklarált és rendelkezik értékkel, illetve a kifejezésben szereplő operandusok és operátorok, illetve esetleges zárójelek értelmezhető műveletet alkotnak és a művelet elvégzésekor egyértelmű értéket adnak. A kifejezés lehet konstans, vagy állhat operandusokból, operátorokból és zárójelekből. Operandus lehet konstans vagy változó, operátor lehet az adott adattípuson értelmezhető műveleti jel (pl. számok esetében +,, *, /). Zárójelezésre a különböző programozási nyelvekben jellemzően kerek zárójelet () szoktunk használni. A zárójelek egymásba ágyazhatók. A kifejezések megadása a programozási nyelvekben leggyakrabban infix alakban történik, azaz az operátorok az operandusok között vannak elhelyezve. Jegyzetünkben a változók nevét nagybetűsen szedjük. Ez eltérhet a szakirodalmakban szokásos szabályoktól, és nem követi az egyes magas szintű programozási nyelvekben használt de facto szabványokat, illetve konvencionális jelölésmódot. (Magas szintű forráskódokban nagybetűs szedéssel az ún. nevesített konstansokat (Pascal), más néven szimbolikus állandókat (ANSI C) vagy konstansokat (C#) szokás jelölni. A változók nevében csak kisbetűket szoktak alkalmazni, kivéve akkor, ha a változó neve több szóból áll. Mivel szóköz néhány ritka példától eltekintve a programozási nyelvek egyikében sem fordulhat elő azonosítóban, az aláhúzás jelnek (_) pedig egyes nyelvekben speciális a funkciója, megszokott, hogy a több szóból álló változóneveket szóköz nélkül egybeírjuk, de a szókezdő betűket nagyra cseréljük, kivéve az elsőt: pl. elemekszáma, ügyfélneve, ellenőrzőszámjegy, legnagyobbközösosztó stb.) Jegyzetünk azonban nem konkrét programozási nyelvhez készült. Az algoritmusok átírását ez a jelölésmód nem befolyásolja, de átíráskor figyelembe kell venni az adott nyelv szintaxisának szabályait (pl. a magyar ékezetes karakterek sok nyelvben nem használhatók azonosítókban). Példák értékadásra: A = 17 B = A * 2 C = (A + B) / (A B) KÉTPI = 6.28 SZÖVEG = "hétfő" 20

Az A = A B értékadás azért tűnhet speciálisnak, mert az egyenlőség bal és jobboldalán is szerepel az A változó. Ennek programozástechnikai szempontból semmilyen jelentősége nincs, mert az értékadás jobboldala egy kiértékelhető kifejezés, és a kiértékelés után kapott érték kerül a bal oldalon lévő változóba. A jegyzetben a vezérlési szerkezetek megadására a következő leírónyelvi utasításokat fogjuk használni: input: output: be: <változó 1 >, <változó 2 >,... ki: <változó 1 >, <változó 2 >,... elágazás: ha logikai kifejezés, akkor utasítás 1 [különben utasítás 2 ] A logikai kifejezés olyan kifejezés, amelynek logikai értéke {IGAZ, HAMIS} van. Ilyen kifejezéseket összehasonlító operátorokkal (<,, >,, =, ) és logikai operátorokkal (NEM, ÉS, VAGY), illetve zárójelekkel adhatunk meg. Abban az esetben, ha a megadott logikai kifejezés értéke igaz, akkor az utasítás 1 -et hajtjuk végre. Ha a logikai kifejezés értéke hamis, és adtunk meg különben-ágat, akkor az utasítás 2 -t hajtjuk végre. Az utasítás 1 és utasítás 2 bármilyen utasítás lehet: végrehajtható utasítás, újabb elágazás, ciklus stb. Ahol utasítás szerepel, ott bármilyen utasítássorozat is megadható. A különben-ágon jelölt szögletes zárójelek ([ ]) azt jelentik, hogy a vezérlési szerkezetben ez a rész opcionális, tehát nem kötelező megadni. A különböző programozási nyelvek általában nem csak kétirányú feltételes elágazást kezelnek, hanem ún. esetszétválasztó elágazásokat, más szóval többirányú elágazást is (Pascal: CASE OF; C, C#, Java: switch). Jegyzetünkben ezektől eltekintünk, és minden algoritmusban csak kétirányú feltételes elágazást fogunk használni. előfeltételes vagy elöltesztelő ciklus: ciklus, amíg logikai kifejezés utasítások A logikai kifejezésnek itt is egy logikai értéke van, tehát kiértékelhető kifejezés, ez a ciklus feltétele. A ciklusmagban lévő utasításokat addig ismételjük, amíg a ciklusfejben megadott ciklusfeltétel igaz. Ha az első lefutás előtt a ciklusfeltétel hamis, akkor a ciklus 21

magja egyszer sem fut le, ilyenkor üres ciklusról beszélünk. Az előfeltételes ciklus válhat végtelenné, ha a ciklusmagban nem gondoskodunk arról, hogy a ciklusfeltétel egyszer hamissá váljon. Ilyenkor a ciklusból sosem lépünk ki. Ez szemantikai hiba. (A ciklus végtelenné válása nem azonos a fajta szerinti végtelen ciklussal. A végtelen ciklus olyan ciklus, amelynek sem a fejében, sem a végében nem adunk meg feltételt sem az ismétlésre, sem a befejezésre. Az ilyen ciklusokból a ciklusmagban elhelyezett befejeztető/kiugró pl. EXIT utasításokkal lehet kilépni. Ilyen végtelen ciklusokat alkalmaznak pl. operációs rendszerekben, de jegyzetünk tárgyába ezek a ciklusok nem tartoznak bele, így használatukat kerülni fogjuk.) végfeltételes, utófeltételes vagy hátultesztelő ciklus: ciklus utasítások amíg logikai kifejezés A hátultesztelő ciklus feltételét a ciklus végében helyezzük el. Ez a feltétel lehet pozitív vagy negatív, vagyis jelentheti az ismétlés (a ciklusban maradás) vagy a kilépés feltételét is. Jegyzetünkben minden esetben a ciklusban maradás feltételét fogjuk megadni az ilyen ciklusoknál. Ez a ciklusszerkezet lényegesen eltér az előfeltételes ciklustól annyiban, hogy ez a ciklus nem lehet üres. Ha a ciklus feltétele az első lefutás előtt hamis (tehát kiléptet a ciklusból), a ciklusmag egyszer akkor is lefut. A ciklus magja egyszer mindenképpen lefut, mert csak az első lefutás után értékeljük ki a ciklus feltételét. Ha a ciklusmagban nem gondoskodunk arról, hogy a ciklusmagot úgy változtassuk meg, hogy kiléptessen a ciklusból, akkor a végfeltételes ciklus is válhat végtelenné. számláló ciklus (előírt lépésszámú ciklus): ciklus <változó> = kezdőérték-től végérték-ig utasítások A számláló ciklusban az első lefutás előtt a változó (ciklusváltozó) felveszi a kezdőértéket, majd az utasítások végrehajtódnak. Ezután a ciklusváltozó értéke automatikusan megnő eggyel, és ha az aktuális érték még nem haladja meg a végértéket, akkor a mag ismét lefut. A ciklus addig ismétel, amíg a ciklusváltozó értéke meg nem haladja a végértéket. A különböző programozási nyelveket összehasonlítva a számláló ciklus szintaxisa jelentősen különböző lehet. Vannak nyelvek, amelyek a fenti szintaxisú ciklust nem ismerik, de hasonlót igen (pl. C, C#, Java). Vannak nyelvek, amelyek ismerik a fenti szerkezetet, de a lépésköz csak ±1 lehet (pl. Pascal). Vannak nyelvek, amelyekben megadható lépésköz (pl. különböző BASIC-ek). Az egyes programozási nyelvekben a végérték helyett célszerűbb a határ kifejezést használnunk, mert ha megadhatunk lépésközt, akkor nem biztos, hogy a ciklusváltozó fel is veszi a határként megadott értéket. Pl. ha a kezdőérték 5, a lépésköz 3, a határ 12, akkor a 22

ciklusváltozó által felvett értékek a következők: 5, 8, 11. A 12-t nem veszi fel, vagyis végértékről itt nem beszélhetünk. Jegyzetünkben a számláló ciklusoknál általában nem jelölünk lépésközt. Ahol nem jelölünk, ott a lépésköz mindig +1 lesz. Abban az egy-két esetben, ahol mégis jelölni fogjuk, a lépésköz 1 lesz. A számláló ciklus is lehet üres, ha a megadott kezdőértéktől a megadott irányban a végértékig (határig) nincs egyetlen felvehető érték sem. Ilyen ciklusokat azonban a jegyzetünkben nem fogunk használni. iterátor (bejáró) ciklus: // C# foreach (elem in adatszerkezet) { utasítások } // Java for (változó : adatszerkezet) { utasítások } // PHP foreach ($adatszerkezet as $változó) { utasítások } // Javascript for (változó in adatszerkezet) { utasítások } Ezt a ciklusszerkezetet az újabb programozási nyelvek gyakran alkalmazzák. A lényege, hogy a ciklus az adatszerkezet összes elemére lefut. A fejben megadott változó felveszi az adatszerkezet összes értékét és a ciklus magja az összes értékre lefut. Az adatszerkezet lehet tömb, de más is (az egyes nyelvekben eltérő típusú adatszerkezetek támogatottak). Tömb esetén nem kell kezelnünk az elemek indexét, és nem kell tudnunk azt sem, hogy hány elemű a tömb, mert a vezérlési szerkezet helyettünk kezeli ezeket. Mivel ez a vezérlési szerkezet csak bizonyos programozási nyelvekben fordul elő 4, és jegyzetünk a legtöbb algoritmust vektorok alkalmazásával adja meg, melyekben egyszerű 4 A fentieken kívül találkozhatunk vele például az ActionScript, az Ada, az Objective C, a Delphi, a Perl, a Python, a Ruby és a Visual Basic.NET nyelvekben. 23

az elemek elérése az indexükkel, a jegyzet további részében nem fogjuk alkalmazni az itertor ciklust. Amint arról korábban már szóltunk, ez a jelölésmód részben önkényes, de a szakirodalomban általában használt szabályokat követi. Példaként bemutatunk az LNKO-algoritmusra egy másik szimbólumrendszert alkalmazó peszudokóddal megadott változatot is, ez inkább hasonlít a nemzetközi szakirodalomban alkalmazotthoz: LNKO 1 a x 2 b y 3 while a b 4 do if a > b 5 then a a b 6 else b b a 7 print a Ebben a pszeudokód-szintaxisban értékadás, feltételes elágazás (if then else) és kétféle ciklus (while do és for <változó> létezik. Végrehajtható utasításként egyedül az értékadásnak van saját szimbóluma: <változó> kifejezés. Minden egyéb műveletet szövegesen kell leírni, ennek nyelve és szintaxisa a különböző irodalmakban eltérő (általános az angol nyelv). A fenti példában nem input és output műveletet írtunk, hanem a-nak és b-nek x és y kezdőértéket adtunk. Ez nem befolyásolja az algoritmus átírhatóságát, a lényeg, hogy az a és b változók valahonnan kezdőértéket kapjanak. Az input és output utasítások egyébként is nagyon különböző módon vannak implementálva a különböző programozási nyelvekben. Vannak olyan nyelvek, amelyekben kulcsszavakkal történik az I/O, de jellemzőbb, hogy az I/O utasítások nem képezik a nyelv részét, hanem unitokban vagy osztályokban vannak definiálva. 2.3.4.4 Folyamatábra, D-diagram Folyamatábrákkal grafikus módon írhatók le az algoritmusok. A folyamatábrák síkidomokból, illetve az azokat összekötő nyilakból állnak. A végrehajtási sorrendet a nyilak határozzák meg. A síkidomok alakja jelöli az egyes vezérlési szerkezetek és végrehajtható utasítások típusát. Folyamatábrákkal nemcsak informatikai algoritmusoknál találkozhatunk. Minden olyan esetben szívesen alkalmazzák őket, amikor valamilyen műveletsort írnak le, és lényeges, hogy az egyes lépések milyen sorrendben kövessék egymást. (Ez végül is az algoritmusok lényege.) Találkozhatunk folyamatábrákkal lapra szerelt bútorok összeállítási útmutatójában, minőségellenőrzési feladatoknál, vészhelyzeti útmutatóban stb. Ezek a folyamatábrák azonban gyakran elég kuszák a sok nyíl miatt. Informatikai algoritmusok megadásánál nagyon fontos az egyszerűség, az áttekinthetőség és az egyértelműség, ezért az ilyen algoritmusok megadásához ún. D-diagramokat használunk. A D betű Dijkstra 5 nevére utal, aki 5 Edsger Wybe Dijkstra (1930 2002) holland matematikus, informatikus. Számos fontos és máig használt algoritmus és módszer kidolgozója (pl. Dijkstra-algoritmus a legrövidebb út meghatározásához egy adott csomópontból irányított gráfokban, Dijkstra-szemaforok többszálú programokban a szálak szinkronizálásához). 24

elsőként adott egy szűkebb jelölési rendszert a folyamatábrák megadására. A jelölési rendszer legfontosabb szabálya, hogy minden síkidomnak egyértelműen csak egy továbblépési ága van. Ez alól egyedül a program végét jelölő szimbólum a kivétel, amely megállítja a vezérlést. 6. kép Edsger Wybe Dijsktra Az alábbiakban a folyamatábrák D-diagram-elemeit mutatjuk be. A folyamatábra kezdő szimbóluma a startszimbólum, utolsó eleme pedig a stopszimbólum. Mindkettőt ellipszis jelöli. A startszimbólumnak nincs megelőző, a stopszimbólumnak nincs rákövetkező eleme. 7. kép Start- és stopszimbólum folyamatábrában A szekvencia egyes lépéseit téglalapok jelölik. A lépések sorrendjét a nyilak jelölik ki. 25

8. kép Szekvencia D-diagram A szekvencia a végrehajtható utasítások lépéseinek meghatározott sorrendű sorozata. Előfordul azonban olyan eset, amikor az algoritmus felírásához, vagy megértéséhez nem szükséges minden egyes végrehajtható lépést teljes részletességgel elemeznünk. Például ha egy olyan algoritmust készítünk, amely tartalmazza egy adatbázis feltöltését, illetve az egyes lehetséges lekérdezések eljárásait is, akkor nem biztos, hogy az adatrögzítés minden egyes lépését szükséges részletesen szerepeltetni a folyamatábrában. (Ha a dokumentációban szerepel, hogy milyen adatokat kell rögzítenünk, ezek milyen adattípusúak, hol kell őket tárolni stb., akkor szinte biztos, hogy szükségtelen ezeket az információkat a folyamatábrában újra szerepeltetni.) Ilyen esetekben ahelyett, hogy részletesen kifejtenénk a lépéseket, egyetlen síkidommal jelöljük a nem elemi lépést az algoritmusban. Ez egy téglalap, melynek rövidebb oldalait dupla vonallal jelöljük. 9. kép Szekvencia nem elemi utasítással 26

Az input és output utasítások jelölésére külön síkidom, a paralelogramma szolgál. 10. kép Input és output Az elágazások jelölésére sarkára állított négyszöget (négyzet, rombusz, deltoid stb.) használunk. Az elágazásba egy irányból léphetünk be, de két lehetséges irányban léphetünk ki belőle. Az elágazást jelölő négyszögbe kerül a feltétel. Ha a feltétel igaz, akkor az egyik ágat, ha hamis, akkor a másik ágat kell választanunk. 11. kép Elágazás D-diagram A feltételes ciklus jelölésére nincs külön síkidom, elágazás és szekvencia, illetve az őket összekötő nyilak segítségével írjuk fel őket. 27

12. kép Előfeltételes ciklus D-diagram 13. kép Végfeltételes ciklus D-diagram 28

2.3.4.5 Struktogram AZ ALGORITMIZÁLÁS ALAPJAI Szintén grafikus algoritmusleíró eszköz. A szakirodalomban találkozhatunk vele Nassi Shneiderman-diagram 6 (NSD), illetve Chapin-ábra 7 (Chapin chart) néven is. 14. kép Isaac Ike Nassi és Ben Shneiderman 15. kép Ned Chapin 6 Isaac Ike Nassi és Ben Shneiderman (1947 ) informatikusok által 1972/73-ban kifejlesztett eszköz. I. Nassi and B. Shneiderman: Flowchart Techniques for Structured Programming. In: ACM SIGPLAN Notices, Volume 8, Number 8 (August 1973), pp.12 26. 7 Ned Chapin: New Format for Flowcharts. In: Software Practice and Experience, Volume 4, Number 4 (October-December 1974), pp. 341 357. 29

A struktogramok a különböző tevékenységeket téglalapokkal jelölik. Maga az algoritmus is egy tevékenység, így ezt is egy téglalap jelöli. Az algoritmus lépéseit ezen a téglalapon belül elhelyezett kisebb téglalapok jelentik. Szekvencia: 16. kép Szekvencia a struktogramban Az elágazás lépésének téglalapját több síkidomra bontjuk. Ha a feltétel igaz, akkor az igaz ághoz tartozó téglalap lépését hajtjuk végre, ha hamis, akkor pedig a hamis ághoz tartozó lépést. 17. kép Elágazás a struktogramban A ciklus jelölésére az adott téglalapon belül elhelyezett kisebb téglalap szolgál. A belső téglalapba kerül a ciklus magja, alá/fölé pedig az ismétlés feltétele. Fölé akkor, ha a ciklus előfeltételes, alá pedig akkor, ha a ciklus végfeltételes. 18. kép Ciklusok a struktogramban 30

2.4 ÖSSZEFOGLALÁS AZ ALGORITMIZÁLÁS ALAPJAI Az algoritmusok egy feladat, probléma megoldásának lépéseit, vagyis a megoldás módszerének pontos leírását adják. Az algoritmusokkal szemben támasztott követelményekről a lecke elején már olvashattunk, de a lecke végigolvasása után leszögezhetjük, hogy a legfontosabb követelmények a pontosság és az egyértelműség. A leckében bemutatott algoritmusleíró eszközöket a szakirodalomban használt szabályok alapján ismertettük, de kisebb-nagyobb eltérések a különböző szakirodalmak jelölésmódjában előfordulhatnak. A legfontosabb követelmény itt is az, hogy egyértelműen és pontosan értelmezhető legyen az egyes lépések feladata és a lépések közötti sorrend. 2.5 ÖNELLENŐRZŐ KÉRDÉSEK 1. Értelmezze az algoritmus fogalmát! Milyen követelményeket fogalmazhatunk meg az algoritmusokkal szemben? 2. Mutassa be az algoritmusleíró eszközöket! Milyen módon adhatók meg a vezérlési szerkezetek a különböző algoritmusleíró eszközökben? 3. Hasonlítsa össze az elő- és végfeltételes ciklusokat! Milyen ciklus lehet üres? Milyen ciklus válhat végtelenné? 4. Adja meg néhány, eddigi (közép- és felsőfokú) tanulmányai során megismert művelet, tevékenység algoritmusát! Milyen körülményekre kell figyelnie? 31

3. ADATOK, ADATSZERKEZETEK 3.1 CÉLKITŰZÉS Ebben a leckében az algoritmusokban, illetve a programokban használt adatok megjelenési formáiról, adattípusokról és adatszerkezetekről fogunk beszélni. A lecke célja, hogy megismerjük az elemi és az összetett adatok tárolási és feldolgozási lehetőségeit, az adattípusok és az adatszerkezetek jellemzőit. Algoritmusok készítésénél fontos felismernünk, hogy milyen adattípus vagy adatszerkezet a legalkalmasabb az adott probléma megoldásához. 3.2 TARTALOM Az adat A literál A változó Az adatok jellemzői, adattípusok Összetett adatok kezelésének lehetőségei Adatszerkezetek jellemzői Homogén adatszerkezetek: tömb, halmaz, sor, verem, lista, fájl A rekord adatszerkezet Adatok és adattípusok megjelenése a különböző programozási nyelvekben, példák 3.3 A TANANYAG KIFEJTÉSE 3.3.1 Adat Tágabb értelemben minden jel adatnak tekinthető. Az adatok segítségével információt közölhetünk, a kommunikációban az adat hordozza az ismereteket, de nem önmagában. Információvá akkor válhat egy adat, ha az új ismeretként értelmezhető. Az adatok eszerint olyan jelsorozatok, amelyek alkalmasok arra, hogy emberi vagy gépi kommunikációban információkat közvetíthessenek. Ehhez a jeleknek érzékelhetőknek, észlelhetőknek, feldolgozhatóknak és megérthetőknek kell lenniük. Szűkebb értelemben, és a számítástechnikában általában az adatok számokkal leírt dolgokat jelentenek. Közismert, hogy a számítógépek az adatok tárolásakor lényegében minden dolgot kettes számrendszerű, azaz bináris jelsorozatokká alakítanak. Azért éppen binárissá, mert a kettes számrendszerben mindössze kétféle jelet használunk, a 0-t és az 1-et. Kétféle számjegyhez sokkal egyszerűbb egyértelműen megkülönböztethető fizikai állapotokat rendelni, mintha többféle számjegyhez kellene ugyanezt megtennünk. Ugyanakkor tudjuk azt is, hogy bármilyen adat átalakítható kettes számrendszerbeli jelsorozatokká. A természetes számok egyszerű konverzióval, az előjeles, esetleg nem egész számok különböző algoritmusokkal 8, a szöveges adatok különféle karakterkódolások 9 alkalmazásával, 8 Az érdeklődők számára javasoljuk, hogy önállóan ismerkedjenek meg a fixpontos és lebegőpontos aritmetika alapjaival. Ezekről az első látásra kissé talán bonyolult eljárásokról számos irodalomban olvashatunk. 32

hangok, álló- és mozgóképek pedig digitalizálással. A digitalizálás nem egyetlen eljárást jelent, a különböző médiumtípusok esetén számos eljárás ismeretes. Ezek ismertetése azonban nem tartozik jegyzetünk tárgykörébe. Most lássuk, hogy milyen módon fordulhatnak elő adatok algoritmusainkban, programjainkban. 3.3.2 A literálok A programokban, algoritmusokban előforduló számokat, karaktereket és szövegeket literáloknak nevezzük. Literálok lehetnek például: 42, 173.51, "c", "szerda", "1989. október 23." Ezek olyan adatok, amelyek önmagukat jelentik, értékük, típusuk, jelentésük állandó. Nem tudjuk, hogy minek a mennyiségét jelöli a 42 jelsorozat, de mindenki számára nyilvánvaló, hogy ez egy kétjegyű, decimális (tízes számrendszerbeli) egész. 10 Nem tudjuk, hogy milyen halmaz számosságát jelenti (pl. milyen tárgyból van negyvenkettő), de tudjuk, hogy ez az összes negyvenkét elemű halmaz közös tulajdonsága, és a nagyságrendjét is érzékeljük: több mint öt, de kevesebb, mint százhúsz. Hasonló a helyzet a 173.51 esetében, ez nagy valószínűséggel egy negatív, véges tizedes törtet, vagyis egy racionális számot jelöl. Azt ebben az esetben sem tudjuk, hogy pontosan milyen dolognak a mérőszámáról van szó, de valószínűleg mindenki egy mennyiségre gondol, ha ezt a jelsorozatot látja. A "c" egy latin betű, a magyar ábécé negyedik betűje, a fizikában a fény vákuumbeli sebességét jelölik vele. Ha egy üres lapon önmagában látjuk ezt a jelet, valószínűleg nem tulajdonítunk neki jelentést, azon túl, hogy ez egy c betű. Hasonlóképp a "szerda", amely egy karaktersorozat és magyar nyelven a hét egyik napjának neve, illetve az utolsó példa, amely egy dátum. (Számunkra azért fontos, mert akkor kiáltották ki a harmadik magyar köztársaságot, egy külföldi számára azonban valószínűleg jelentéktelen napról van szó). A számliterálok mennyiségeket, a karakterliterálok betűket (pontosabban karaktereket, vagyis a kódtábla egy-egy elemét) jelölnek, a szövegliterálok pedig karaktersorozatokat. Ezek az adatok csak önmagukat jelentik, az értékük változatlan, de alkalmasak arra, hogy műveleteket végezzünk velük, illetve rajtuk. A különböző programozási nyelvekben eltérő szabályok vonatkozhatnak a literálok megadására. Számliterálokban számjegyek, előjel és tizedespont állhat. A különböző programozási nyelvekben használhatunk tízestől eltérő alapszámú számrendszerbeli számliterálokat is, tehát nemcsak decimális, hanem pl. bináris, oktális vagy hexadecimális számliterálokat is. A karakteres literálok megadása is különbözhet az egyes programozási nyelvekben. Ezeket vagy aposztrófok ( ), vagy macskakörmök ("") között kell megadnunk (Pascalban pl. minden karakter- és sztringliterált aposztrófok között, a C-szerű nyelvekben 9 Járjunk utána, hogy milyen kódtáblákon alapuló karakterkódolási eljárások használatosak ma. Járjunk utána az ASCII kódrendszer, illetve az azon alapuló más kódrendszerek (pl. ISO-8859 szabványcsalád kódolásai) jellemzőinek. Járjunk utána, hogy mi a jelentősége a UNICODE szabványnak, és mit jelent az UTF-8 kódolás, amely a UNICODE rendszer egyik gépi megvalósítása! 10 Valójában ez egyáltalán nem ennyire egyértelmű. Önmagát tekintve ez a jelsorozat még azt sem garantálja, hogy egy mennyiségről van szó. Elképzelhető, hogy ez egy kétszavas üzenet, amelynek első szavát a 4, második szavát a 2 helyettesíti. Ahhoz, hogy ezt megfejtsük, szükségünk van egy szótárra, egy kulcsra, egy kódtáblára. Mindemellett azonban valószínű, hogy mindannyiunk számára inkább az a természetes, hogy a 42 jelsorozat egy pozitív egész számot jelent, tízes számrendszerben. 33